psyclaw 0.27.22 → 0.28.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 (1048) hide show
  1. package/README.md +11 -5
  2. package/dist/apps/panel/index.html +2 -2
  3. package/dist/src/adapters/pi/extension.js +225 -92
  4. package/dist/src/adapters/pi/extension.js.map +1 -1
  5. package/dist/src/agents/import.js +44 -32
  6. package/dist/src/agents/import.js.map +1 -1
  7. package/dist/src/analysis/hooks.d.ts +1 -0
  8. package/dist/src/analysis/hooks.js +20 -1
  9. package/dist/src/analysis/hooks.js.map +1 -1
  10. package/dist/src/ars/bridge.d.ts +20 -0
  11. package/dist/src/ars/bridge.js +132 -0
  12. package/dist/src/ars/bridge.js.map +1 -0
  13. package/dist/src/ars/contracts.d.ts +55 -0
  14. package/dist/src/ars/contracts.js +2 -0
  15. package/dist/src/ars/contracts.js.map +1 -0
  16. package/dist/src/ars/panel-plan.d.ts +4 -0
  17. package/dist/src/ars/panel-plan.js +11 -0
  18. package/dist/src/ars/panel-plan.js.map +1 -0
  19. package/dist/src/ars/pi-panel-executor.d.ts +30 -0
  20. package/dist/src/ars/pi-panel-executor.js +140 -0
  21. package/dist/src/ars/pi-panel-executor.js.map +1 -0
  22. package/dist/src/ars/profile.d.ts +36 -0
  23. package/dist/src/ars/profile.js +119 -0
  24. package/dist/src/ars/profile.js.map +1 -0
  25. package/dist/src/ars/re-review.d.ts +22 -0
  26. package/dist/src/ars/re-review.js +172 -0
  27. package/dist/src/ars/re-review.js.map +1 -0
  28. package/dist/src/branding.d.ts +2 -4
  29. package/dist/src/branding.js +3 -5
  30. package/dist/src/branding.js.map +1 -1
  31. package/dist/src/bundled-tools.d.ts +3 -0
  32. package/dist/src/bundled-tools.js +21 -0
  33. package/dist/src/bundled-tools.js.map +1 -0
  34. package/dist/src/chat.js +6 -5
  35. package/dist/src/chat.js.map +1 -1
  36. package/dist/src/creation/contracts.d.ts +38 -0
  37. package/dist/src/creation/contracts.js +2 -0
  38. package/dist/src/creation/contracts.js.map +1 -0
  39. package/dist/src/creation/service.d.ts +7 -0
  40. package/dist/src/creation/service.js +196 -0
  41. package/dist/src/creation/service.js.map +1 -0
  42. package/dist/src/index.d.ts +10 -0
  43. package/dist/src/index.js +10 -0
  44. package/dist/src/index.js.map +1 -1
  45. package/dist/src/install/installer.js +32 -10
  46. package/dist/src/install/installer.js.map +1 -1
  47. package/dist/src/orchestration/personas.d.ts +15 -0
  48. package/dist/src/orchestration/personas.js +51 -0
  49. package/dist/src/orchestration/personas.js.map +1 -0
  50. package/dist/src/orchestration/pi-executor.d.ts +1 -0
  51. package/dist/src/orchestration/pi-executor.js +1 -1
  52. package/dist/src/orchestration/pi-executor.js.map +1 -1
  53. package/dist/src/panel/extension.js +29 -8
  54. package/dist/src/panel/extension.js.map +1 -1
  55. package/dist/src/panel/server.js +14 -5
  56. package/dist/src/panel/server.js.map +1 -1
  57. package/dist/src/project/paths.d.ts +3 -0
  58. package/dist/src/project/paths.js +7 -0
  59. package/dist/src/project/paths.js.map +1 -1
  60. package/dist/src/rules/user-rules.d.ts +8 -0
  61. package/dist/src/rules/user-rules.js +36 -0
  62. package/dist/src/rules/user-rules.js.map +1 -0
  63. package/dist/src/skills/contracts.d.ts +4 -4
  64. package/dist/src/skills/recommended.js +1 -1
  65. package/dist/src/skills/registry.js +49 -28
  66. package/dist/src/skills/registry.js.map +1 -1
  67. package/dist/src/style/cli-ui.js +0 -1
  68. package/dist/src/style/cli-ui.js.map +1 -1
  69. package/package.json +14 -3
  70. package/scripts/rebrand-pi.mjs +6 -0
  71. package/skills/recommended/catalog.json +2 -11
  72. package/vendor/ars/.claude/CLAUDE.md +371 -0
  73. package/vendor/ars/.command-invariants.toml +24 -0
  74. package/vendor/ars/CITATION.cff +35 -0
  75. package/vendor/ars/LICENSE +417 -0
  76. package/vendor/ars/MODE_REGISTRY.md +76 -0
  77. package/vendor/ars/NOTICE.md +26 -0
  78. package/vendor/ars/POSITIONING.md +99 -0
  79. package/vendor/ars/PSYCLAW_SOURCE.json +10 -0
  80. package/vendor/ars/README.md +751 -0
  81. package/vendor/ars/SECURITY.md +52 -0
  82. package/vendor/ars/THIRD_PARTY.md +70 -0
  83. package/vendor/ars/academic-paper/SKILL.md +542 -0
  84. package/vendor/ars/academic-paper/agents/abstract_bilingual_agent.md +171 -0
  85. package/vendor/ars/academic-paper/agents/argument_builder_agent.md +276 -0
  86. package/vendor/ars/academic-paper/agents/citation_compliance_agent.md +422 -0
  87. package/vendor/ars/academic-paper/agents/draft_writer_agent.md +656 -0
  88. package/vendor/ars/academic-paper/agents/formatter_agent.md +999 -0
  89. package/vendor/ars/academic-paper/agents/intake_agent.md +393 -0
  90. package/vendor/ars/academic-paper/agents/literature_strategist_agent.md +626 -0
  91. package/vendor/ars/academic-paper/agents/peer_reviewer_agent.md +516 -0
  92. package/vendor/ars/academic-paper/agents/revision_coach_agent.md +334 -0
  93. package/vendor/ars/academic-paper/agents/socratic_mentor_agent.md +527 -0
  94. package/vendor/ars/academic-paper/agents/structure_architect_agent.md +401 -0
  95. package/vendor/ars/academic-paper/agents/visualization_agent.md +441 -0
  96. package/vendor/ars/academic-paper/examples/chinese_paper_example.md +278 -0
  97. package/vendor/ars/academic-paper/examples/clinical_citation_verification_checklist.md +95 -0
  98. package/vendor/ars/academic-paper/examples/clinical_epistemic_status_example.md +100 -0
  99. package/vendor/ars/academic-paper/examples/commitment_ledger_example.md +147 -0
  100. package/vendor/ars/academic-paper/examples/imrad_hei_example.md +234 -0
  101. package/vendor/ars/academic-paper/examples/literature_review_example.md +260 -0
  102. package/vendor/ars/academic-paper/examples/plan_mode_guided_writing.md +600 -0
  103. package/vendor/ars/academic-paper/examples/revision_mode_example.md +344 -0
  104. package/vendor/ars/academic-paper/examples/revision_recovery_example.md +506 -0
  105. package/vendor/ars/academic-paper/examples/version_family_reconciliation_example.md +89 -0
  106. package/vendor/ars/academic-paper/references/abstract_writing_guide.md +169 -0
  107. package/vendor/ars/academic-paper/references/academic_writing_style.md +188 -0
  108. package/vendor/ars/academic-paper/references/anti_leakage_protocol.md +83 -0
  109. package/vendor/ars/academic-paper/references/apa7_chinese_citation_guide.md +364 -0
  110. package/vendor/ars/academic-paper/references/apa7_extended_guide.md +198 -0
  111. package/vendor/ars/academic-paper/references/changelog.md +11 -0
  112. package/vendor/ars/academic-paper/references/citation_format_switcher.md +228 -0
  113. package/vendor/ars/academic-paper/references/committee_correspondence_protocol.md +158 -0
  114. package/vendor/ars/academic-paper/references/credit_authorship_guide.md +308 -0
  115. package/vendor/ars/academic-paper/references/disclosure_mode_protocol.md +478 -0
  116. package/vendor/ars/academic-paper/references/domain_evidence_profiles.md +38 -0
  117. package/vendor/ars/academic-paper/references/failure_paths.md +349 -0
  118. package/vendor/ars/academic-paper/references/funding_statement_guide.md +319 -0
  119. package/vendor/ars/academic-paper/references/hei_domain_glossary.md +169 -0
  120. package/vendor/ars/academic-paper/references/intro_title_rhetoric_guide.md +114 -0
  121. package/vendor/ars/academic-paper/references/journal_submission_guide.md +249 -0
  122. package/vendor/ars/academic-paper/references/latex_template_reference.md +378 -0
  123. package/vendor/ars/academic-paper/references/mode_selection_guide.md +378 -0
  124. package/vendor/ars/academic-paper/references/paper_structure_patterns.md +330 -0
  125. package/vendor/ars/academic-paper/references/plan_mode_protocol.md +112 -0
  126. package/vendor/ars/academic-paper/references/policy_anchor_disclosure_protocol.md +200 -0
  127. package/vendor/ars/academic-paper/references/policy_anchor_table.md +157 -0
  128. package/vendor/ars/academic-paper/references/revision_patch_protocol.md +173 -0
  129. package/vendor/ars/academic-paper/references/statistical_visualization_standards.md +750 -0
  130. package/vendor/ars/academic-paper/references/venue_disclosure_policies.md +259 -0
  131. package/vendor/ars/academic-paper/references/vlm_figure_verification.md +126 -0
  132. package/vendor/ars/academic-paper/references/workflow_phase_details.md +135 -0
  133. package/vendor/ars/academic-paper/references/writing_judgment_framework.md +59 -0
  134. package/vendor/ars/academic-paper/references/writing_quality_check.md +173 -0
  135. package/vendor/ars/academic-paper/templates/bilingual_abstract_template.md +78 -0
  136. package/vendor/ars/academic-paper/templates/case_study_template.md +129 -0
  137. package/vendor/ars/academic-paper/templates/conference_paper_template.md +108 -0
  138. package/vendor/ars/academic-paper/templates/credit_statement_template.md +132 -0
  139. package/vendor/ars/academic-paper/templates/funding_statement_template.md +290 -0
  140. package/vendor/ars/academic-paper/templates/imrad_template.md +183 -0
  141. package/vendor/ars/academic-paper/templates/latex_article_template.tex +199 -0
  142. package/vendor/ars/academic-paper/templates/literature_review_template.md +135 -0
  143. package/vendor/ars/academic-paper/templates/policy_brief_template.md +139 -0
  144. package/vendor/ars/academic-paper/templates/revision_tracking_template.md +199 -0
  145. package/vendor/ars/academic-paper/templates/theoretical_paper_template.md +119 -0
  146. package/vendor/ars/academic-paper-reviewer/SKILL.md +491 -0
  147. package/vendor/ars/academic-paper-reviewer/agents/devils_advocate_reviewer_agent.md +443 -0
  148. package/vendor/ars/academic-paper-reviewer/agents/domain_reviewer_agent.md +412 -0
  149. package/vendor/ars/academic-paper-reviewer/agents/editorial_synthesizer_agent.md +478 -0
  150. package/vendor/ars/academic-paper-reviewer/agents/eic_agent.md +339 -0
  151. package/vendor/ars/academic-paper-reviewer/agents/field_analyst_agent.md +221 -0
  152. package/vendor/ars/academic-paper-reviewer/agents/methodology_reviewer_agent.md +449 -0
  153. package/vendor/ars/academic-paper-reviewer/agents/perspective_reviewer_agent.md +427 -0
  154. package/vendor/ars/academic-paper-reviewer/examples/hei_paper_review_example.md +391 -0
  155. package/vendor/ars/academic-paper-reviewer/examples/interdisciplinary_review_example.md +299 -0
  156. package/vendor/ars/academic-paper-reviewer/examples/subclaim_decomposition_example.md +80 -0
  157. package/vendor/ars/academic-paper-reviewer/references/calibration_mode_protocol.md +256 -0
  158. package/vendor/ars/academic-paper-reviewer/references/changelog.md +10 -0
  159. package/vendor/ars/academic-paper-reviewer/references/editorial_decision_standards.md +236 -0
  160. package/vendor/ars/academic-paper-reviewer/references/guided_mode_protocol.md +34 -0
  161. package/vendor/ars/academic-paper-reviewer/references/integration_guide.md +15 -0
  162. package/vendor/ars/academic-paper-reviewer/references/quality_rubrics.md +84 -0
  163. package/vendor/ars/academic-paper-reviewer/references/re_review_mode_protocol.md +340 -0
  164. package/vendor/ars/academic-paper-reviewer/references/review_criteria_framework.md +98 -0
  165. package/vendor/ars/academic-paper-reviewer/references/review_panel_provenance_protocol.md +197 -0
  166. package/vendor/ars/academic-paper-reviewer/references/review_quality_thinking.md +58 -0
  167. package/vendor/ars/academic-paper-reviewer/references/reviewer_sprint_prompt_source.md +324 -0
  168. package/vendor/ars/academic-paper-reviewer/references/sprint_contract_protocol.md +296 -0
  169. package/vendor/ars/academic-paper-reviewer/references/statistical_reporting_standards.md +505 -0
  170. package/vendor/ars/academic-paper-reviewer/references/top_journals_by_field.md +206 -0
  171. package/vendor/ars/academic-paper-reviewer/templates/editorial_decision_template.md +235 -0
  172. package/vendor/ars/academic-paper-reviewer/templates/peer_review_report_template.md +305 -0
  173. package/vendor/ars/academic-paper-reviewer/templates/revision_response_template.md +248 -0
  174. package/vendor/ars/academic-pipeline/SKILL.md +736 -0
  175. package/vendor/ars/academic-pipeline/agents/claim_ref_alignment_audit_agent.md +382 -0
  176. package/vendor/ars/academic-pipeline/agents/collaboration_depth_agent.md +164 -0
  177. package/vendor/ars/academic-pipeline/agents/integrity_verification_agent.md +870 -0
  178. package/vendor/ars/academic-pipeline/agents/pipeline_orchestrator_agent.md +1379 -0
  179. package/vendor/ars/academic-pipeline/agents/state_tracker_agent.md +622 -0
  180. package/vendor/ars/academic-pipeline/examples/full_pipeline_example.md +482 -0
  181. package/vendor/ars/academic-pipeline/examples/integrity_failure_recovery.md +389 -0
  182. package/vendor/ars/academic-pipeline/examples/mid_entry_example.md +414 -0
  183. package/vendor/ars/academic-pipeline/references/adapters/.gitkeep +0 -0
  184. package/vendor/ars/academic-pipeline/references/adapters/overview.md +153 -0
  185. package/vendor/ars/academic-pipeline/references/ai_research_failure_modes.md +185 -0
  186. package/vendor/ars/academic-pipeline/references/changelog.md +14 -0
  187. package/vendor/ars/academic-pipeline/references/claim_audit_calibration_protocol.md +175 -0
  188. package/vendor/ars/academic-pipeline/references/claim_verification_protocol.md +282 -0
  189. package/vendor/ars/academic-pipeline/references/external_review_protocol.md +131 -0
  190. package/vendor/ars/academic-pipeline/references/integrity_review_protocol.md +110 -0
  191. package/vendor/ars/academic-pipeline/references/literature_corpus_consumers.md +193 -0
  192. package/vendor/ars/academic-pipeline/references/mode_advisor.md +135 -0
  193. package/vendor/ars/academic-pipeline/references/passport_as_reset_boundary.md +132 -0
  194. package/vendor/ars/academic-pipeline/references/pipeline_state_machine.md +405 -0
  195. package/vendor/ars/academic-pipeline/references/plagiarism_detection_protocol.md +239 -0
  196. package/vendor/ars/academic-pipeline/references/process_summary_protocol.md +209 -0
  197. package/vendor/ars/academic-pipeline/references/progress_dashboard_template.md +38 -0
  198. package/vendor/ars/academic-pipeline/references/reinforcement_content.md +15 -0
  199. package/vendor/ars/academic-pipeline/references/reproducibility_audit.md +55 -0
  200. package/vendor/ars/academic-pipeline/references/score_trajectory_protocol.md +78 -0
  201. package/vendor/ars/academic-pipeline/references/team_collaboration_protocol.md +261 -0
  202. package/vendor/ars/academic-pipeline/references/two_stage_review_protocol.md +27 -0
  203. package/vendor/ars/academic-pipeline/templates/pipeline_status_template.md +146 -0
  204. package/vendor/ars/agents/report_compiler_agent.md +341 -0
  205. package/vendor/ars/agents/research_architect_agent.md +298 -0
  206. package/vendor/ars/agents/synthesis_agent.md +356 -0
  207. package/vendor/ars/commands/ars-3w.md +10 -0
  208. package/vendor/ars/commands/ars-abstract.md +10 -0
  209. package/vendor/ars/commands/ars-cache-invalidate.md +20 -0
  210. package/vendor/ars/commands/ars-citation-check.md +10 -0
  211. package/vendor/ars/commands/ars-disclosure.md +10 -0
  212. package/vendor/ars/commands/ars-format-convert.md +10 -0
  213. package/vendor/ars/commands/ars-full.md +9 -0
  214. package/vendor/ars/commands/ars-lit-review.md +12 -0
  215. package/vendor/ars/commands/ars-mark-read.md +18 -0
  216. package/vendor/ars/commands/ars-outline.md +10 -0
  217. package/vendor/ars/commands/ars-plan.md +10 -0
  218. package/vendor/ars/commands/ars-rebuttal-audit.md +12 -0
  219. package/vendor/ars/commands/ars-reviewer.md +9 -0
  220. package/vendor/ars/commands/ars-revision-coach.md +9 -0
  221. package/vendor/ars/commands/ars-revision.md +10 -0
  222. package/vendor/ars/commands/ars-unmark-read.md +16 -0
  223. package/vendor/ars/deep-research/SKILL.md +600 -0
  224. package/vendor/ars/deep-research/agents/bibliography_agent.md +473 -0
  225. package/vendor/ars/deep-research/agents/devils_advocate_agent.md +192 -0
  226. package/vendor/ars/deep-research/agents/editor_in_chief_agent.md +167 -0
  227. package/vendor/ars/deep-research/agents/ethics_review_agent.md +267 -0
  228. package/vendor/ars/deep-research/agents/meta_analysis_agent.md +325 -0
  229. package/vendor/ars/deep-research/agents/monitoring_agent.md +209 -0
  230. package/vendor/ars/deep-research/agents/report_compiler_agent.md +341 -0
  231. package/vendor/ars/deep-research/agents/research_architect_agent.md +298 -0
  232. package/vendor/ars/deep-research/agents/research_question_agent.md +216 -0
  233. package/vendor/ars/deep-research/agents/risk_of_bias_agent.md +231 -0
  234. package/vendor/ars/deep-research/agents/socratic_mentor_agent.md +764 -0
  235. package/vendor/ars/deep-research/agents/source_verification_agent.md +219 -0
  236. package/vendor/ars/deep-research/agents/synthesis_agent.md +356 -0
  237. package/vendor/ars/deep-research/agents/timeline_extraction_agent.md +99 -0
  238. package/vendor/ars/deep-research/examples/exploratory_research.md +157 -0
  239. package/vendor/ars/deep-research/examples/fact_check_mode.md +173 -0
  240. package/vendor/ars/deep-research/examples/handoff_to_paper.md +318 -0
  241. package/vendor/ars/deep-research/examples/idea_diversity_coverage_gap_advisory.md +64 -0
  242. package/vendor/ars/deep-research/examples/policy_analysis.md +161 -0
  243. package/vendor/ars/deep-research/examples/review_mode.md +253 -0
  244. package/vendor/ars/deep-research/examples/socratic_guided_research.md +331 -0
  245. package/vendor/ars/deep-research/examples/systematic_review.md +133 -0
  246. package/vendor/ars/deep-research/references/apa7_style_guide.md +162 -0
  247. package/vendor/ars/deep-research/references/argumentation_reasoning_framework.md +68 -0
  248. package/vendor/ars/deep-research/references/arxiv_api_protocol.md +76 -0
  249. package/vendor/ars/deep-research/references/changelog.md +22 -0
  250. package/vendor/ars/deep-research/references/chinese_literature_api_protocol.md +317 -0
  251. package/vendor/ars/deep-research/references/cross_agent_quality_definitions.md +14 -0
  252. package/vendor/ars/deep-research/references/crossref_api_protocol.md +84 -0
  253. package/vendor/ars/deep-research/references/equator_reporting_guidelines.md +482 -0
  254. package/vendor/ars/deep-research/references/ethics_checklist.md +282 -0
  255. package/vendor/ars/deep-research/references/failure_paths.md +355 -0
  256. package/vendor/ars/deep-research/references/interdisciplinary_bridges.md +292 -0
  257. package/vendor/ars/deep-research/references/irb_decision_tree.md +315 -0
  258. package/vendor/ars/deep-research/references/literature_monitoring_strategies.md +263 -0
  259. package/vendor/ars/deep-research/references/logical_fallacies.md +192 -0
  260. package/vendor/ars/deep-research/references/methodology_patterns.md +462 -0
  261. package/vendor/ars/deep-research/references/mode_selection_guide.md +331 -0
  262. package/vendor/ars/deep-research/references/openalex_api_protocol.md +82 -0
  263. package/vendor/ars/deep-research/references/preregistration_guide.md +324 -0
  264. package/vendor/ars/deep-research/references/semantic_scholar_api_protocol.md +107 -0
  265. package/vendor/ars/deep-research/references/socratic_mode_protocol.md +99 -0
  266. package/vendor/ars/deep-research/references/socratic_questioning_framework.md +232 -0
  267. package/vendor/ars/deep-research/references/source_quality_hierarchy.md +188 -0
  268. package/vendor/ars/deep-research/references/systematic_review_protocol.md +95 -0
  269. package/vendor/ars/deep-research/references/systematic_review_toolkit.md +353 -0
  270. package/vendor/ars/deep-research/templates/evidence_assessment_template.md +127 -0
  271. package/vendor/ars/deep-research/templates/literature_matrix_template.md +85 -0
  272. package/vendor/ars/deep-research/templates/preregistration_template.md +318 -0
  273. package/vendor/ars/deep-research/templates/prisma_protocol_template.md +248 -0
  274. package/vendor/ars/deep-research/templates/prisma_report_template.md +415 -0
  275. package/vendor/ars/deep-research/templates/research_brief_template.md +93 -0
  276. package/vendor/ars/package.json +24 -0
  277. package/vendor/ars/pi/README.md +161 -0
  278. package/vendor/ars/pi/package.json +26 -0
  279. package/vendor/ars/pi/wrapper.js +193 -0
  280. package/vendor/ars/pi/wrapper.test.mjs +201 -0
  281. package/vendor/ars/pyproject.toml +2 -0
  282. package/vendor/ars/requirements-pdf-content-classifier.txt +5 -0
  283. package/vendor/ars/scripts/_block_parser.py +396 -0
  284. package/vendor/ars/scripts/_ci_pytest_manifest.toml +661 -0
  285. package/vendor/ars/scripts/_claim_audit_constants.py +268 -0
  286. package/vendor/ars/scripts/_e4_evidence.py +110 -0
  287. package/vendor/ars/scripts/_eval_threshold_gate.py +73 -0
  288. package/vendor/ars/scripts/_markdown_lint_util.py +224 -0
  289. package/vendor/ars/scripts/_next_verified_at_ms.py +176 -0
  290. package/vendor/ars/scripts/_passport_yaml.py +53 -0
  291. package/vendor/ars/scripts/_skill_lint.py +254 -0
  292. package/vendor/ars/scripts/_text_similarity.py +141 -0
  293. package/vendor/ars/scripts/adapters/README.md +89 -0
  294. package/vendor/ars/scripts/adapters/_common.py +209 -0
  295. package/vendor/ars/scripts/adapters/examples/folder_scan/expected_passport.yaml +25 -0
  296. package/vendor/ars/scripts/adapters/examples/folder_scan/expected_rejection_log.yaml +18 -0
  297. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/Chen2024_AIAssessment.pdf +0 -0
  298. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/Wang_2023_formative_feedback.pdf +0 -0
  299. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/paper1.pdf +0 -0
  300. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture//344/270/255/346/226/207/346/252/224/345/220/215_2024.pdf +0 -0
  301. package/vendor/ars/scripts/adapters/examples/obsidian/expected_passport.yaml +40 -0
  302. package/vendor/ars/scripts/adapters/examples/obsidian/expected_rejection_log.yaml +13 -0
  303. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/.gitkeep +0 -0
  304. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/.obsidian/app.json +1 -0
  305. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/_templates/tmpl.md +7 -0
  306. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/chen2024ai.md +13 -0
  307. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/invalid.md +1 -0
  308. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/wang2023formative.md +10 -0
  309. package/vendor/ars/scripts/adapters/examples/zotero/expected_passport.yaml +34 -0
  310. package/vendor/ars/scripts/adapters/examples/zotero/expected_rejection_log.yaml +34 -0
  311. package/vendor/ars/scripts/adapters/examples/zotero/input_fixture/.gitkeep +0 -0
  312. package/vendor/ars/scripts/adapters/examples/zotero/input_fixture/export.json +43 -0
  313. package/vendor/ars/scripts/adapters/folder_scan.py +214 -0
  314. package/vendor/ars/scripts/adapters/obsidian.py +336 -0
  315. package/vendor/ars/scripts/adapters/tests/.gitkeep +0 -0
  316. package/vendor/ars/scripts/adapters/tests/conftest.py +77 -0
  317. package/vendor/ars/scripts/adapters/tests/test_check_corpus_consumer_protocol.py +632 -0
  318. package/vendor/ars/scripts/adapters/tests/test_check_literature_corpus_schema.py +631 -0
  319. package/vendor/ars/scripts/adapters/tests/test_common.py +365 -0
  320. package/vendor/ars/scripts/adapters/tests/test_conftest.py +96 -0
  321. package/vendor/ars/scripts/adapters/tests/test_folder_scan.py +260 -0
  322. package/vendor/ars/scripts/adapters/tests/test_literature_corpus_entry_schema.py +745 -0
  323. package/vendor/ars/scripts/adapters/tests/test_obsidian.py +357 -0
  324. package/vendor/ars/scripts/adapters/tests/test_rejection_log_schema.py +271 -0
  325. package/vendor/ars/scripts/adapters/tests/test_sync_adapter_docs.py +88 -0
  326. package/vendor/ars/scripts/adapters/tests/test_zotero.py +454 -0
  327. package/vendor/ars/scripts/adapters/zotero.py +318 -0
  328. package/vendor/ars/scripts/adjudication_activity.py +1592 -0
  329. package/vendor/ars/scripts/announce-ars-loaded.sh +144 -0
  330. package/vendor/ars/scripts/ars_anchorize_draft.py +170 -0
  331. package/vendor/ars/scripts/ars_apply_revision_patch.py +912 -0
  332. package/vendor/ars/scripts/ars_cache_invalidate.py +40 -0
  333. package/vendor/ars/scripts/ars_mark_read.py +521 -0
  334. package/vendor/ars/scripts/ars_phase_scope_manifest.json +33 -0
  335. package/vendor/ars/scripts/ars_update_check.sh +215 -0
  336. package/vendor/ars/scripts/ars_write_scope_guard.py +506 -0
  337. package/vendor/ars/scripts/arxiv_client.py +222 -0
  338. package/vendor/ars/scripts/audit_snapshot.py +572 -0
  339. package/vendor/ars/scripts/bibliographic_integrity_signals.py +800 -0
  340. package/vendor/ars/scripts/bootstrap_timeline_yaml.py +146 -0
  341. package/vendor/ars/scripts/build_claim_standing_candidate_ledger.py +1238 -0
  342. package/vendor/ars/scripts/build_claim_standing_query_plan.py +643 -0
  343. package/vendor/ars/scripts/build_content_coverage_advisory.py +1205 -0
  344. package/vendor/ars/scripts/build_cross_document_consistency_advisory.py +2332 -0
  345. package/vendor/ars/scripts/build_review_pathway_rule_trace.py +830 -0
  346. package/vendor/ars/scripts/build_submission_packet_manifest.py +2310 -0
  347. package/vendor/ars/scripts/check_215_field_norm.py +173 -0
  348. package/vendor/ars/scripts/check_216_surface_form.py +250 -0
  349. package/vendor/ars/scripts/check_268_nested_commitment_ledger.py +180 -0
  350. package/vendor/ars/scripts/check_390_revision_patch_discipline.py +296 -0
  351. package/vendor/ars/scripts/check_392_citation_verification_intake.py +126 -0
  352. package/vendor/ars/scripts/check_394_submission_policy.py +178 -0
  353. package/vendor/ars/scripts/check_439_format_profile.py +307 -0
  354. package/vendor/ars/scripts/check_619_disclosure_closeout.py +237 -0
  355. package/vendor/ars/scripts/check_630_codex_subscription_transport.py +456 -0
  356. package/vendor/ars/scripts/check_669_review_pathway_rule_trace.py +662 -0
  357. package/vendor/ars/scripts/check_670_revision_roadmap_integration.py +518 -0
  358. package/vendor/ars/scripts/check_673_adjudication_activity.py +684 -0
  359. package/vendor/ars/scripts/check_684_review_criteria_binding.py +557 -0
  360. package/vendor/ars/scripts/check_agents_mirror_sync.py +115 -0
  361. package/vendor/ars/scripts/check_audit_artifact_consistency.py +2313 -0
  362. package/vendor/ars/scripts/check_benchmark_report.py +79 -0
  363. package/vendor/ars/scripts/check_bibliographic_integrity_signals.py +831 -0
  364. package/vendor/ars/scripts/check_calibration_tiers.py +235 -0
  365. package/vendor/ars/scripts/check_changelog_covers_merges.py +289 -0
  366. package/vendor/ars/scripts/check_ci_pytest_manifest.py +204 -0
  367. package/vendor/ars/scripts/check_claim_audit_consistency.py +1664 -0
  368. package/vendor/ars/scripts/check_claim_standing_candidate_ledger_integration.py +500 -0
  369. package/vendor/ars/scripts/check_claim_standing_freshness.py +253 -0
  370. package/vendor/ars/scripts/check_claim_standing_transmissions.py +449 -0
  371. package/vendor/ars/scripts/check_collaboration_depth_rubric.py +180 -0
  372. package/vendor/ars/scripts/check_command_frontmatter_name.py +116 -0
  373. package/vendor/ars/scripts/check_committee_correspondence.py +333 -0
  374. package/vendor/ars/scripts/check_compliance_report.py +108 -0
  375. package/vendor/ars/scripts/check_content_coverage_advisory_integration.py +796 -0
  376. package/vendor/ars/scripts/check_control_availability.py +172 -0
  377. package/vendor/ars/scripts/check_corpus_consumer_protocol.py +404 -0
  378. package/vendor/ars/scripts/check_cross_document_consistency_advisory_integration.py +1191 -0
  379. package/vendor/ars/scripts/check_cross_model_handoff_contract.py +234 -0
  380. package/vendor/ars/scripts/check_cross_model_verification_sync.py +261 -0
  381. package/vendor/ars/scripts/check_data_access_level.py +131 -0
  382. package/vendor/ars/scripts/check_data_flows.py +252 -0
  383. package/vendor/ars/scripts/check_decision_contract.py +464 -0
  384. package/vendor/ars/scripts/check_degradation_registry.py +326 -0
  385. package/vendor/ars/scripts/check_distribution_surface_claims.py +226 -0
  386. package/vendor/ars/scripts/check_domain_evidence_profile.py +538 -0
  387. package/vendor/ars/scripts/check_e4_promotion.py +195 -0
  388. package/vendor/ars/scripts/check_evals_gold_set.py +279 -0
  389. package/vendor/ars/scripts/check_evidence_row_integration.py +396 -0
  390. package/vendor/ars/scripts/check_experiment_provenance.py +117 -0
  391. package/vendor/ars/scripts/check_field_norm_severity.py +144 -0
  392. package/vendor/ars/scripts/check_firm_rules_sync.py +375 -0
  393. package/vendor/ars/scripts/check_heldout_measurement_report.py +1179 -0
  394. package/vendor/ars/scripts/check_human_subjects_output_contract.py +139 -0
  395. package/vendor/ars/scripts/check_human_subjects_reference_migration.py +844 -0
  396. package/vendor/ars/scripts/check_indirect_prompt_injection_no_call.py +328 -0
  397. package/vendor/ars/scripts/check_instruction_data_boundary.py +236 -0
  398. package/vendor/ars/scripts/check_judge_prompt_version.py +125 -0
  399. package/vendor/ars/scripts/check_literature_corpus_schema.py +402 -0
  400. package/vendor/ars/scripts/check_model_tiering.py +223 -0
  401. package/vendor/ars/scripts/check_panel_synthesis.py +1331 -0
  402. package/vendor/ars/scripts/check_passport_reset_contract.py +214 -0
  403. package/vendor/ars/scripts/check_pattern_eval_manifest.py +422 -0
  404. package/vendor/ars/scripts/check_persuasion_invariance_fixtures.py +637 -0
  405. package/vendor/ars/scripts/check_phase_conformance.py +2180 -0
  406. package/vendor/ars/scripts/check_pipeline_boundary_semantics.py +600 -0
  407. package/vendor/ars/scripts/check_pipeline_integrity.py +340 -0
  408. package/vendor/ars/scripts/check_policy_anchor_protocol.py +159 -0
  409. package/vendor/ars/scripts/check_policy_anchor_table.py +286 -0
  410. package/vendor/ars/scripts/check_preprint_venues_consistency.py +123 -0
  411. package/vendor/ars/scripts/check_prisma_trAIce_freshness.py +84 -0
  412. package/vendor/ars/scripts/check_promotion_bakeoff_preregistration.py +1303 -0
  413. package/vendor/ars/scripts/check_ranking_lift.py +323 -0
  414. package/vendor/ars/scripts/check_re_review_synthesis.py +2719 -0
  415. package/vendor/ars/scripts/check_receipt_enum_sync.py +203 -0
  416. package/vendor/ars/scripts/check_repro_lock.py +85 -0
  417. package/vendor/ars/scripts/check_review_pathway_output.py +276 -0
  418. package/vendor/ars/scripts/check_reviewer_data_fences.py +224 -0
  419. package/vendor/ars/scripts/check_reviewer_finding_contract.py +783 -0
  420. package/vendor/ars/scripts/check_reviewer_role_label.py +447 -0
  421. package/vendor/ars/scripts/check_reviewer_scoring_honesty.py +292 -0
  422. package/vendor/ars/scripts/check_reviewer_sprint_prompt_sync.py +409 -0
  423. package/vendor/ars/scripts/check_revision_claim_drift_suite_v2.py +1622 -0
  424. package/vendor/ars/scripts/check_revision_token_conservation.py +263 -0
  425. package/vendor/ars/scripts/check_risk_register.py +280 -0
  426. package/vendor/ars/scripts/check_role_scoped_contract.py +702 -0
  427. package/vendor/ars/scripts/check_rq_framing_patterns.py +184 -0
  428. package/vendor/ars/scripts/check_rubric_weight_consistency.py +32 -0
  429. package/vendor/ars/scripts/check_seeded_defect_fixtures.py +425 -0
  430. package/vendor/ars/scripts/check_setup_cross_model_parity.py +139 -0
  431. package/vendor/ars/scripts/check_spec_consistency.py +1249 -0
  432. package/vendor/ars/scripts/check_sprint_contract.py +371 -0
  433. package/vendor/ars/scripts/check_stage_capability_matrix.py +784 -0
  434. package/vendor/ars/scripts/check_submission_packet_manifest_integration.py +358 -0
  435. package/vendor/ars/scripts/check_surface_form_parity.py +445 -0
  436. package/vendor/ars/scripts/check_task_type.py +22 -0
  437. package/vendor/ars/scripts/check_tools_allowlist.py +538 -0
  438. package/vendor/ars/scripts/check_tortured_phrase_screening_integration.py +1974 -0
  439. package/vendor/ars/scripts/check_v3_10_134_write_scope.py +286 -0
  440. package/vendor/ars/scripts/check_v3_10_policy.py +656 -0
  441. package/vendor/ars/scripts/check_v3_6_6_ab_manifest.py +364 -0
  442. package/vendor/ars/scripts/check_v3_6_7_pattern_protection.py +1366 -0
  443. package/vendor/ars/scripts/check_v3_6_8_audit_scope_block.py +464 -0
  444. package/vendor/ars/scripts/check_v3_6_8_cite_provenance_pipeline.py +231 -0
  445. package/vendor/ars/scripts/check_v3_6_8_frontmatter_trust_schema.py +248 -0
  446. package/vendor/ars/scripts/check_v3_6_8_mark_read_commands.py +79 -0
  447. package/vendor/ars/scripts/check_v3_6_8_pattern_protection.py +941 -0
  448. package/vendor/ars/scripts/check_v3_7_3_three_layer_citation.py +318 -0
  449. package/vendor/ars/scripts/check_v3_8_annotation_literal_sync.py +228 -0
  450. package/vendor/ars/scripts/check_v3_9_0_triangulation.py +366 -0
  451. package/vendor/ars/scripts/check_v3_9_2_phase_boundary.py +270 -0
  452. package/vendor/ars/scripts/check_v3_9_4_temporal_verification.py +139 -0
  453. package/vendor/ars/scripts/check_venue_disclosure_policies.py +63 -0
  454. package/vendor/ars/scripts/check_version_consistency.py +826 -0
  455. package/vendor/ars/scripts/check_workflow_classification.py +223 -0
  456. package/vendor/ars/scripts/chinese_literature_client.py +1938 -0
  457. package/vendor/ars/scripts/citation_verification_summary.py +85 -0
  458. package/vendor/ars/scripts/claim_audit_calibration.py +517 -0
  459. package/vendor/ars/scripts/claim_audit_finalizer.py +456 -0
  460. package/vendor/ars/scripts/claim_audit_pipeline.py +1594 -0
  461. package/vendor/ars/scripts/claim_registry_coverage.py +493 -0
  462. package/vendor/ars/scripts/claim_standing_discovery.py +784 -0
  463. package/vendor/ars/scripts/claim_standing_stance_runner.py +758 -0
  464. package/vendor/ars/scripts/claim_standing_stance_scorer.py +239 -0
  465. package/vendor/ars/scripts/claim_strength_drift_disposition.py +666 -0
  466. package/vendor/ars/scripts/contamination_signals.py +689 -0
  467. package/vendor/ars/scripts/corpus_consumer_manifest.json +19 -0
  468. package/vendor/ars/scripts/cross_model_codex_transport.py +1374 -0
  469. package/vendor/ars/scripts/cross_model_codex_verify.sh +6 -0
  470. package/vendor/ars/scripts/cross_model_handoff.py +359 -0
  471. package/vendor/ars/scripts/cross_model_smoke_test.sh +183 -0
  472. package/vendor/ars/scripts/cross_model_smoke_test_codex.sh +35 -0
  473. package/vendor/ars/scripts/cross_model_verification/gemini_is_grounded.jq +45 -0
  474. package/vendor/ars/scripts/cross_model_verification/gemini_sources.jq +45 -0
  475. package/vendor/ars/scripts/cross_model_verification/normalize_compat_verdict.py +57 -0
  476. package/vendor/ars/scripts/cross_model_verification/openai_has_completed_web_search.jq +15 -0
  477. package/vendor/ars/scripts/cross_model_verification/openai_sources.jq +17 -0
  478. package/vendor/ars/scripts/cross_model_verification/openai_text.jq +11 -0
  479. package/vendor/ars/scripts/crossref_client.py +225 -0
  480. package/vendor/ars/scripts/dispatch_e4_panel.py +2731 -0
  481. package/vendor/ars/scripts/evidence_rows.py +2043 -0
  482. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3.json +32 -0
  483. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_input.json +27 -0
  484. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_prime.json +32 -0
  485. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_prime_input.json +27 -0
  486. package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_override.json +50 -0
  487. package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_override_action.json +14 -0
  488. package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_pass.json +44 -0
  489. package/vendor/ars/scripts/fixtures/adjudication_activity/explicit_user_request_log.json +26 -0
  490. package/vendor/ars/scripts/fixtures/adjudication_activity/mandatory_checkpoint_log.json +116 -0
  491. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/README.md +56 -0
  492. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +24 -0
  493. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
  494. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
  495. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.verdict.yaml +19 -0
  496. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a7_orphan_completion/2026-04-30T15-22-04Z-d8f3.jsonl +3 -0
  497. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +26 -0
  498. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
  499. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
  500. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.verdict.yaml +19 -0
  501. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +24 -0
  502. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
  503. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
  504. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.verdict.yaml +12 -0
  505. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/retraction.json +46 -0
  506. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/retraction_check_attestation.json +44 -0
  507. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase.json +44 -0
  508. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase_v1_2_abstract_missing.json +107 -0
  509. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase_v1_2_detected.json +124 -0
  510. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/expected_outcomes.json +77 -0
  511. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/manifest.yaml +37 -0
  512. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/001-valid-doi-test.json +20 -0
  513. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/002-valid-arxiv-test.json +19 -0
  514. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/003-fabricated-test.json +20 -0
  515. package/vendor/ars/scripts/fixtures/claim_audit_calibration/gold_set.json +344 -0
  516. package/vendor/ars/scripts/fixtures/claim_standing_candidate_ledger/query_plan.json +108 -0
  517. package/vendor/ars/scripts/fixtures/claim_standing_candidate_ledger/retrieval_input.json +179 -0
  518. package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/concern_tracker.json +129 -0
  519. package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/response_skeleton.md +19 -0
  520. package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/source_letter.txt +9 -0
  521. package/vendor/ars/scripts/fixtures/content_coverage_advisory/base_draft.json +48 -0
  522. package/vendor/ars/scripts/fixtures/content_coverage_advisory/base_inventory.json +38 -0
  523. package/vendor/ars/scripts/fixtures/content_coverage_advisory/packet/consent.txt +1 -0
  524. package/vendor/ars/scripts/fixtures/content_coverage_advisory/session_sources.json +3 -0
  525. package/vendor/ars/scripts/fixtures/cross_document_consistency/README.md +6 -0
  526. package/vendor/ars/scripts/fixtures/cross_document_consistency/accepted_draft.md +35 -0
  527. package/vendor/ars/scripts/fixtures/cross_document_consistency/cases.json +75 -0
  528. package/vendor/ars/scripts/fixtures/cross_document_consistency/preregistration.md +5 -0
  529. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/forbidden_event.jsonl +4 -0
  530. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/grounded_verified.jsonl +3 -0
  531. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/malformed.jsonl +3 -0
  532. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/missing_search.jsonl +2 -0
  533. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/multiple_finals.jsonl +4 -0
  534. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/not_found.jsonl +3 -0
  535. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/unbound_source.jsonl +3 -0
  536. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/wrong_search_shape.jsonl +3 -0
  537. package/vendor/ars/scripts/fixtures/evidence_rows/phase_e_inputs.json +114 -0
  538. package/vendor/ars/scripts/fixtures/evidence_rows/session_sources.json +5 -0
  539. package/vendor/ars/scripts/fixtures/human_subjects_authority/cross-border-us-tw-gdpr.json +71 -0
  540. package/vendor/ars/scripts/fixtures/human_subjects_authority/gdpr-member-state-unresolved.json +62 -0
  541. package/vendor/ars/scripts/fixtures/human_subjects_authority/missing-data-axis.json +45 -0
  542. package/vendor/ars/scripts/fixtures/human_subjects_authority/no-profile.json +33 -0
  543. package/vendor/ars/scripts/fixtures/human_subjects_authority/tw-gdpr-two-axis.json +74 -0
  544. package/vendor/ars/scripts/fixtures/human_subjects_authority/us-gdpr-two-axis.json +74 -0
  545. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/lint-near-misses.json +72 -0
  546. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/no-profile-request.json +18 -0
  547. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/tw-candidates-request.json +63 -0
  548. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/us-candidates-request.json +77 -0
  549. package/vendor/ars/scripts/fixtures/review_target_context/exact-declaration.json +24 -0
  550. package/vendor/ars/scripts/fixtures/review_target_context/field-general-declaration.json +24 -0
  551. package/vendor/ars/scripts/fixtures/review_target_context/msr-2027-technical-full-declaration.json +24 -0
  552. package/vendor/ars/scripts/fixtures/review_target_context/synthetic-registry.json +92 -0
  553. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/README.md +21 -0
  554. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_attested_only.json +55 -0
  555. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_machine_supported.json +49 -0
  556. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_not_isolated.json +56 -0
  557. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_unknown.json +49 -0
  558. package/vendor/ars/scripts/fixtures/submission_package/clean/paper.md +16 -0
  559. package/vendor/ars/scripts/fixtures/submission_package/clean/references.bib +13 -0
  560. package/vendor/ars/scripts/fixtures/submission_package/fallback_authoryear/paper.md +11 -0
  561. package/vendor/ars/scripts/fixtures/submission_package/fallback_authoryear/references.bib +13 -0
  562. package/vendor/ars/scripts/fixtures/submission_package/fallback_latex/paper.tex +9 -0
  563. package/vendor/ars/scripts/fixtures/submission_package/fallback_latex/references.bib +13 -0
  564. package/vendor/ars/scripts/fixtures/submission_package/marker_no_join/paper.md +7 -0
  565. package/vendor/ars/scripts/fixtures/submission_package/orphan_intext/paper.md +8 -0
  566. package/vendor/ars/scripts/fixtures/submission_package/orphan_intext/references.bib +6 -0
  567. package/vendor/ars/scripts/fixtures/submission_package/passports/corpus_only.yaml +9 -0
  568. package/vendor/ars/scripts/fixtures/submission_package/passports/summary_join.yaml +16 -0
  569. package/vendor/ars/scripts/fixtures/submission_package/profiles/full.yaml +15 -0
  570. package/vendor/ars/scripts/fixtures/submission_package/profiles/tight.yaml +15 -0
  571. package/vendor/ars/scripts/fixtures/submission_package/summary_join/paper.md +7 -0
  572. package/vendor/ars/scripts/fixtures/submission_package/uncited_reference/paper.md +9 -0
  573. package/vendor/ars/scripts/fixtures/submission_package/uncited_reference/references.bib +13 -0
  574. package/vendor/ars/scripts/fixtures/submission_package/venue_clean/paper.md +26 -0
  575. package/vendor/ars/scripts/fixtures/submission_package/venue_clean/references.bib +13 -0
  576. package/vendor/ars/scripts/fixtures/submission_package/venue_violations/paper.md +20 -0
  577. package/vendor/ars/scripts/fixtures/submission_package/venue_violations/references.bib +13 -0
  578. package/vendor/ars/scripts/fixtures/submission_packet_manifest/base_inventory.json +101 -0
  579. package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/consent-materials.txt +3 -0
  580. package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/training-certificate.txt +2 -0
  581. package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/tw-consent-materials.txt +3 -0
  582. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/corpus_input.yaml +30 -0
  583. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/own_draft.md +36 -0
  584. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/own_draft.tex +21 -0
  585. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/seed_expectations.json +218 -0
  586. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/snapshot.json +164 -0
  587. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/snapshot_manifest.json +29 -0
  588. package/vendor/ars/scripts/fixtures/transport_bodies/README.md +55 -0
  589. package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/empty_feed.xml +10 -0
  590. package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/error_5xx.html +7 -0
  591. package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/id_hit.xml +26 -0
  592. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/README.md +69 -0
  593. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/cnki_landing_page.html +13 -0
  594. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/error_5xx.html +2 -0
  595. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_ambiguous.json +16 -0
  596. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_hit.json +16 -0
  597. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_zero.json +14 -0
  598. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coverage_hit.json +15 -0
  599. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coverage_zero.json +13 -0
  600. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_hit.json +43 -0
  601. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_issn_mismatch.json +31 -0
  602. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_no_doi.json +31 -0
  603. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_unknown_ra.json +34 -0
  604. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_year_mismatch.json +31 -0
  605. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_absent.json +4 -0
  606. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_exists.json +16 -0
  607. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_internal_error.json +4 -0
  608. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/istic_csl_hit.json +25 -0
  609. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/istic_csl_other_title.json +18 -0
  610. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_cnki.json +6 -0
  611. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_crossref.json +6 -0
  612. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_istic.json +6 -0
  613. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_unknown_prefix.json +6 -0
  614. package/vendor/ars/scripts/fixtures/transport_bodies/crossref/doi_hit.json +24 -0
  615. package/vendor/ars/scripts/fixtures/transport_bodies/crossref/error_5xx.html +7 -0
  616. package/vendor/ars/scripts/fixtures/transport_bodies/crossref/title_search_miss.json +15 -0
  617. package/vendor/ars/scripts/fixtures/transport_bodies/openalex/doi_hit.json +29 -0
  618. package/vendor/ars/scripts/fixtures/transport_bodies/openalex/error_5xx.json +4 -0
  619. package/vendor/ars/scripts/fixtures/transport_bodies/openalex/title_search_miss.json +11 -0
  620. package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/doi_hit.json +14 -0
  621. package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/error_5xx.json +3 -0
  622. package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/title_search_miss.json +5 -0
  623. package/vendor/ars/scripts/human_read_attestation_resolver.py +467 -0
  624. package/vendor/ars/scripts/ideation_diversity_assignment_gate.py +430 -0
  625. package/vendor/ars/scripts/inquiry_branch_ledger.py +2540 -0
  626. package/vendor/ars/scripts/legacy/ars_apply_revision_patch_v1_0.py +715 -0
  627. package/vendor/ars/scripts/legacy/check_re_review_synthesis_v1_0.py +2215 -0
  628. package/vendor/ars/scripts/migrate_literature_corpus_to_v3_10.py +223 -0
  629. package/vendor/ars/scripts/migrate_literature_corpus_to_v3_7_3.py +277 -0
  630. package/vendor/ars/scripts/migrate_literature_corpus_to_v3_9_0.py +304 -0
  631. package/vendor/ars/scripts/model_tiering_manifest.json +46 -0
  632. package/vendor/ars/scripts/openalex_client.py +232 -0
  633. package/vendor/ars/scripts/parse_audit_verdict.py +802 -0
  634. package/vendor/ars/scripts/pdf_content_classifier_worker.py +176 -0
  635. package/vendor/ars/scripts/pdf_read_preflight.py +1455 -0
  636. package/vendor/ars/scripts/policy_anchor_disclosure_referee.py +354 -0
  637. package/vendor/ars/scripts/recompute_receipts.py +1414 -0
  638. package/vendor/ars/scripts/render_claim_standing_view.py +374 -0
  639. package/vendor/ars/scripts/render_eval_comment.py +130 -0
  640. package/vendor/ars/scripts/render_harness_retirement_issue.py +167 -0
  641. package/vendor/ars/scripts/repro_lock_validation.py +90 -0
  642. package/vendor/ars/scripts/research_workflow_profile.py +1079 -0
  643. package/vendor/ars/scripts/resolve_human_subjects_authority.py +1158 -0
  644. package/vendor/ars/scripts/resolve_review_target_context.py +683 -0
  645. package/vendor/ars/scripts/resume_e4_record.py +509 -0
  646. package/vendor/ars/scripts/retraction_status.py +484 -0
  647. package/vendor/ars/scripts/review_criteria_binding.py +889 -0
  648. package/vendor/ars/scripts/review_panel_provenance.py +744 -0
  649. package/vendor/ars/scripts/revision_roadmap.py +1967 -0
  650. package/vendor/ars/scripts/run_ci_pytest_manifest.py +116 -0
  651. package/vendor/ars/scripts/run_codex_audit.sh +1191 -0
  652. package/vendor/ars/scripts/run_evals.py +513 -0
  653. package/vendor/ars/scripts/run_ideation_diversity_no_call.py +3505 -0
  654. package/vendor/ars/scripts/run_indirect_prompt_injection_no_call.py +3330 -0
  655. package/vendor/ars/scripts/run_indirect_prompt_injection_probe.py +399 -0
  656. package/vendor/ars/scripts/run_review_criteria_constructive_value.py +1895 -0
  657. package/vendor/ars/scripts/run_role_topology_utility_dry_run.py +606 -0
  658. package/vendor/ars/scripts/score_review_criteria_constructive_value.py +617 -0
  659. package/vendor/ars/scripts/semantic_scholar_client.py +291 -0
  660. package/vendor/ars/scripts/slr_lineage.py +59 -0
  661. package/vendor/ars/scripts/sync_adapter_docs.py +118 -0
  662. package/vendor/ars/scripts/temporal_integrity_audit.py +840 -0
  663. package/vendor/ars/scripts/test_431_exact_or_bust.py +253 -0
  664. package/vendor/ars/scripts/test__eval_threshold_gate.py +125 -0
  665. package/vendor/ars/scripts/test__markdown_lint_util.py +129 -0
  666. package/vendor/ars/scripts/test__next_verified_at_ms.py +256 -0
  667. package/vendor/ars/scripts/test_adjacent_framing_probe_lint.py +171 -0
  668. package/vendor/ars/scripts/test_adjudication_activity.py +1515 -0
  669. package/vendor/ars/scripts/test_ars_anchorize_draft.py +178 -0
  670. package/vendor/ars/scripts/test_ars_apply_revision_patch.py +1335 -0
  671. package/vendor/ars/scripts/test_ars_cache_invalidate.py +51 -0
  672. package/vendor/ars/scripts/test_ars_mark_read.py +910 -0
  673. package/vendor/ars/scripts/test_ars_update_check.py +816 -0
  674. package/vendor/ars/scripts/test_ars_write_scope_guard.py +790 -0
  675. package/vendor/ars/scripts/test_arxiv_client.py +374 -0
  676. package/vendor/ars/scripts/test_audit_schemas.py +560 -0
  677. package/vendor/ars/scripts/test_audit_snapshot_render_section_0.py +105 -0
  678. package/vendor/ars/scripts/test_block_parser.py +259 -0
  679. package/vendor/ars/scripts/test_bootstrap_timeline_yaml.py +148 -0
  680. package/vendor/ars/scripts/test_build_claim_standing_candidate_ledger.py +1203 -0
  681. package/vendor/ars/scripts/test_build_claim_standing_query_plan.py +791 -0
  682. package/vendor/ars/scripts/test_build_submission_packet_manifest.py +2644 -0
  683. package/vendor/ars/scripts/test_check_215_field_norm.py +238 -0
  684. package/vendor/ars/scripts/test_check_216_surface_form.py +341 -0
  685. package/vendor/ars/scripts/test_check_268_nested_commitment_ledger.py +194 -0
  686. package/vendor/ars/scripts/test_check_390_revision_patch_discipline.py +279 -0
  687. package/vendor/ars/scripts/test_check_392_citation_verification_intake.py +144 -0
  688. package/vendor/ars/scripts/test_check_394_submission_policy.py +200 -0
  689. package/vendor/ars/scripts/test_check_439_format_profile.py +252 -0
  690. package/vendor/ars/scripts/test_check_619_disclosure_closeout.py +158 -0
  691. package/vendor/ars/scripts/test_check_630_codex_subscription_transport.py +245 -0
  692. package/vendor/ars/scripts/test_check_669_review_pathway_rule_trace.py +304 -0
  693. package/vendor/ars/scripts/test_check_670_revision_roadmap_integration.py +260 -0
  694. package/vendor/ars/scripts/test_check_673_adjudication_activity.py +367 -0
  695. package/vendor/ars/scripts/test_check_684_review_criteria_binding.py +274 -0
  696. package/vendor/ars/scripts/test_check_agents_mirror_sync.py +137 -0
  697. package/vendor/ars/scripts/test_check_audit_artifact_consistency.py +2133 -0
  698. package/vendor/ars/scripts/test_check_benchmark_report.py +117 -0
  699. package/vendor/ars/scripts/test_check_bibliographic_integrity_signals.py +609 -0
  700. package/vendor/ars/scripts/test_check_calibration_tiers.py +376 -0
  701. package/vendor/ars/scripts/test_check_changelog_covers_merges.py +509 -0
  702. package/vendor/ars/scripts/test_check_ci_pytest_manifest.py +425 -0
  703. package/vendor/ars/scripts/test_check_claim_standing_candidate_ledger_integration.py +256 -0
  704. package/vendor/ars/scripts/test_check_claim_standing_freshness.py +277 -0
  705. package/vendor/ars/scripts/test_check_collaboration_depth_rubric.py +239 -0
  706. package/vendor/ars/scripts/test_check_command_frontmatter_name.py +178 -0
  707. package/vendor/ars/scripts/test_check_committee_correspondence.py +298 -0
  708. package/vendor/ars/scripts/test_check_compliance_report.py +381 -0
  709. package/vendor/ars/scripts/test_check_content_coverage_advisory_integration.py +308 -0
  710. package/vendor/ars/scripts/test_check_control_availability.py +361 -0
  711. package/vendor/ars/scripts/test_check_cross_document_consistency_advisory_integration.py +698 -0
  712. package/vendor/ars/scripts/test_check_cross_model_handoff_contract.py +301 -0
  713. package/vendor/ars/scripts/test_check_cross_model_verification_sync.py +203 -0
  714. package/vendor/ars/scripts/test_check_data_access_level.py +227 -0
  715. package/vendor/ars/scripts/test_check_data_flows.py +382 -0
  716. package/vendor/ars/scripts/test_check_decision_contract.py +387 -0
  717. package/vendor/ars/scripts/test_check_degradation_registry.py +270 -0
  718. package/vendor/ars/scripts/test_check_distribution_surface_claims.py +225 -0
  719. package/vendor/ars/scripts/test_check_domain_evidence_profile.py +439 -0
  720. package/vendor/ars/scripts/test_check_e4_promotion.py +163 -0
  721. package/vendor/ars/scripts/test_check_evals_gold_set.py +312 -0
  722. package/vendor/ars/scripts/test_check_evidence_row_integration.py +187 -0
  723. package/vendor/ars/scripts/test_check_field_norm_severity.py +173 -0
  724. package/vendor/ars/scripts/test_check_firm_rules_sync.py +342 -0
  725. package/vendor/ars/scripts/test_check_heldout_measurement_report.py +1508 -0
  726. package/vendor/ars/scripts/test_check_human_subjects_output_contract.py +129 -0
  727. package/vendor/ars/scripts/test_check_human_subjects_reference_migration.py +756 -0
  728. package/vendor/ars/scripts/test_check_instruction_data_boundary.py +204 -0
  729. package/vendor/ars/scripts/test_check_judge_prompt_version.py +90 -0
  730. package/vendor/ars/scripts/test_check_model_tiering.py +236 -0
  731. package/vendor/ars/scripts/test_check_panel_synthesis.py +1658 -0
  732. package/vendor/ars/scripts/test_check_passport_reset_contract.py +249 -0
  733. package/vendor/ars/scripts/test_check_pattern_eval_manifest.py +381 -0
  734. package/vendor/ars/scripts/test_check_persuasion_invariance_fixtures.py +591 -0
  735. package/vendor/ars/scripts/test_check_phase_conformance.py +4366 -0
  736. package/vendor/ars/scripts/test_check_pipeline_boundary_semantics.py +931 -0
  737. package/vendor/ars/scripts/test_check_pipeline_integrity.py +243 -0
  738. package/vendor/ars/scripts/test_check_policy_anchor_protocol.py +236 -0
  739. package/vendor/ars/scripts/test_check_policy_anchor_table.py +295 -0
  740. package/vendor/ars/scripts/test_check_prisma_trAIce_freshness.py +68 -0
  741. package/vendor/ars/scripts/test_check_promotion_bakeoff_preregistration.py +799 -0
  742. package/vendor/ars/scripts/test_check_ranking_lift.py +377 -0
  743. package/vendor/ars/scripts/test_check_re_review_synthesis.py +3398 -0
  744. package/vendor/ars/scripts/test_check_receipt_enum_sync.py +176 -0
  745. package/vendor/ars/scripts/test_check_repro_lock.py +107 -0
  746. package/vendor/ars/scripts/test_check_reviewer_data_fences.py +334 -0
  747. package/vendor/ars/scripts/test_check_reviewer_finding_contract.py +1107 -0
  748. package/vendor/ars/scripts/test_check_reviewer_role_label.py +491 -0
  749. package/vendor/ars/scripts/test_check_reviewer_scoring_honesty.py +222 -0
  750. package/vendor/ars/scripts/test_check_reviewer_sprint_prompt_sync.py +373 -0
  751. package/vendor/ars/scripts/test_check_revision_claim_drift_suite_v2.py +1925 -0
  752. package/vendor/ars/scripts/test_check_revision_token_conservation.py +398 -0
  753. package/vendor/ars/scripts/test_check_risk_register.py +336 -0
  754. package/vendor/ars/scripts/test_check_role_scoped_contract.py +1104 -0
  755. package/vendor/ars/scripts/test_check_rq_framing_patterns.py +110 -0
  756. package/vendor/ars/scripts/test_check_rubric_weight_consistency.py +32 -0
  757. package/vendor/ars/scripts/test_check_seeded_defect_fixtures.py +392 -0
  758. package/vendor/ars/scripts/test_check_setup_cross_model_parity.py +121 -0
  759. package/vendor/ars/scripts/test_check_spec_consistency.py +1081 -0
  760. package/vendor/ars/scripts/test_check_sprint_contract.py +458 -0
  761. package/vendor/ars/scripts/test_check_stage_capability_matrix.py +698 -0
  762. package/vendor/ars/scripts/test_check_submission_packet_manifest_integration.py +177 -0
  763. package/vendor/ars/scripts/test_check_surface_form_parity.py +417 -0
  764. package/vendor/ars/scripts/test_check_task_type.py +116 -0
  765. package/vendor/ars/scripts/test_check_tools_allowlist.py +835 -0
  766. package/vendor/ars/scripts/test_check_tortured_phrase_screening_integration.py +1306 -0
  767. package/vendor/ars/scripts/test_check_v3_10_134_write_scope.py +251 -0
  768. package/vendor/ars/scripts/test_check_v3_10_policy.py +547 -0
  769. package/vendor/ars/scripts/test_check_v3_6_7_pattern_protection.py +960 -0
  770. package/vendor/ars/scripts/test_check_v3_6_8_audit_scope_block.py +1000 -0
  771. package/vendor/ars/scripts/test_check_v3_6_8_cite_provenance_pipeline.py +454 -0
  772. package/vendor/ars/scripts/test_check_v3_6_8_frontmatter_trust_schema.py +581 -0
  773. package/vendor/ars/scripts/test_check_v3_6_8_mark_read_commands.py +120 -0
  774. package/vendor/ars/scripts/test_check_v3_6_8_pattern_protection.py +1138 -0
  775. package/vendor/ars/scripts/test_check_v3_7_3_three_layer_citation.py +566 -0
  776. package/vendor/ars/scripts/test_check_v3_8_annotation_literal_sync.py +262 -0
  777. package/vendor/ars/scripts/test_check_v3_9_0_triangulation.py +321 -0
  778. package/vendor/ars/scripts/test_check_v3_9_2_phase_boundary.py +190 -0
  779. package/vendor/ars/scripts/test_check_v3_9_4_temporal_verification.py +471 -0
  780. package/vendor/ars/scripts/test_check_version_consistency.py +1462 -0
  781. package/vendor/ars/scripts/test_check_workflow_classification.py +225 -0
  782. package/vendor/ars/scripts/test_chinese_literature_client.py +1889 -0
  783. package/vendor/ars/scripts/test_citation_existence_policy.py +480 -0
  784. package/vendor/ars/scripts/test_citation_verification_summary.py +342 -0
  785. package/vendor/ars/scripts/test_claim_audit_calibration.py +882 -0
  786. package/vendor/ars/scripts/test_claim_audit_finalizer.py +979 -0
  787. package/vendor/ars/scripts/test_claim_audit_pipeline.py +2398 -0
  788. package/vendor/ars/scripts/test_claim_audit_schema.py +1778 -0
  789. package/vendor/ars/scripts/test_claim_intent_manifest.py +666 -0
  790. package/vendor/ars/scripts/test_claim_registry_coverage.py +246 -0
  791. package/vendor/ars/scripts/test_claim_standing_discovery.py +579 -0
  792. package/vendor/ars/scripts/test_claim_standing_pipeline_wiring.py +290 -0
  793. package/vendor/ars/scripts/test_claim_standing_stance_assets.py +399 -0
  794. package/vendor/ars/scripts/test_claim_standing_stance_contracts.py +383 -0
  795. package/vendor/ars/scripts/test_claim_standing_stance_runner.py +349 -0
  796. package/vendor/ars/scripts/test_claim_standing_transmissions.py +605 -0
  797. package/vendor/ars/scripts/test_claim_strength_drift_disposition.py +517 -0
  798. package/vendor/ars/scripts/test_claim_verification_coverage_contract.py +104 -0
  799. package/vendor/ars/scripts/test_contamination_signals.py +1086 -0
  800. package/vendor/ars/scripts/test_content_coverage_advisory.py +1767 -0
  801. package/vendor/ars/scripts/test_cross_document_consistency_advisory.py +1270 -0
  802. package/vendor/ars/scripts/test_cross_model_codex_transport.py +1152 -0
  803. package/vendor/ars/scripts/test_cross_model_handoff.py +571 -0
  804. package/vendor/ars/scripts/test_cross_model_verification_guards.py +792 -0
  805. package/vendor/ars/scripts/test_crossref_client.py +393 -0
  806. package/vendor/ars/scripts/test_dispatch_e4_panel.py +4235 -0
  807. package/vendor/ars/scripts/test_e2e_claim_audit.py +540 -0
  808. package/vendor/ars/scripts/test_eval_harness_workflow.py +140 -0
  809. package/vendor/ars/scripts/test_evals_citation_extraction.py +150 -0
  810. package/vendor/ars/scripts/test_evals_lift_report_schema.py +108 -0
  811. package/vendor/ars/scripts/test_evidence_rows.py +2593 -0
  812. package/vendor/ars/scripts/test_experiment_provenance.py +915 -0
  813. package/vendor/ars/scripts/test_human_read_attestation_resolver.py +477 -0
  814. package/vendor/ars/scripts/test_ideation_diversity_assignment_gate.py +615 -0
  815. package/vendor/ars/scripts/test_indirect_prompt_injection_behavior_probe.py +250 -0
  816. package/vendor/ars/scripts/test_inquiry_branch_ledger.py +2296 -0
  817. package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_10.py +248 -0
  818. package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_7_3.py +545 -0
  819. package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_9_0.py +497 -0
  820. package/vendor/ars/scripts/test_normalize_compat_verdict.py +149 -0
  821. package/vendor/ars/scripts/test_openalex_client.py +490 -0
  822. package/vendor/ars/scripts/test_passport_yaml.py +104 -0
  823. package/vendor/ars/scripts/test_pattern_eval_runtime.py +1295 -0
  824. package/vendor/ars/scripts/test_pdf_read_preflight.py +1943 -0
  825. package/vendor/ars/scripts/test_policy_anchor_disclosure.py +666 -0
  826. package/vendor/ars/scripts/test_reading_probe_lint.py +218 -0
  827. package/vendor/ars/scripts/test_recompute_receipts.py +778 -0
  828. package/vendor/ars/scripts/test_render_claim_standing_view.py +192 -0
  829. package/vendor/ars/scripts/test_render_eval_comment.py +162 -0
  830. package/vendor/ars/scripts/test_render_harness_retirement_issue.py +110 -0
  831. package/vendor/ars/scripts/test_repro_lock_validation_drift.py +100 -0
  832. package/vendor/ars/scripts/test_research_workflow_profile.py +734 -0
  833. package/vendor/ars/scripts/test_resolve_human_subjects_authority.py +1219 -0
  834. package/vendor/ars/scripts/test_resolve_review_target_context.py +703 -0
  835. package/vendor/ars/scripts/test_resume_e4_record.py +315 -0
  836. package/vendor/ars/scripts/test_retraction_status.py +456 -0
  837. package/vendor/ars/scripts/test_review_criteria_binding.py +629 -0
  838. package/vendor/ars/scripts/test_review_panel_provenance.py +565 -0
  839. package/vendor/ars/scripts/test_review_pathway_rule_trace.py +821 -0
  840. package/vendor/ars/scripts/test_revision_roadmap.py +1255 -0
  841. package/vendor/ars/scripts/test_run_ci_pytest_manifest.py +186 -0
  842. package/vendor/ars/scripts/test_run_codex_audit_e2e.py +369 -0
  843. package/vendor/ars/scripts/test_run_evals.py +430 -0
  844. package/vendor/ars/scripts/test_run_guard_launcher.py +500 -0
  845. package/vendor/ars/scripts/test_run_ideation_diversity_no_call.py +1833 -0
  846. package/vendor/ars/scripts/test_run_indirect_prompt_injection_no_call.py +1889 -0
  847. package/vendor/ars/scripts/test_run_review_criteria_constructive_value.py +586 -0
  848. package/vendor/ars/scripts/test_run_role_topology_utility_dry_run.py +428 -0
  849. package/vendor/ars/scripts/test_score_review_criteria_constructive_value.py +340 -0
  850. package/vendor/ars/scripts/test_semantic_scholar_client.py +554 -0
  851. package/vendor/ars/scripts/test_slr_lineage_emission.py +230 -0
  852. package/vendor/ars/scripts/test_socratic_rq_non_generation_contract.py +173 -0
  853. package/vendor/ars/scripts/test_temporal_integrity_audit.py +438 -0
  854. package/vendor/ars/scripts/test_text_similarity.py +95 -0
  855. package/vendor/ars/scripts/test_title_fuzzy_false_positive.py +111 -0
  856. package/vendor/ars/scripts/test_tortured_phrase_screening.py +2959 -0
  857. package/vendor/ars/scripts/test_transport_fixture_citation_gate.py +338 -0
  858. package/vendor/ars/scripts/test_uncited_assertion.py +558 -0
  859. package/vendor/ars/scripts/test_v3_6_7_phase_6_6.py +1279 -0
  860. package/vendor/ars/scripts/test_validate_compliance_fixtures.py +36 -0
  861. package/vendor/ars/scripts/test_validate_ideation_diversity_assets.py +230 -0
  862. package/vendor/ars/scripts/test_venue_disclosure_contract.py +755 -0
  863. package/vendor/ars/scripts/test_verification_cache.py +280 -0
  864. package/vendor/ars/scripts/test_verification_gate.py +461 -0
  865. package/vendor/ars/scripts/test_verify_passport_cli.py +123 -0
  866. package/vendor/ars/scripts/test_verify_submission_package.py +1407 -0
  867. package/vendor/ars/scripts/test_version_records_schema.py +211 -0
  868. package/vendor/ars/scripts/tortured_phrase_screening.py +3502 -0
  869. package/vendor/ars/scripts/uncited_assertion_detector.py +254 -0
  870. package/vendor/ars/scripts/v3_6_7_inversion_manifest.json +9 -0
  871. package/vendor/ars/scripts/v3_6_8_inversion_manifest.json +10 -0
  872. package/vendor/ars/scripts/validate_claim_standing_stance_assets.py +249 -0
  873. package/vendor/ars/scripts/validate_compliance_fixtures.py +56 -0
  874. package/vendor/ars/scripts/validate_ideation_diversity_assets.py +303 -0
  875. package/vendor/ars/scripts/venue_disclosure_contract_harness.py +837 -0
  876. package/vendor/ars/scripts/verification_cache.py +276 -0
  877. package/vendor/ars/scripts/verification_gate/__init__.py +345 -0
  878. package/vendor/ars/scripts/verify_passport.py +133 -0
  879. package/vendor/ars/scripts/verify_submission_package.py +1657 -0
  880. package/vendor/ars/shared/agents/compliance_agent.md +136 -0
  881. package/vendor/ars/shared/artifact_reproducibility_pattern.md +173 -0
  882. package/vendor/ars/shared/benchmark_report.schema.json +81 -0
  883. package/vendor/ars/shared/benchmark_report_pattern.md +180 -0
  884. package/vendor/ars/shared/bibliographic_integrity_signals.md +142 -0
  885. package/vendor/ars/shared/collaboration_depth_rubric.md +154 -0
  886. package/vendor/ars/shared/compliance_checkpoint_protocol.md +162 -0
  887. package/vendor/ars/shared/compliance_report.schema.json +187 -0
  888. package/vendor/ars/shared/contracts/README.md +938 -0
  889. package/vendor/ars/shared/contracts/activity/adjudication_activity_input.schema.json +555 -0
  890. package/vendor/ars/shared/contracts/activity/adjudication_activity_store.schema.json +560 -0
  891. package/vendor/ars/shared/contracts/audit/audit_jsonl.schema.json +128 -0
  892. package/vendor/ars/shared/contracts/audit/audit_sidecar.schema.json +169 -0
  893. package/vendor/ars/shared/contracts/audit/audit_verdict.schema.json +133 -0
  894. package/vendor/ars/shared/contracts/audit/cross_document_consistency_advisory.schema.json +480 -0
  895. package/vendor/ars/shared/contracts/audit/cross_document_consistency_advisory_draft.schema.json +564 -0
  896. package/vendor/ars/shared/contracts/audit/cross_document_source_manifest.schema.json +202 -0
  897. package/vendor/ars/shared/contracts/audit/tortured_phrase_advisory.schema.json +1362 -0
  898. package/vendor/ars/shared/contracts/audit/tortured_phrase_snapshot.schema.json +208 -0
  899. package/vendor/ars/shared/contracts/audit/tortured_phrase_snapshot_manifest.schema.json +335 -0
  900. package/vendor/ars/shared/contracts/capability/stage_capability_matrix.json +434 -0
  901. package/vendor/ars/shared/contracts/claim_standing/candidate_ledger.schema.json +705 -0
  902. package/vendor/ars/shared/contracts/claim_standing/query_plan.schema.json +389 -0
  903. package/vendor/ars/shared/contracts/claim_standing/query_plan_v1_1.schema.json +782 -0
  904. package/vendor/ars/shared/contracts/claim_standing/retrieval_input.schema.json +440 -0
  905. package/vendor/ars/shared/contracts/claim_standing/stance_record.schema.json +460 -0
  906. package/vendor/ars/shared/contracts/claim_standing/transmission_ledger.schema.json +282 -0
  907. package/vendor/ars/shared/contracts/cross_model/codex_citation_receipt.schema.json +157 -0
  908. package/vendor/ars/shared/contracts/cross_model/codex_citation_request.schema.json +23 -0
  909. package/vendor/ars/shared/contracts/cross_model/promotion_bakeoff_sealed_commitment.schema.json +55 -0
  910. package/vendor/ars/shared/contracts/cross_model/promotion_bakeoff_sealed_reveal.schema.json +50 -0
  911. package/vendor/ars/shared/contracts/degradation_registry.json +428 -0
  912. package/vendor/ars/shared/contracts/evaluator/full.json +126 -0
  913. package/vendor/ars/shared/contracts/evidence/claim_registry.schema.json +50 -0
  914. package/vendor/ars/shared/contracts/evidence/claim_registry_coverage_report.schema.json +79 -0
  915. package/vendor/ars/shared/contracts/evidence/evidence_row.schema.json +504 -0
  916. package/vendor/ars/shared/contracts/evidence/evidence_row_v1_1.schema.json +364 -0
  917. package/vendor/ars/shared/contracts/evidence/evidence_row_v1_2.schema.json +714 -0
  918. package/vendor/ars/shared/contracts/evidence/evidence_row_v1_3.schema.json +524 -0
  919. package/vendor/ars/shared/contracts/human_subjects/authority_profile_registry.schema.json +497 -0
  920. package/vendor/ars/shared/contracts/human_subjects/committee_correspondence.schema.json +253 -0
  921. package/vendor/ars/shared/contracts/human_subjects/content_coverage_advisory.schema.json +620 -0
  922. package/vendor/ars/shared/contracts/human_subjects/irb_context_record.schema.json +329 -0
  923. package/vendor/ars/shared/contracts/human_subjects/resolved_authority_context.schema.json +347 -0
  924. package/vendor/ars/shared/contracts/human_subjects/review_pathway_rule_trace.schema.json +265 -0
  925. package/vendor/ars/shared/contracts/human_subjects/review_pathway_trace_request.schema.json +127 -0
  926. package/vendor/ars/shared/contracts/human_subjects/submission_packet_inventory.schema.json +240 -0
  927. package/vendor/ars/shared/contracts/human_subjects/submission_packet_manifest.schema.json +999 -0
  928. package/vendor/ars/shared/contracts/passport/audit_artifact_entry.schema.json +266 -0
  929. package/vendor/ars/shared/contracts/passport/bibliographic_integrity_signal.schema.json +1676 -0
  930. package/vendor/ars/shared/contracts/passport/citation_provenance.schema.json +107 -0
  931. package/vendor/ars/shared/contracts/passport/citation_verification_summary.schema.json +164 -0
  932. package/vendor/ars/shared/contracts/passport/claim_audit_result.schema.json +124 -0
  933. package/vendor/ars/shared/contracts/passport/claim_drift.schema.json +58 -0
  934. package/vendor/ars/shared/contracts/passport/claim_intent_manifest.schema.json +107 -0
  935. package/vendor/ars/shared/contracts/passport/constraint_violation.schema.json +65 -0
  936. package/vendor/ars/shared/contracts/passport/experiment_alignment_result.schema.json +69 -0
  937. package/vendor/ars/shared/contracts/passport/experiment_provenance_entry.schema.json +169 -0
  938. package/vendor/ars/shared/contracts/passport/human_read_log.schema.json +86 -0
  939. package/vendor/ars/shared/contracts/passport/inquiry_ledger_ref.schema.json +22 -0
  940. package/vendor/ars/shared/contracts/passport/literature_corpus_entry.schema.json +650 -0
  941. package/vendor/ars/shared/contracts/passport/preregistration_artifact.schema.json +132 -0
  942. package/vendor/ars/shared/contracts/passport/rejection_log.schema.json +89 -0
  943. package/vendor/ars/shared/contracts/passport/reset_ledger_entry.schema.json +158 -0
  944. package/vendor/ars/shared/contracts/passport/temporal_audit_results.schema.json +208 -0
  945. package/vendor/ars/shared/contracts/passport/terminal_policies.schema.json +50 -0
  946. package/vendor/ars/shared/contracts/passport/timeline.schema.json +102 -0
  947. package/vendor/ars/shared/contracts/passport/uncited_assertion.schema.json +56 -0
  948. package/vendor/ars/shared/contracts/passport/uncited_audit_failure.schema.json +72 -0
  949. package/vendor/ars/shared/contracts/passport/user_attested_read_resolution.schema.json +87 -0
  950. package/vendor/ars/shared/contracts/passport/version_records.schema.json +138 -0
  951. package/vendor/ars/shared/contracts/patch/block_manifest.schema.json +45 -0
  952. package/vendor/ars/shared/contracts/patch/legacy/v1_0/revision_patch.schema.json +110 -0
  953. package/vendor/ars/shared/contracts/patch/revision_patch.schema.json +250 -0
  954. package/vendor/ars/shared/contracts/pdf/pdf_content_classifier_diagnostic.schema.json +39 -0
  955. package/vendor/ars/shared/contracts/pdf/pdf_content_classifier_worker.schema.json +88 -0
  956. package/vendor/ars/shared/contracts/pdf/pdf_read_preflight.schema.json +227 -0
  957. package/vendor/ars/shared/contracts/re_review/input_manifest.schema.json +161 -0
  958. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/input_manifest.schema.json +134 -0
  959. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/precommitment.schema.json +193 -0
  960. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/traceability.schema.json +841 -0
  961. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/verdict_record.schema.json +244 -0
  962. package/vendor/ars/shared/contracts/re_review/precommitment.schema.json +193 -0
  963. package/vendor/ars/shared/contracts/re_review/traceability.schema.json +916 -0
  964. package/vendor/ars/shared/contracts/re_review/verdict_record.schema.json +245 -0
  965. package/vendor/ars/shared/contracts/research_workflow/inquiry_branch_ledger.schema.json +449 -0
  966. package/vendor/ars/shared/contracts/research_workflow/research_workflow_profile.schema.json +222 -0
  967. package/vendor/ars/shared/contracts/research_workflow/research_workflow_profile_selection_receipt.schema.json +111 -0
  968. package/vendor/ars/shared/contracts/review_target/constructive_review_findings.schema.json +172 -0
  969. package/vendor/ars/shared/contracts/review_target/criteria_registry.schema.json +97 -0
  970. package/vendor/ars/shared/contracts/review_target/review_criteria_binding_manifest.schema.json +235 -0
  971. package/vendor/ars/shared/contracts/review_target/review_criteria_source_receipt.schema.json +158 -0
  972. package/vendor/ars/shared/contracts/review_target/review_target_context.schema.json +106 -0
  973. package/vendor/ars/shared/contracts/review_target/review_target_declaration.schema.json +118 -0
  974. package/vendor/ars/shared/contracts/reviewer/full.json +114 -0
  975. package/vendor/ars/shared/contracts/reviewer/methodology_focus.json +75 -0
  976. package/vendor/ars/shared/contracts/reviewer/review_panel_provenance.schema.json +263 -0
  977. package/vendor/ars/shared/contracts/reviewer/review_panel_provenance_carrier.schema.json +122 -0
  978. package/vendor/ars/shared/contracts/reviewer/review_panel_provenance_input.schema.json +120 -0
  979. package/vendor/ars/shared/contracts/revision/author_adjudication.schema.json +251 -0
  980. package/vendor/ars/shared/contracts/revision/author_adjudication_input.schema.json +25 -0
  981. package/vendor/ars/shared/contracts/revision/claim_strength_drift_disposition.schema.json +97 -0
  982. package/vendor/ars/shared/contracts/revision/claim_strength_drift_disposition_input.schema.json +80 -0
  983. package/vendor/ars/shared/contracts/revision/claim_strength_drift_findings.schema.json +139 -0
  984. package/vendor/ars/shared/contracts/revision/claim_surface_manifest.schema.json +99 -0
  985. package/vendor/ars/shared/contracts/revision/integrity_correction_authorization.schema.json +140 -0
  986. package/vendor/ars/shared/contracts/revision/integrity_correction_authorization_input.schema.json +28 -0
  987. package/vendor/ars/shared/contracts/revision/integrity_correction_list.schema.json +48 -0
  988. package/vendor/ars/shared/contracts/revision/integrity_pass_receipt.schema.json +16 -0
  989. package/vendor/ars/shared/contracts/revision/revision_evidence_bundle.schema.json +134 -0
  990. package/vendor/ars/shared/contracts/revision/revision_roadmap.schema.json +334 -0
  991. package/vendor/ars/shared/contracts/submission/format_profile.example.yaml +33 -0
  992. package/vendor/ars/shared/contracts/submission/format_profile.schema.json +102 -0
  993. package/vendor/ars/shared/contracts/submission/submission_verification_report.schema.json +233 -0
  994. package/vendor/ars/shared/contracts/submission/venue_profile.schema.json +114 -0
  995. package/vendor/ars/shared/contracts/writer/full.json +87 -0
  996. package/vendor/ars/shared/cross_model_verification.md +714 -0
  997. package/vendor/ars/shared/evals_lift_report.schema.json +141 -0
  998. package/vendor/ars/shared/ground_truth_isolation_pattern.md +275 -0
  999. package/vendor/ars/shared/handoff_schemas.md +1209 -0
  1000. package/vendor/ars/shared/human_subjects_authority_registry.json +1278 -0
  1001. package/vendor/ars/shared/mode_spectrum.md +57 -0
  1002. package/vendor/ars/shared/model_tiering.md +83 -0
  1003. package/vendor/ars/shared/policy_data/nature_policy.md +56 -0
  1004. package/vendor/ars/shared/prisma_trAIce_protocol.md +157 -0
  1005. package/vendor/ars/shared/raise_framework.md +129 -0
  1006. package/vendor/ars/shared/references/authority_content_coverage_advisory_protocol.md +275 -0
  1007. package/vendor/ars/shared/references/claim_standing_candidate_ledger_protocol.md +66 -0
  1008. package/vendor/ars/shared/references/claim_strength_ladder.md +93 -0
  1009. package/vendor/ars/shared/references/cross_document_consistency_advisory_protocol.md +263 -0
  1010. package/vendor/ars/shared/references/evidence_row_protocol.md +260 -0
  1011. package/vendor/ars/shared/references/firm_rules.md +90 -0
  1012. package/vendor/ars/shared/references/human_subjects_authority_protocol.md +274 -0
  1013. package/vendor/ars/shared/references/intent_clarification_protocol.md +168 -0
  1014. package/vendor/ars/shared/references/irb_terminology_glossary.md +229 -0
  1015. package/vendor/ars/shared/references/protected_hedging_phrases.md +118 -0
  1016. package/vendor/ars/shared/references/psychometric_terminology_glossary.md +109 -0
  1017. package/vendor/ars/shared/references/review_criteria_consumer_protocol.md +238 -0
  1018. package/vendor/ars/shared/references/review_pathway_rule_trace_protocol.md +166 -0
  1019. package/vendor/ars/shared/references/submission_packet_manifest_protocol.md +292 -0
  1020. package/vendor/ars/shared/references/word_count_conventions.md +124 -0
  1021. package/vendor/ars/shared/research_workflow_profiles/field_general.json +1 -0
  1022. package/vendor/ars/shared/review_criteria_registry.json +207 -0
  1023. package/vendor/ars/shared/review_criteria_sources/msr-2027-technical-papers.2026-08-24.json +46 -0
  1024. package/vendor/ars/shared/review_criteria_sources/sigsoft-empirical-standards.2026-08-24.json +31 -0
  1025. package/vendor/ars/shared/sprint_contract.schema.json +482 -0
  1026. package/vendor/ars/shared/style_calibration_protocol.md +151 -0
  1027. package/vendor/ars/shared/templates/codex_audit_multifile_template.md +263 -0
  1028. package/vendor/ars/tools/release-discipline/.toolkit-version +1 -0
  1029. package/vendor/ars/tools/release-discipline/README.md +4 -0
  1030. package/vendor/ars/tools/release-discipline/scripts/_release_doc_alignment_schema.py +1011 -0
  1031. package/vendor/ars/tools/release-discipline/scripts/check_command_invariants.py +497 -0
  1032. package/vendor/ars/tools/release-discipline/scripts/check_release_doc_alignment.py +263 -0
  1033. package/vendor/ars/tools/release-discipline/scripts/sync-toolkit.sh +147 -0
  1034. package/vendor/windows/NOTICE.md +12 -0
  1035. package/vendor/windows/arm64/fd.exe +0 -0
  1036. package/vendor/windows/arm64/licenses/fd/LICENSE-APACHE +201 -0
  1037. package/vendor/windows/arm64/licenses/fd/LICENSE-MIT +21 -0
  1038. package/vendor/windows/arm64/licenses/ripgrep/COPYING +3 -0
  1039. package/vendor/windows/arm64/licenses/ripgrep/LICENSE-MIT +21 -0
  1040. package/vendor/windows/arm64/licenses/ripgrep/UNLICENSE +24 -0
  1041. package/vendor/windows/arm64/rg.exe +0 -0
  1042. package/vendor/windows/x64/fd.exe +0 -0
  1043. package/vendor/windows/x64/licenses/fd/LICENSE-APACHE +201 -0
  1044. package/vendor/windows/x64/licenses/fd/LICENSE-MIT +21 -0
  1045. package/vendor/windows/x64/licenses/ripgrep/COPYING +3 -0
  1046. package/vendor/windows/x64/licenses/ripgrep/LICENSE-MIT +21 -0
  1047. package/vendor/windows/x64/licenses/ripgrep/UNLICENSE +24 -0
  1048. package/vendor/windows/x64/rg.exe +0 -0
@@ -0,0 +1,1238 @@
1
+ #!/usr/bin/env python3
2
+ """Build or replay-validate the offline #655 Track A candidate ledger.
3
+
4
+ The runtime consumes only explicitly named local JSON files containing a frozen
5
+ query plan and already-returned synthetic or externally supplied retrieval
6
+ records. It has no discovery adapter, transport, model, stance classifier,
7
+ renderer, ambient clock, retry, or overwrite path.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import copy
14
+ import hashlib
15
+ import json
16
+ import os
17
+ import re
18
+ import sys
19
+ import unicodedata
20
+ from collections import Counter
21
+ from datetime import datetime
22
+ from itertools import combinations
23
+ from json import JSONDecodeError
24
+ from pathlib import Path
25
+ from typing import Any
26
+
27
+ from jsonschema import Draft202012Validator
28
+ from jsonschema.exceptions import SchemaError, ValidationError
29
+
30
+
31
+ PLAN_VERSION = "claim-standing-query-plan/1.0"
32
+ PLAN_VERSION_1_1 = "claim-standing-query-plan/1.1"
33
+ PLAN_SCHEMA_FILENAMES = {
34
+ PLAN_VERSION: "query_plan.schema.json",
35
+ PLAN_VERSION_1_1: "query_plan_v1_1.schema.json",
36
+ }
37
+ RETRIEVAL_ONLY_CONTENT_CLASSES = ["accepted_search_query"]
38
+ STANCE_CONTENT_CLASSES = [
39
+ "accepted_search_query",
40
+ "claim_and_selected_evidence_to_stance_provider",
41
+ ]
42
+ CHECKPOINT_REQUIRED_TIER = {"stage_2_5": "HIGH-IMPACT", "stage_4_5": "ALL"}
43
+ HIGH_IMPACT_BASIS_VALUES = (
44
+ "headline_conclusion",
45
+ "numerical",
46
+ "causal",
47
+ "methods_critical",
48
+ "disputed",
49
+ )
50
+ INPUT_VERSION = "claim-standing-retrieval-input/1.0"
51
+ LEDGER_VERSION = "claim-standing-candidate-ledger/1.0"
52
+ RELEVANCE_INPUT_VERSION = "claim-standing-relevance-assessment-input/1.0"
53
+ MAX_QUERIES = 3
54
+ MAX_INDEXES = 4
55
+ MAX_HITS_PER_QUERY_INDEX = 20
56
+ MAX_RAW_HITS = 240
57
+ MAX_SELECTED = 40
58
+ EXPLICIT_LOCAL_EXPORT_BOUNDARY = (
59
+ "One local candidate-ledger export to the operator-named output path is authorized."
60
+ )
61
+ # Complete family of artifacts derivable from one consented
62
+ # authorized_output_path ("" = the candidate ledger at the path itself).
63
+ # Consumed by the consent surface disclosure and pinned to each owning
64
+ # module's suffix constant by test.
65
+ ARTIFACT_SUFFIXES = {
66
+ "candidate_ledger": "",
67
+ "query_plan": ".query-plan.json",
68
+ "retrieval_input": ".retrieval-input.json",
69
+ "transmission_ledger": ".transmission-ledger.json",
70
+ "stance_record": ".stance-record.json",
71
+ "evidence_rows": ".evidence-rows.json",
72
+ "rendered_view": ".view.md",
73
+ }
74
+ CAPS = {
75
+ "max_queries": MAX_QUERIES,
76
+ "max_indexes": MAX_INDEXES,
77
+ "max_hits_per_query_index": MAX_HITS_PER_QUERY_INDEX,
78
+ "max_raw_hits": MAX_RAW_HITS,
79
+ "max_selected_work_families": MAX_SELECTED,
80
+ }
81
+ FAILURE_OUTCOMES = {
82
+ "timeout",
83
+ "authentication_failed",
84
+ "rate_limited",
85
+ "malformed_response",
86
+ "service_unavailable",
87
+ "unsupported_query",
88
+ }
89
+ RAW_TERMINAL_STATES = (
90
+ "selected",
91
+ "missing_title_and_stable_id",
92
+ "outside_date_range",
93
+ "outside_language_set",
94
+ "outside_document_type",
95
+ "duplicate_version",
96
+ "not_relevant",
97
+ "not_checked",
98
+ "candidate_cap_exceeded",
99
+ )
100
+ SCHEMA_DIR = Path(__file__).resolve().parents[1] / "shared/contracts/claim_standing"
101
+
102
+ _CITATION_MARKER_RE = re.compile(r"<!--(?:ref|anchor):[^>]*-->")
103
+ _ASCII_WS_RE = re.compile(r"[\t\n\r\f\v ]+")
104
+ _DOI_PREFIX_RE = re.compile(r"(?i)^(?:https?://(?:dx\.)?doi\.org/|doi:\s*)")
105
+ _DOI_BODY_RE = re.compile(r"^10\.[0-9]{4,9}/(.+)$")
106
+
107
+
108
+ class LedgerError(ValueError):
109
+ """A closed #655 Track A invariant failed."""
110
+
111
+
112
+ class DuplicateKeyError(ValueError):
113
+ pass
114
+
115
+
116
+ def _pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
117
+ result: dict[str, Any] = {}
118
+ for key, value in pairs:
119
+ if key in result:
120
+ raise DuplicateKeyError(f"duplicate JSON key {key!r}")
121
+ result[key] = value
122
+ return result
123
+
124
+
125
+ def load_json(path: Path) -> Any:
126
+ try:
127
+ return json.loads(
128
+ path.read_text(encoding="utf-8"),
129
+ object_pairs_hook=_pairs,
130
+ parse_constant=lambda token: (_ for _ in ()).throw(
131
+ ValueError(f"non-finite number {token!r}")
132
+ ),
133
+ )
134
+ except (OSError, UnicodeError, JSONDecodeError, ValueError) as exc:
135
+ raise LedgerError(f"{path}: cannot read strict UTF-8 JSON: {exc}") from exc
136
+
137
+
138
+ _VALIDATOR_CACHE: dict[str, Draft202012Validator] = {}
139
+
140
+
141
+ def validate_schema(value: Any, filename: str, label: str) -> None:
142
+ try:
143
+ validator = _VALIDATOR_CACHE.get(filename)
144
+ if validator is None:
145
+ # Schema files are read-only within a process; meta-validate once.
146
+ schema = load_json(SCHEMA_DIR / filename)
147
+ Draft202012Validator.check_schema(schema)
148
+ validator = Draft202012Validator(schema)
149
+ _VALIDATOR_CACHE[filename] = validator
150
+ validator.validate(value)
151
+ except (SchemaError, ValidationError) as exc:
152
+ location = ".".join(str(part) for part in exc.absolute_path) or "<root>"
153
+ raise LedgerError(f"{label} schema violation at {location}: {exc.message}") from exc
154
+
155
+
156
+ def canonical_bytes(value: Any) -> bytes:
157
+ try:
158
+ return json.dumps(
159
+ value,
160
+ ensure_ascii=False,
161
+ sort_keys=True,
162
+ separators=(",", ":"),
163
+ allow_nan=False,
164
+ ).encode("utf-8")
165
+ except UnicodeError as exc:
166
+ raise LedgerError("canonical JSON contains invalid Unicode text") from exc
167
+
168
+
169
+ def digest(value: Any) -> str:
170
+ return hashlib.sha256(canonical_bytes(value)).hexdigest()
171
+
172
+
173
+ def text_digest(value: str) -> str:
174
+ try:
175
+ encoded = value.encode("utf-8")
176
+ except UnicodeError as exc:
177
+ raise LedgerError("text digest input contains invalid Unicode text") from exc
178
+ return hashlib.sha256(encoded).hexdigest()
179
+
180
+
181
+ def bound_digest(value: dict[str, Any], field: str) -> str:
182
+ payload = copy.deepcopy(value)
183
+ payload.pop(field, None)
184
+ return digest(payload)
185
+
186
+
187
+ def plan_schema_filename(plan: dict[str, Any]) -> str:
188
+ version = plan.get("schema_version")
189
+ filename = PLAN_SCHEMA_FILENAMES.get(version)
190
+ if filename is None:
191
+ raise LedgerError(
192
+ f"schema_version: unknown query-plan version {version!r}"
193
+ )
194
+ return filename
195
+
196
+
197
+ def consentable_plan_projection(plan: dict[str, Any]) -> dict[str, Any]:
198
+ """Return every plan field whose use requires the named consent receipt."""
199
+
200
+ projection = {
201
+ "schema_version": plan["schema_version"],
202
+ "probe_id": plan["probe_id"],
203
+ "claim": copy.deepcopy(plan["claim"]),
204
+ "queries": copy.deepcopy(plan["queries"]),
205
+ "provider_roster": copy.deepcopy(plan["provider_roster"]),
206
+ "allowed_languages": copy.deepcopy(plan["allowed_languages"]),
207
+ "allowed_document_types": copy.deepcopy(plan["allowed_document_types"]),
208
+ "authorized_content_classes": copy.deepcopy(
209
+ plan["authorized_content_classes"]
210
+ ),
211
+ "caps": copy.deepcopy(plan["caps"]),
212
+ "created_at": plan["created_at"],
213
+ }
214
+ if plan.get("schema_version") == PLAN_VERSION_1_1:
215
+ projection["stance_plan"] = copy.deepcopy(plan["stance_plan"])
216
+ return projection
217
+
218
+
219
+ def relevance_assessment_input_projection(
220
+ plan: dict[str, Any], hit: dict[str, Any], assessment: dict[str, Any]
221
+ ) -> dict[str, Any]:
222
+ """Return the exact claim, candidate, and assessor contract seen at relevance."""
223
+
224
+ claim = plan["claim"]
225
+ return {
226
+ "schema_version": RELEVANCE_INPUT_VERSION,
227
+ "claim": {
228
+ "claim_id": claim["claim_id"],
229
+ "claim_text": claim["claim_text"],
230
+ "claim_sha256": claim["claim_sha256"],
231
+ },
232
+ "candidate": {
233
+ "canonical_raw_hit_id": hit["raw_hit_id"],
234
+ "title": hit["title"],
235
+ "abstract_state": hit["abstract_state"],
236
+ "abstract_text": hit["abstract_text"],
237
+ "abstract_sha256": hit["abstract_sha256"],
238
+ },
239
+ "assessor_contract": {
240
+ "assessor_kind": assessment["assessor_kind"],
241
+ "assessor_id": assessment["assessor_id"],
242
+ "prompt_version": assessment["prompt_version"],
243
+ "task": "topical_relevance_only_no_stance_or_credibility",
244
+ },
245
+ }
246
+
247
+
248
+ def render_relevance_prompt(projection: dict[str, Any]) -> str:
249
+ """Render the only prompt bytes bound by a Track A relevance row."""
250
+
251
+ return (
252
+ "Assess topical relevance only. Do not infer stance, credibility, consensus, "
253
+ "or claim truth. Use exactly this canonical assessment input:\n"
254
+ + canonical_bytes(projection).decode("utf-8")
255
+ )
256
+
257
+
258
+ def parse_rfc3339(value: str, path: str) -> datetime:
259
+ try:
260
+ return datetime.fromisoformat(value.removesuffix("Z") + "+00:00")
261
+ except (TypeError, ValueError) as exc:
262
+ raise LedgerError(f"{path}: must be a real RFC 3339 UTC timestamp") from exc
263
+
264
+
265
+ def exact_claim_query(claim_text: str) -> str:
266
+ return _ASCII_WS_RE.sub(" ", _CITATION_MARKER_RE.sub("", claim_text)).strip()
267
+
268
+
269
+ def has_visible_semantic_text(value: Any, *, allow_symbols: bool) -> bool:
270
+ """Return whether NFKC text has a visible semantic base character.
271
+
272
+ Surrogates are rejected anywhere. Unicode control/format/separator-only,
273
+ combining-mark-only, whitespace-only, and punctuation-only strings are not
274
+ semantic text. Narrative fields may opt into a symbol as the visible base;
275
+ provider identifiers and author names require a letter or number.
276
+ """
277
+
278
+ if not isinstance(value, str):
279
+ return False
280
+ try:
281
+ normalized = unicodedata.normalize("NFKC", value)
282
+ categories = [unicodedata.category(char) for char in normalized]
283
+ except UnicodeError as exc:
284
+ raise LedgerError("semantic text contains invalid Unicode") from exc
285
+ if any(category == "Cs" for category in categories):
286
+ return False
287
+ allowed_groups = {"L", "N", "S"} if allow_symbols else {"L", "N"}
288
+ return any(category[0] in allowed_groups for category in categories)
289
+
290
+
291
+ def _expect(condition: bool, path: str, message: str) -> None:
292
+ if not condition:
293
+ raise LedgerError(f"{path}: {message}")
294
+
295
+
296
+ def _unique(rows: list[dict[str, Any]], field: str, path: str) -> dict[str, dict[str, Any]]:
297
+ result: dict[str, dict[str, Any]] = {}
298
+ for index, row in enumerate(rows):
299
+ value = row.get(field)
300
+ _expect(isinstance(value, str) and value, f"{path}[{index}].{field}", "must be non-empty")
301
+ _expect(value not in result, f"{path}[{index}].{field}", f"duplicate {value!r}")
302
+ result[value] = row
303
+ return result
304
+
305
+
306
+ def _validate_stance_authorization(
307
+ plan: dict[str, Any], consent: dict[str, Any]
308
+ ) -> None:
309
+ stance_plan = plan.get("stance_plan")
310
+ if consent["decision"] == "retrieval_plus_stance":
311
+ _expect(
312
+ consent["stance_classification_authorized"] is True,
313
+ "consent.stance_classification_authorized",
314
+ "retrieval_plus_stance requires an explicit true",
315
+ )
316
+ _expect(
317
+ isinstance(stance_plan, dict),
318
+ "stance_plan",
319
+ "retrieval_plus_stance requires a stance_plan",
320
+ )
321
+ _expect(
322
+ consent.get("stance_plan_sha256") == digest(stance_plan),
323
+ "consent.stance_plan_sha256",
324
+ "does not bind the stance_plan",
325
+ )
326
+ _expect(
327
+ plan["authorized_content_classes"] == STANCE_CONTENT_CLASSES,
328
+ "authorized_content_classes",
329
+ "retrieval_plus_stance authorizes exactly the stance content classes",
330
+ )
331
+ for field in ("provider_identity", "model_identity"):
332
+ _expect(
333
+ has_visible_semantic_text(stance_plan[field], allow_symbols=True),
334
+ f"stance_plan.{field}",
335
+ "must contain visible semantic text after NFKC normalization",
336
+ )
337
+ if stance_plan["retention_state"] == "known":
338
+ _expect(
339
+ has_visible_semantic_text(
340
+ stance_plan["retention_reference"], allow_symbols=True
341
+ ),
342
+ "stance_plan.retention_reference",
343
+ "known retention requires a semantically visible reference",
344
+ )
345
+ else:
346
+ _expect(
347
+ stance_plan["retention_reference"] is None,
348
+ "stance_plan.retention_reference",
349
+ "unknown retention requires a null reference",
350
+ )
351
+ else:
352
+ _expect(
353
+ consent["stance_classification_authorized"] is False,
354
+ "consent.stance_classification_authorized",
355
+ "retrieval_only must be false",
356
+ )
357
+ _expect(stance_plan is None, "stance_plan", "retrieval_only requires null")
358
+ _expect(
359
+ consent.get("stance_plan_sha256") is None,
360
+ "consent.stance_plan_sha256",
361
+ "retrieval_only requires null",
362
+ )
363
+ _expect(
364
+ plan["authorized_content_classes"] == RETRIEVAL_ONLY_CONTENT_CLASSES,
365
+ "authorized_content_classes",
366
+ "retrieval_only authorizes exactly the accepted search queries",
367
+ )
368
+
369
+
370
+ def validate_plan(plan: dict[str, Any]) -> None:
371
+ _expect(
372
+ plan.get("schema_version") in PLAN_SCHEMA_FILENAMES,
373
+ "schema_version",
374
+ f"must be one of {sorted(PLAN_SCHEMA_FILENAMES)}",
375
+ )
376
+ _expect(plan.get("caps") == CAPS, "caps", "must equal the frozen v1 ceilings")
377
+ claim = plan["claim"]
378
+ _expect(
379
+ has_visible_semantic_text(claim["claim_text"], allow_symbols=True),
380
+ "claim.claim_text",
381
+ "must contain visible semantic text after NFKC normalization",
382
+ )
383
+ _expect(text_digest(claim["claim_text"]) == claim["claim_sha256"], "claim.claim_sha256", "does not bind exact claim text")
384
+ checkpoint = claim["checkpoint"]
385
+ tier = claim["registry_selection_tier"]
386
+ _expect(
387
+ CHECKPOINT_REQUIRED_TIER.get(checkpoint) == tier,
388
+ "claim.registry_selection_tier",
389
+ "must be HIGH-IMPACT at Stage 2.5 or ALL with explicit high-impact basis at Stage 4.5",
390
+ )
391
+ _expect(bool(claim["high_impact_basis"]), "claim.high_impact_basis", "must be explicit")
392
+ queries = plan["queries"]
393
+ roster = plan["provider_roster"]
394
+ _expect(1 <= len(queries) <= MAX_QUERIES, "queries", "must contain 1..3 rows")
395
+ _expect(1 <= len(roster) <= MAX_INDEXES, "provider_roster", "must contain 1..4 rows")
396
+ _expect(
397
+ plan.get("schema_version") == PLAN_VERSION_1_1
398
+ or plan["authorized_content_classes"] == RETRIEVAL_ONLY_CONTENT_CLASSES,
399
+ "authorized_content_classes",
400
+ "Track A retrieval authorizes only accepted search-query text",
401
+ )
402
+ query_map = _unique(queries, "query_id", "queries")
403
+ provider_map = _unique(roster, "index_id", "provider_roster")
404
+ for index_id, provider in provider_map.items():
405
+ for disclosure_field in (
406
+ "product_identity",
407
+ "query_capability",
408
+ "pagination_behavior",
409
+ ):
410
+ disclosure = provider[disclosure_field]
411
+ _expect(
412
+ has_visible_semantic_text(disclosure, allow_symbols=True),
413
+ f"provider_roster.{index_id}.{disclosure_field}",
414
+ "must contain visible semantic provider disclosure text after NFKC normalization",
415
+ )
416
+ retention_state = provider["retention_state"]
417
+ retention_reference = provider["retention_reference"]
418
+ _expect(
419
+ (
420
+ retention_state == "known"
421
+ and has_visible_semantic_text(
422
+ retention_reference, allow_symbols=True
423
+ )
424
+ )
425
+ or (retention_state == "unknown" and retention_reference is None),
426
+ f"provider_roster.{index_id}.retention_reference",
427
+ "known retention requires visible semantic reference text after NFKC normalization; unknown requires null",
428
+ )
429
+ for query_id, query in query_map.items():
430
+ for query_text_field in ("original_query_text", "accepted_query_text"):
431
+ _expect(
432
+ has_visible_semantic_text(
433
+ query[query_text_field], allow_symbols=True
434
+ ),
435
+ f"queries.{query_id}.{query_text_field}",
436
+ "must contain visible semantic text after NFKC normalization",
437
+ )
438
+ _expect(query["source_claim_sha256"] == claim["claim_sha256"], f"queries.{query_id}.source_claim_sha256", "claim binding drifted")
439
+ _expect(text_digest(query["accepted_query_text"]) == query["query_sha256"], f"queries.{query_id}.query_sha256", "does not bind accepted query text")
440
+ if query["construction"] == "exact_claim":
441
+ expected = exact_claim_query(claim["claim_text"])
442
+ _expect(query["accepted_query_text"] == expected, f"queries.{query_id}.accepted_query_text", "exact_claim may only strip ARS markers and collapse ASCII whitespace")
443
+ _expect(set(query["index_targets"]) <= set(provider_map), f"queries.{query_id}.index_targets", "names an index outside provider roster")
444
+ dates = query["date_filter"]
445
+ if dates["from_year"] is not None and dates["through_year"] is not None:
446
+ _expect(dates["from_year"] <= dates["through_year"], f"queries.{query_id}.date_filter", "from_year exceeds through_year")
447
+ consent = plan["consent"]
448
+ if plan.get("schema_version") == PLAN_VERSION_1_1:
449
+ _validate_stance_authorization(plan, consent)
450
+ else:
451
+ _expect(consent["decision"] == "retrieval_only", "consent.decision", "Track A accepts retrieval_only")
452
+ _expect(consent["stance_classification_authorized"] is False, "consent.stance_classification_authorized", "must be false")
453
+ for disclosure_field in ("deletion_boundary", "export_boundary"):
454
+ _expect(
455
+ has_visible_semantic_text(
456
+ consent[disclosure_field], allow_symbols=True
457
+ ),
458
+ f"consent.{disclosure_field}",
459
+ "must contain visible semantic disclosure text after NFKC normalization",
460
+ )
461
+ if consent["local_persistence"] == "explicit_local_export":
462
+ _expect(
463
+ consent["export_boundary"] == EXPLICIT_LOCAL_EXPORT_BOUNDARY,
464
+ "consent.export_boundary",
465
+ "explicit_local_export requires the exact closed local-export authorization boundary",
466
+ )
467
+ _expect(
468
+ has_visible_semantic_text(
469
+ consent.get("authorized_output_path"), allow_symbols=True
470
+ ),
471
+ "consent.authorized_output_path",
472
+ "explicit_local_export requires one hash-bound operator-named output path",
473
+ )
474
+ _expect(
475
+ Path(consent["authorized_output_path"]).is_absolute(),
476
+ "consent.authorized_output_path",
477
+ "must be an absolute path so its target cannot vary by working directory",
478
+ )
479
+ _expect(
480
+ not consent["authorized_output_path"].endswith(("/", "\\")),
481
+ "consent.authorized_output_path",
482
+ "must not end with a path separator: derived artifact names would become hidden dotfiles",
483
+ )
484
+ else:
485
+ _expect(
486
+ consent.get("authorized_output_path") is None,
487
+ "consent.authorized_output_path",
488
+ "session_only must not authorize an output path",
489
+ )
490
+ bindings = {
491
+ "claim_sha256": claim["claim_sha256"],
492
+ "provider_roster_sha256": digest(roster),
493
+ "caps_sha256": digest(plan["caps"]),
494
+ "queries_sha256": digest(queries),
495
+ }
496
+ for field, expected in bindings.items():
497
+ _expect(consent[field] == expected, f"consent.{field}", "consent binding drifted")
498
+ _expect(
499
+ consent["consentable_plan_sha256"]
500
+ == digest(consentable_plan_projection(plan)),
501
+ "consent.consentable_plan_sha256",
502
+ "does not bind the complete consentable plan projection",
503
+ )
504
+ _expect(consent["receipt_sha256"] == bound_digest(consent, "receipt_sha256"), "consent.receipt_sha256", "does not bind receipt")
505
+ _expect(plan["plan_sha256"] == bound_digest(plan, "plan_sha256"), "plan_sha256", "does not bind plan")
506
+ _expect(
507
+ parse_rfc3339(plan["created_at"], "created_at")
508
+ <= parse_rfc3339(consent["accepted_at"], "consent.accepted_at"),
509
+ "consent.accepted_at",
510
+ "must not precede plan creation",
511
+ )
512
+
513
+
514
+ def validate_input(plan: dict[str, Any], retained: dict[str, Any]) -> None:
515
+ _expect(retained.get("schema_version") == INPUT_VERSION, "retrieval_input.schema_version", f"must equal {INPUT_VERSION}")
516
+ _expect(retained["query_plan_sha256"] == plan["plan_sha256"], "retrieval_input.query_plan_sha256", "plan binding drifted")
517
+ _expect(len(retained["raw_hits"]) <= MAX_RAW_HITS, "raw_hits", "exceeds 240")
518
+ query_map = {row["query_id"]: row for row in plan["queries"]}
519
+ provider_ids = {row["index_id"] for row in plan["provider_roster"]}
520
+ attempts = _unique(retained["attempts"], "attempt_id", "attempts")
521
+ hits = _unique(retained["raw_hits"], "raw_hit_id", "raw_hits")
522
+ retry_receipts = _unique(
523
+ retained["retry_authorizations"],
524
+ "retry_authorization_receipt_id",
525
+ "retry_authorizations",
526
+ )
527
+ consent_id = plan["consent"]["consent_receipt_id"]
528
+ plan_completed = parse_rfc3339(retained["completed_at"], "completed_at")
529
+ consent_accepted = parse_rfc3339(
530
+ plan["consent"]["accepted_at"], "consent.accepted_at"
531
+ )
532
+ root_attempts: Counter[tuple[str, str]] = Counter()
533
+ retry_authorizations: set[str] = set()
534
+ hits_by_attempt: Counter[str] = Counter()
535
+ ranks_by_attempt: dict[str, set[int]] = {}
536
+ attempt_times: dict[str, tuple[datetime, datetime]] = {}
537
+ hit_returned_times: dict[str, datetime] = {}
538
+ for attempt_id, attempt in attempts.items():
539
+ _expect(attempt["query_id"] in query_map, f"attempts.{attempt_id}.query_id", "unknown query")
540
+ _expect(attempt["index_id"] in query_map[attempt["query_id"]]["index_targets"], f"attempts.{attempt_id}.index_id", "not targeted by query")
541
+ _expect(attempt["index_id"] in provider_ids, f"attempts.{attempt_id}.index_id", "outside roster")
542
+ _expect(attempt["consent_receipt_id"] == consent_id, f"attempts.{attempt_id}.consent_receipt_id", "consent drifted")
543
+ started = parse_rfc3339(
544
+ attempt["started_at"], f"attempts.{attempt_id}.started_at"
545
+ )
546
+ completed = parse_rfc3339(
547
+ attempt["completed_at"], f"attempts.{attempt_id}.completed_at"
548
+ )
549
+ _expect(
550
+ consent_accepted <= started <= completed <= plan_completed,
551
+ f"attempts.{attempt_id}",
552
+ "must satisfy consent accepted <= start <= completion <= input completion",
553
+ )
554
+ attempt_times[attempt_id] = (started, completed)
555
+ retry = attempt["retry_of_attempt_id"]
556
+ authorization = attempt["retry_authorization_receipt_id"]
557
+ if retry is None:
558
+ pair = (attempt["query_id"], attempt["index_id"])
559
+ root_attempts[pair] += 1
560
+ _expect(
561
+ root_attempts[pair] == 1,
562
+ f"attempts.{attempt_id}",
563
+ "a query/index pair may have only one initial attempt; later attempts must name a retry",
564
+ )
565
+ _expect((retry is None) == (authorization is None), f"attempts.{attempt_id}", "retry and its fresh authorization must appear together")
566
+ if retry is not None:
567
+ _expect(retry in attempts and retry != attempt_id, f"attempts.{attempt_id}.retry_of_attempt_id", "must name another retained attempt")
568
+ previous = attempts[retry]
569
+ _expect(previous["outcome"] != "success", f"attempts.{attempt_id}.retry_of_attempt_id", "must name a failed attempt")
570
+ _expect(
571
+ (attempt["query_id"], attempt["index_id"])
572
+ == (previous["query_id"], previous["index_id"]),
573
+ f"attempts.{attempt_id}.retry_of_attempt_id",
574
+ "retry must preserve the failed query/index pair",
575
+ )
576
+ _expect(
577
+ authorization != consent_id,
578
+ f"attempts.{attempt_id}.retry_authorization_receipt_id",
579
+ "initial retrieval consent is not a fresh retry authorization",
580
+ )
581
+ _expect(
582
+ authorization not in retry_authorizations,
583
+ f"attempts.{attempt_id}.retry_authorization_receipt_id",
584
+ "fresh retry authorization identifiers cannot be reused",
585
+ )
586
+ retry_authorizations.add(authorization)
587
+ if attempt["outcome"] != "success":
588
+ _expect(attempt["outcome"] in FAILURE_OUTCOMES, f"attempts.{attempt_id}.outcome", "unknown failure")
589
+ _expect(attempt["returned_count"] == attempt["retained_hit_count"] == attempt["truncated_count"] == 0, f"attempts.{attempt_id}", "failed attempts carry zero hits and no fabricated source row")
590
+ expected_root_pairs = {
591
+ (query["query_id"], index_id)
592
+ for query in plan["queries"]
593
+ for index_id in query["index_targets"]
594
+ }
595
+ _expect(
596
+ set(root_attempts) == expected_root_pairs,
597
+ "attempts",
598
+ "must contain exactly one initial attempt for every planned query/index target pair",
599
+ )
600
+ _expect(
601
+ retry_authorizations == set(retry_receipts),
602
+ "retry_authorizations",
603
+ "must contain exactly one retained receipt for every retry and no unused receipts",
604
+ )
605
+ for receipt_id, receipt in retry_receipts.items():
606
+ _expect(
607
+ receipt["query_plan_sha256"] == plan["plan_sha256"],
608
+ f"retry_authorizations.{receipt_id}.query_plan_sha256",
609
+ "plan binding drifted",
610
+ )
611
+ _expect(
612
+ receipt["probe_id"] == plan["probe_id"],
613
+ f"retry_authorizations.{receipt_id}.probe_id",
614
+ "probe binding drifted",
615
+ )
616
+ _expect(
617
+ receipt["consent_receipt_id"] == consent_id,
618
+ f"retry_authorizations.{receipt_id}.consent_receipt_id",
619
+ "initial consent binding drifted",
620
+ )
621
+ failed_id = receipt["failed_attempt_id"]
622
+ _expect(
623
+ failed_id in attempts and attempts[failed_id]["outcome"] != "success",
624
+ f"retry_authorizations.{receipt_id}.failed_attempt_id",
625
+ "must bind a retained failed attempt",
626
+ )
627
+ failed = attempts[failed_id]
628
+ _expect(
629
+ (receipt["query_id"], receipt["index_id"])
630
+ == (failed["query_id"], failed["index_id"]),
631
+ f"retry_authorizations.{receipt_id}",
632
+ "query/index pair differs from failed attempt",
633
+ )
634
+ _expect(
635
+ receipt["receipt_sha256"] == bound_digest(receipt, "receipt_sha256"),
636
+ f"retry_authorizations.{receipt_id}.receipt_sha256",
637
+ "does not bind retry authorization receipt",
638
+ )
639
+ for attempt_id in attempts:
640
+ seen = {attempt_id}
641
+ cursor = attempts[attempt_id]["retry_of_attempt_id"]
642
+ while cursor is not None:
643
+ _expect(cursor not in seen, f"attempts.{attempt_id}.retry_of_attempt_id", "retry graph contains a cycle")
644
+ seen.add(cursor)
645
+ cursor = attempts[cursor]["retry_of_attempt_id"]
646
+ for hit_id, hit in hits.items():
647
+ provider_record_id = hit["provider_record_id"]
648
+ _expect(
649
+ provider_record_id is None
650
+ or has_visible_semantic_text(
651
+ provider_record_id, allow_symbols=False
652
+ ),
653
+ f"raw_hits.{hit_id}.provider_record_id",
654
+ "must be null or contain a visible letter or number after NFKC normalization",
655
+ )
656
+ doi = hit["doi"]
657
+ _expect(
658
+ doi is None or isinstance(doi, str),
659
+ f"raw_hits.{hit_id}.doi",
660
+ "must be null or a retained provider DOI string",
661
+ )
662
+ title = hit["title"]
663
+ _expect(
664
+ title is None
665
+ or has_visible_semantic_text(title, allow_symbols=True),
666
+ f"raw_hits.{hit_id}.title",
667
+ "must be null or contain visible semantic text after NFKC normalization",
668
+ )
669
+ for author_index, author in enumerate(hit["authors"]):
670
+ for author_field in ("family", "given"):
671
+ author_value = author[author_field]
672
+ _expect(
673
+ author_value is None
674
+ or has_visible_semantic_text(
675
+ author_value, allow_symbols=False
676
+ ),
677
+ f"raw_hits.{hit_id}.authors[{author_index}].{author_field}",
678
+ "must be null or contain a visible letter or number after NFKC normalization",
679
+ )
680
+ _expect(hit["probe_id"] == plan["probe_id"], f"raw_hits.{hit_id}.probe_id", "probe drifted")
681
+ _expect(hit["attempt_id"] in attempts, f"raw_hits.{hit_id}.attempt_id", "unknown attempt")
682
+ attempt = attempts[hit["attempt_id"]]
683
+ _expect(attempt["outcome"] == "success", f"raw_hits.{hit_id}.attempt_id", "failed attempts cannot own source rows")
684
+ _expect((hit["query_id"], hit["index_id"]) == (attempt["query_id"], attempt["index_id"]), f"raw_hits.{hit_id}", "query/index differ from attempt")
685
+ ranks = ranks_by_attempt.setdefault(hit["attempt_id"], set())
686
+ _expect(hit["provider_rank"] not in ranks, f"raw_hits.{hit_id}.provider_rank", "duplicate within attempt")
687
+ ranks.add(hit["provider_rank"])
688
+ hits_by_attempt[hit["attempt_id"]] += 1
689
+ if hit["abstract_state"] == "available":
690
+ _expect(
691
+ has_visible_semantic_text(
692
+ hit["abstract_text"], allow_symbols=True
693
+ ),
694
+ f"raw_hits.{hit_id}.abstract_text",
695
+ "available abstract must contain visible semantic text after NFKC normalization",
696
+ )
697
+ _expect(hit["abstract_sha256"] == text_digest(hit["abstract_text"]), f"raw_hits.{hit_id}.abstract_sha256", "does not bind abstract")
698
+ else:
699
+ _expect(hit["abstract_text"] is None and hit["abstract_sha256"] is None, f"raw_hits.{hit_id}.abstract", "missing/not-returned abstract must be null")
700
+ relation = hit["explicit_version_of_raw_hit_id"]
701
+ if relation is not None:
702
+ _expect(relation in hits and relation != hit_id, f"raw_hits.{hit_id}.explicit_version_of_raw_hit_id", "must name another hit")
703
+ returned = parse_rfc3339(
704
+ hit["returned_at"], f"raw_hits.{hit_id}.returned_at"
705
+ )
706
+ hit_returned_times[hit_id] = returned
707
+ started, completed = attempt_times[hit["attempt_id"]]
708
+ _expect(
709
+ started <= returned <= completed,
710
+ f"raw_hits.{hit_id}.returned_at",
711
+ "must fall within its owning attempt",
712
+ )
713
+ for attempt_id, attempt in attempts.items():
714
+ _expect(hits_by_attempt[attempt_id] == attempt["retained_hit_count"], f"attempts.{attempt_id}.retained_hit_count", "does not equal retained source rows")
715
+ _expect(attempt["retained_hit_count"] <= MAX_HITS_PER_QUERY_INDEX, f"attempts.{attempt_id}.retained_hit_count", "exceeds 20")
716
+ _expect(attempt["returned_count"] == attempt["retained_hit_count"] + attempt["truncated_count"], f"attempts.{attempt_id}", "returned count must partition retained and truncated")
717
+ if attempt["provider_reported_count"] is not None:
718
+ _expect(attempt["provider_reported_count"] >= attempt["returned_count"], f"attempts.{attempt_id}.provider_reported_count", "cannot be below returned count")
719
+ pair_counts = Counter((hit["query_id"], hit["index_id"]) for hit in hits.values())
720
+ for (query_id, index_id), count in pair_counts.items():
721
+ _expect(
722
+ count <= MAX_HITS_PER_QUERY_INDEX,
723
+ f"raw_hits.{query_id}.{index_id}",
724
+ "exceeds the aggregate 20-hit query/index ceiling across attempts",
725
+ )
726
+ for index, relation in enumerate(retained["researcher_confirmed_version_relations"]):
727
+ left, right = relation["left_raw_hit_id"], relation["right_raw_hit_id"]
728
+ _expect(left in hits and right in hits and left != right, f"researcher_confirmed_version_relations[{index}]", "must name two distinct retained hits")
729
+ for attempt_id, attempt in attempts.items():
730
+ receipt_id = attempt["retry_authorization_receipt_id"]
731
+ if receipt_id is None:
732
+ continue
733
+ receipt = retry_receipts[receipt_id]
734
+ _expect(
735
+ receipt["failed_attempt_id"] == attempt["retry_of_attempt_id"],
736
+ f"attempts.{attempt_id}.retry_authorization_receipt_id",
737
+ "receipt must bind this retry's failed attempt",
738
+ )
739
+ _expect(
740
+ (receipt["query_id"], receipt["index_id"])
741
+ == (attempt["query_id"], attempt["index_id"]),
742
+ f"attempts.{attempt_id}.retry_authorization_receipt_id",
743
+ "receipt must bind this retry's query/index pair",
744
+ )
745
+ authorized = parse_rfc3339(
746
+ receipt["accepted_at"],
747
+ f"retry_authorizations.{receipt_id}.accepted_at",
748
+ )
749
+ failed_completed = attempt_times[attempt["retry_of_attempt_id"]][1]
750
+ retry_started = attempt_times[attempt_id][0]
751
+ _expect(
752
+ failed_completed < authorized < retry_started,
753
+ f"retry_authorizations.{receipt_id}.accepted_at",
754
+ "must be later than the failed attempt and earlier than the retry",
755
+ )
756
+ for index, assessment in enumerate(retained["relevance_assessments"]):
757
+ assessed_raw_hit_id = assessment["canonical_raw_hit_id"]
758
+ _expect(
759
+ assessed_raw_hit_id in hits,
760
+ f"relevance_assessments[{index}].canonical_raw_hit_id",
761
+ "must name a retained raw hit",
762
+ )
763
+ assessed = parse_rfc3339(
764
+ assessment["assessed_at"], f"relevance_assessments[{index}].assessed_at"
765
+ )
766
+ _expect(
767
+ hit_returned_times[assessed_raw_hit_id] <= assessed <= plan_completed,
768
+ f"relevance_assessments[{index}].assessed_at",
769
+ "must not precede its candidate return or exceed input completion",
770
+ )
771
+ raw_output = assessment["raw_output"]
772
+ raw_output_sha256 = assessment["raw_output_sha256"]
773
+ assessment_projection = relevance_assessment_input_projection(
774
+ plan, hits[assessed_raw_hit_id], assessment
775
+ )
776
+ _expect(
777
+ assessment["assessment_input_sha256"] == digest(assessment_projection),
778
+ f"relevance_assessments[{index}].assessment_input_sha256",
779
+ "does not bind exact claim, candidate, and assessor contract",
780
+ )
781
+ expected_prompt = render_relevance_prompt(assessment_projection)
782
+ _expect(
783
+ assessment["prompt_utf8"] == expected_prompt,
784
+ f"relevance_assessments[{index}].prompt_utf8",
785
+ "does not equal the canonical relevance prompt",
786
+ )
787
+ _expect(
788
+ assessment["prompt_sha256"] == text_digest(expected_prompt),
789
+ f"relevance_assessments[{index}].prompt_sha256",
790
+ "does not bind exact prompt UTF-8 bytes",
791
+ )
792
+ if raw_output is None:
793
+ _expect(
794
+ raw_output_sha256 is None,
795
+ f"relevance_assessments[{index}].raw_output_sha256",
796
+ "must be null when raw_output is null",
797
+ )
798
+ else:
799
+ _expect(
800
+ raw_output_sha256 == text_digest(raw_output),
801
+ f"relevance_assessments[{index}].raw_output_sha256",
802
+ "does not bind retained raw output",
803
+ )
804
+ if assessment["outcome"] == "success":
805
+ _expect(
806
+ assessment["state"] != "not_checked",
807
+ f"relevance_assessments[{index}].state",
808
+ "successful assessment must have a checked relevance state",
809
+ )
810
+ _expect(
811
+ has_visible_semantic_text(raw_output, allow_symbols=True),
812
+ f"relevance_assessments[{index}].raw_output",
813
+ "successful assessment must retain visible semantic raw output after NFKC normalization",
814
+ )
815
+ _expect(
816
+ has_visible_semantic_text(
817
+ assessment["rationale"], allow_symbols=True
818
+ ),
819
+ f"relevance_assessments[{index}].rationale",
820
+ "successful assessment must retain visible semantic rationale text after NFKC normalization",
821
+ )
822
+ _expect(
823
+ assessment["failure_code"] is None
824
+ and assessment["failure_detail"] is None,
825
+ f"relevance_assessments[{index}].failure",
826
+ "successful assessment cannot carry failure fields",
827
+ )
828
+ else:
829
+ _expect(
830
+ assessment["state"] == "not_checked",
831
+ f"relevance_assessments[{index}].state",
832
+ "failed assessment must finalize as not_checked",
833
+ )
834
+ _expect(
835
+ assessment["reason_code"] is None
836
+ and assessment["rationale"] is None,
837
+ f"relevance_assessments[{index}]",
838
+ "failed assessment cannot invent relevance rationale",
839
+ )
840
+ _expect(
841
+ assessment["failure_code"] is not None
842
+ and has_visible_semantic_text(
843
+ assessment["failure_detail"], allow_symbols=True
844
+ ),
845
+ f"relevance_assessments[{index}].failure",
846
+ "failed assessment must retain a failure code and visible semantic detail after NFKC normalization",
847
+ )
848
+ _expect(retained["retrieval_input_sha256"] == bound_digest(retained, "retrieval_input_sha256"), "retrieval_input_sha256", "does not bind input")
849
+
850
+
851
+ def normalize_doi(value: str | None) -> str | None:
852
+ if value is None:
853
+ return None
854
+ try:
855
+ normalized = unicodedata.normalize("NFKC", value).strip()
856
+ normalized = _DOI_PREFIX_RE.sub("", normalized).strip().casefold()
857
+ match = _DOI_BODY_RE.fullmatch(normalized)
858
+ if match is None:
859
+ return None
860
+ suffix = match.group(1)
861
+ categories = [unicodedata.category(char) for char in suffix]
862
+ except (TypeError, UnicodeError):
863
+ return None
864
+ if any(
865
+ category[0] in {"C", "Z"} or char.isspace()
866
+ for char, category in zip(suffix, categories)
867
+ ):
868
+ return None
869
+ if not any(category[0] in {"L", "N"} for category in categories):
870
+ return None
871
+ return normalized
872
+
873
+
874
+ def normalize_provider_record_id(value: str | None) -> str | None:
875
+ if value is None or not has_visible_semantic_text(value, allow_symbols=False):
876
+ return None
877
+ return unicodedata.normalize("NFKC", value).strip() or None
878
+
879
+
880
+ def normalize_author_family(value: str | None) -> str | None:
881
+ if value is None or not has_visible_semantic_text(value, allow_symbols=False):
882
+ return None
883
+ normalized = unicodedata.normalize("NFKC", value).casefold().strip()
884
+ return normalized or None
885
+
886
+
887
+ def normalize_title(value: str | None) -> str | None:
888
+ if value is None or not has_visible_semantic_text(value, allow_symbols=True):
889
+ return None
890
+ text = unicodedata.normalize("NFKC", value).casefold()
891
+ text = "".join(
892
+ " " if unicodedata.category(char)[0] in {"C", "P", "Z"} else char
893
+ for char in text
894
+ )
895
+ return _ASCII_WS_RE.sub(" ", text).strip() or None
896
+
897
+
898
+ def _canonical_key(hit: dict[str, Any]) -> tuple[Any, ...]:
899
+ status = {"published": 0, "preprint": 1, "other": 2, "unknown": 3}[hit["publication_status"]]
900
+ return (
901
+ status,
902
+ 0 if normalize_doi(hit["doi"]) else 1,
903
+ 0 if hit["abstract_state"] == "available" else 1,
904
+ hit["provider_rank"],
905
+ hit["index_id"],
906
+ normalize_provider_record_id(hit["provider_record_id"]) or "",
907
+ hit["raw_hit_id"],
908
+ )
909
+
910
+
911
+ def _union_find(ids: list[str]) -> tuple[dict[str, str], Any]:
912
+ parent = {value: value for value in ids}
913
+
914
+ def find(value: str) -> str:
915
+ while parent[value] != value:
916
+ parent[value] = parent[parent[value]]
917
+ value = parent[value]
918
+ return value
919
+
920
+ def union(left: str, right: str) -> None:
921
+ a, b = find(left), find(right)
922
+ if a != b:
923
+ parent[max(a, b)] = min(a, b)
924
+
925
+ return parent, (find, union)
926
+
927
+
928
+ def build_ledger(plan: dict[str, Any], retained: dict[str, Any]) -> dict[str, Any]:
929
+ validate_schema(plan, plan_schema_filename(plan), "query plan")
930
+ validate_schema(retained, "retrieval_input.schema.json", "retrieval input")
931
+ validate_plan(plan)
932
+ validate_input(plan, retained)
933
+ query_order = {row["query_id"]: index for index, row in enumerate(plan["queries"])}
934
+ query_map = {row["query_id"]: row for row in plan["queries"]}
935
+ final_rows = [copy.deepcopy(row) for row in retained["raw_hits"]]
936
+ by_id = {row["raw_hit_id"]: row for row in final_rows}
937
+ eligible: list[dict[str, Any]] = []
938
+ for row in final_rows:
939
+ row.update(terminal_state=None, work_family_id=None, retained_raw_hit_id=None)
940
+ stable = bool(
941
+ normalize_provider_record_id(row["provider_record_id"])
942
+ or normalize_doi(row["doi"])
943
+ )
944
+ if normalize_title(row["title"]) is None and not stable:
945
+ row["terminal_state"] = "missing_title_and_stable_id"
946
+ continue
947
+ dates = query_map[row["query_id"]]["date_filter"]
948
+ if row["year"] is not None and (
949
+ (dates["from_year"] is not None and row["year"] < dates["from_year"])
950
+ or (dates["through_year"] is not None and row["year"] > dates["through_year"])
951
+ ):
952
+ row["terminal_state"] = "outside_date_range"
953
+ continue
954
+ if row["language"] not in set(plan["allowed_languages"]):
955
+ row["terminal_state"] = "outside_language_set"
956
+ continue
957
+ if row["document_type"] not in set(plan["allowed_document_types"]):
958
+ row["terminal_state"] = "outside_document_type"
959
+ continue
960
+ eligible.append(row)
961
+ eligible.sort(key=lambda row: row["raw_hit_id"])
962
+ ids = [row["raw_hit_id"] for row in eligible]
963
+ _, (find, union) = _union_find(ids)
964
+ # Strong relations are applied first. They may intentionally preserve an
965
+ # explicit provider relation even when provider DOI metadata disagrees.
966
+ for left, right in combinations(eligible, 2):
967
+ left_id, right_id = left["raw_hit_id"], right["raw_hit_id"]
968
+ left_doi, right_doi = normalize_doi(left["doi"]), normalize_doi(right["doi"])
969
+ if left_doi and left_doi == right_doi:
970
+ union(left_id, right_id)
971
+ continue
972
+ explicitly_related = (
973
+ left["explicit_version_of_raw_hit_id"] == right_id
974
+ or right["explicit_version_of_raw_hit_id"] == left_id
975
+ )
976
+ if explicitly_related:
977
+ union(left_id, right_id)
978
+
979
+ def weak_key(row: dict[str, Any]) -> tuple[str, int, str] | None:
980
+ title = normalize_title(row["title"])
981
+ first_family = (
982
+ normalize_author_family(row["authors"][0]["family"])
983
+ if row["authors"]
984
+ else None
985
+ )
986
+ if title is None or row["year"] is None or first_family is None:
987
+ return None
988
+ return (title, row["year"], first_family)
989
+
990
+ weak_groups: dict[tuple[str, int, str], list[str]] = {}
991
+ for row in eligible:
992
+ key = weak_key(row)
993
+ if key is not None:
994
+ weak_groups.setdefault(key, []).append(row["raw_hit_id"])
995
+ for member_ids in weak_groups.values():
996
+ roots = sorted({find(member_id) for member_id in member_ids})
997
+ root_dois: dict[str, set[str]] = {root: set() for root in roots}
998
+ for row in eligible:
999
+ root = find(row["raw_hit_id"])
1000
+ if root not in root_dois:
1001
+ continue
1002
+ doi = normalize_doi(row["doi"])
1003
+ if doi:
1004
+ root_dois[root].add(doi)
1005
+ distinct_dois = set().union(*(root_dois[root] for root in roots))
1006
+ if len(distinct_dois) <= 1:
1007
+ for root in roots[1:]:
1008
+ union(roots[0], root)
1009
+ else:
1010
+ # A metadata-only record that weakly matches two distinct DOI
1011
+ # components is ambiguous. Keep it separate rather than allowing
1012
+ # transitive union to bridge the DOI families.
1013
+ no_doi_roots = [root for root in roots if not root_dois[root]]
1014
+ for root in no_doi_roots[1:]:
1015
+ union(no_doi_roots[0], root)
1016
+ for relation in retained["researcher_confirmed_version_relations"]:
1017
+ left, right = relation["left_raw_hit_id"], relation["right_raw_hit_id"]
1018
+ if left in ids and right in ids:
1019
+ left_title = normalize_title(by_id[left]["title"])
1020
+ right_title = normalize_title(by_id[right]["title"])
1021
+ _expect(
1022
+ left_title is not None and left_title == right_title,
1023
+ "researcher_confirmed_version_relations",
1024
+ "confirmation requires equal non-empty normalized titles",
1025
+ )
1026
+ union(left, right)
1027
+ grouped: dict[str, list[dict[str, Any]]] = {}
1028
+ for row in eligible:
1029
+ grouped.setdefault(find(row["raw_hit_id"]), []).append(row)
1030
+ assessments = _unique(retained["relevance_assessments"], "canonical_raw_hit_id", "relevance_assessments")
1031
+ families: list[dict[str, Any]] = []
1032
+ for members in grouped.values():
1033
+ members.sort(key=_canonical_key)
1034
+ canonical = members[0]
1035
+ assessment = assessments.get(canonical["raw_hit_id"])
1036
+ _expect(assessment is not None, f"relevance_assessments.{canonical['raw_hit_id']}", "one explicit assessment is required per canonical family")
1037
+ if assessment["state"] == "not_relevant":
1038
+ _expect(assessment["reason_code"] is not None, f"relevance_assessments.{canonical['raw_hit_id']}.reason_code", "not_relevant requires a mismatch reason")
1039
+ else:
1040
+ _expect(assessment["reason_code"] is None, f"relevance_assessments.{canonical['raw_hit_id']}.reason_code", "relevant/ambiguous reason_code must be null")
1041
+ member_ids = sorted(row["raw_hit_id"] for row in members)
1042
+ family_id = "CSF-" + digest(member_ids)[:16]
1043
+ for row in members:
1044
+ row["work_family_id"] = family_id
1045
+ if row is not canonical:
1046
+ row["terminal_state"] = "duplicate_version"
1047
+ row["retained_raw_hit_id"] = canonical["raw_hit_id"]
1048
+ coverage = "abstract" if canonical["abstract_state"] == "available" else "metadata_only"
1049
+ state = "available" if coverage == "abstract" else "abstract_missing"
1050
+ families.append(
1051
+ {
1052
+ "work_family_id": family_id,
1053
+ "member_raw_hit_ids": member_ids,
1054
+ "canonical_raw_hit_id": canonical["raw_hit_id"],
1055
+ "query_ids": sorted({row["query_id"] for row in members}, key=query_order.__getitem__),
1056
+ "index_ids": sorted({row["index_id"] for row in members}),
1057
+ "relevance": copy.deepcopy(assessment),
1058
+ "selection_state": None,
1059
+ "coverage": coverage,
1060
+ "content_state": state,
1061
+ "content_sha256": canonical["abstract_sha256"] if coverage == "abstract" else None,
1062
+ "sharing_scope": plan["consent"]["local_persistence"],
1063
+ "rights_basis": "not_assessed",
1064
+ }
1065
+ )
1066
+ _expect(set(assessments) == {row["canonical_raw_hit_id"] for row in families}, "relevance_assessments", "must contain exactly one row per computed canonical family")
1067
+ candidates = [row for row in families if row["relevance"]["state"] in {"relevant", "ambiguous"}]
1068
+ candidates.sort(
1069
+ key=lambda family: (
1070
+ 0 if family["relevance"]["state"] == "relevant" else 1,
1071
+ min(by_id[row_id]["provider_rank"] for row_id in family["member_raw_hit_ids"]),
1072
+ min(query_order[by_id[row_id]["query_id"]] for row_id in family["member_raw_hit_ids"]),
1073
+ by_id[family["canonical_raw_hit_id"]]["index_id"],
1074
+ by_id[family["canonical_raw_hit_id"]]["provider_record_id"] or "",
1075
+ family["work_family_id"],
1076
+ )
1077
+ )
1078
+ selected_ids = {row["work_family_id"] for row in candidates[:MAX_SELECTED]}
1079
+ for family in families:
1080
+ canonical = by_id[family["canonical_raw_hit_id"]]
1081
+ if family["relevance"]["state"] == "not_relevant":
1082
+ family["selection_state"] = canonical["terminal_state"] = "not_relevant"
1083
+ elif family["relevance"]["state"] == "not_checked":
1084
+ family["selection_state"] = canonical["terminal_state"] = "not_checked"
1085
+ elif family["work_family_id"] in selected_ids:
1086
+ family["selection_state"] = canonical["terminal_state"] = "selected"
1087
+ else:
1088
+ family["selection_state"] = canonical["terminal_state"] = "candidate_cap_exceeded"
1089
+ families.sort(key=lambda row: row["work_family_id"])
1090
+ state_counts = Counter(row["terminal_state"] for row in final_rows)
1091
+ failure_counts = Counter(row["outcome"] for row in retained["attempts"] if row["outcome"] != "success")
1092
+ ledger = {
1093
+ "schema_version": LEDGER_VERSION,
1094
+ "probe_id": plan["probe_id"],
1095
+ "query_plan_sha256": plan["plan_sha256"],
1096
+ "claim_sha256": plan["claim"]["claim_sha256"],
1097
+ "adapter_registry_sha256": digest(plan["provider_roster"]),
1098
+ "local_persistence": plan["consent"]["local_persistence"],
1099
+ "export_boundary": plan["consent"]["export_boundary"],
1100
+ "authorized_output_path": plan["consent"].get("authorized_output_path"),
1101
+ "retrieval_input_sha256": retained["retrieval_input_sha256"],
1102
+ "attempts": copy.deepcopy(retained["attempts"]),
1103
+ "retry_authorizations": copy.deepcopy(retained["retry_authorizations"]),
1104
+ "raw_hits": final_rows,
1105
+ "researcher_confirmed_version_relations": copy.deepcopy(retained["researcher_confirmed_version_relations"]),
1106
+ "work_families": families,
1107
+ "counts": {
1108
+ "attempts": len(retained["attempts"]),
1109
+ "successful_attempts": sum(row["outcome"] == "success" for row in retained["attempts"]),
1110
+ "failed_attempts": sum(row["outcome"] != "success" for row in retained["attempts"]),
1111
+ "raw_hits": len(final_rows),
1112
+ "work_families": len(families),
1113
+ "selected_work_families": len(selected_ids),
1114
+ "raw_hit_state_counts": {state: state_counts[state] for state in RAW_TERMINAL_STATES},
1115
+ "attempt_failure_counts": {state: failure_counts[state] for state in sorted(FAILURE_OUTCOMES)},
1116
+ },
1117
+ "completed_at": retained["completed_at"],
1118
+ "candidate_ledger_sha256": "",
1119
+ }
1120
+ ledger["candidate_ledger_sha256"] = bound_digest(ledger, "candidate_ledger_sha256")
1121
+ validate_schema(ledger, "candidate_ledger.schema.json", "candidate ledger")
1122
+ return ledger
1123
+
1124
+
1125
+ def validate_ledger(plan: dict[str, Any], retained: dict[str, Any], ledger: dict[str, Any]) -> None:
1126
+ expected = build_ledger(plan, retained)
1127
+ _expect(ledger == expected, "candidate_ledger", "does not exactly replay from named plan and retrieval input")
1128
+
1129
+
1130
+ def write_new_ledger(path: Path, value: dict[str, Any]) -> None:
1131
+ descriptor: int | None = None
1132
+ try:
1133
+ path.parent.mkdir(parents=True, exist_ok=True)
1134
+ flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1135
+ if os.name != "nt":
1136
+ flags |= os.O_NOFOLLOW
1137
+ descriptor = os.open(path, flags, 0o600)
1138
+ with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as handle:
1139
+ descriptor = None
1140
+ handle.write(json.dumps(value, ensure_ascii=False, indent=2) + "\n")
1141
+ handle.flush()
1142
+ os.fsync(handle.fileno())
1143
+ if os.name != "nt":
1144
+ directory_descriptor = os.open(path.parent, os.O_RDONLY)
1145
+ try:
1146
+ os.fsync(directory_descriptor)
1147
+ finally:
1148
+ os.close(directory_descriptor)
1149
+ except FileExistsError as exc:
1150
+ raise LedgerError("output: refuses to overwrite an existing path") from exc
1151
+ except (OSError, UnicodeError) as exc:
1152
+ raise LedgerError(f"output: cannot write new ledger: {exc}") from exc
1153
+ finally:
1154
+ if descriptor is not None:
1155
+ os.close(descriptor)
1156
+
1157
+
1158
+ def authorized_export_path(plan: dict[str, Any], suffix: str) -> Path:
1159
+ """The only writable path for a consent-derived artifact of this probe."""
1160
+ authorized = plan["consent"]["authorized_output_path"]
1161
+ if not isinstance(authorized, str) or not authorized:
1162
+ raise LedgerError(
1163
+ "consent.authorized_output_path: no operator-named output path is "
1164
+ "bound by this receipt"
1165
+ )
1166
+ if authorized.endswith(("/", "\\")):
1167
+ raise LedgerError(
1168
+ "consent.authorized_output_path: a trailing path separator would "
1169
+ "derive a hidden artifact name; name a file base, not a directory"
1170
+ )
1171
+ return Path(authorized + suffix)
1172
+
1173
+
1174
+ def require_export_consent(
1175
+ plan: dict[str, Any], output: Path, suffix: str
1176
+ ) -> Path:
1177
+ """Fail closed unless the hash-bound consent authorizes exactly `output`."""
1178
+ if plan["consent"]["local_persistence"] != "explicit_local_export":
1179
+ raise LedgerError(
1180
+ "consent does not say explicit_local_export: refusing to create "
1181
+ "any output path"
1182
+ )
1183
+ authorized = authorized_export_path(plan, suffix)
1184
+ if str(output) != str(authorized):
1185
+ raise LedgerError(
1186
+ "output path must exactly match the consent-derived path "
1187
+ f"{str(authorized)!r}"
1188
+ )
1189
+ return authorized
1190
+
1191
+
1192
+ def _parser() -> Any:
1193
+ parser = argparse.ArgumentParser(description=__doc__)
1194
+ sub = parser.add_subparsers(dest="command", required=True)
1195
+ build = sub.add_parser("build", help="write one new replayable ledger; never retrieve")
1196
+ build.add_argument("--query-plan", type=Path, required=True)
1197
+ build.add_argument("--retrieval-input", type=Path, required=True)
1198
+ build.add_argument("--output", type=Path, required=True)
1199
+ validate = sub.add_parser("validate", help="rebuild and compare one retained ledger")
1200
+ validate.add_argument("--query-plan", type=Path, required=True)
1201
+ validate.add_argument("--retrieval-input", type=Path, required=True)
1202
+ validate.add_argument("--candidate-ledger", type=Path, required=True)
1203
+ return parser
1204
+
1205
+
1206
+ def main(argv: list[str] | None = None) -> int:
1207
+ args = _parser().parse_args(argv)
1208
+ try:
1209
+ plan = load_json(args.query_plan)
1210
+ retained = load_json(args.retrieval_input)
1211
+ if args.command == "build":
1212
+ _expect(
1213
+ plan.get("consent", {}).get("local_persistence")
1214
+ == "explicit_local_export",
1215
+ "output",
1216
+ "local_persistence=session_only forbids writing a ledger; "
1217
+ "a separately consented explicit_local_export plan is required",
1218
+ )
1219
+ _expect(
1220
+ plan.get("consent", {}).get("authorized_output_path")
1221
+ == str(args.output),
1222
+ "output",
1223
+ "does not exactly match the hash-bound authorized_output_path",
1224
+ )
1225
+ ledger = build_ledger(plan, retained)
1226
+ write_new_ledger(args.output, ledger)
1227
+ print(f"PASS: wrote {args.output}")
1228
+ else:
1229
+ validate_ledger(plan, retained, load_json(args.candidate_ledger))
1230
+ print("PASS: candidate ledger exactly replays")
1231
+ except (LedgerError, KeyError, TypeError) as exc:
1232
+ print(f"ERROR: {exc}", file=sys.stderr)
1233
+ return 1
1234
+ return 0
1235
+
1236
+
1237
+ if __name__ == "__main__":
1238
+ raise SystemExit(main())