psyclaw 0.27.23 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1069) hide show
  1. package/README.md +11 -5
  2. package/agents/recommended/catalog.json +99 -0
  3. package/dist/apps/panel/index.html +5 -5
  4. package/dist/src/adapters/pi/extension.js +339 -94
  5. package/dist/src/adapters/pi/extension.js.map +1 -1
  6. package/dist/src/adapters/pi/rpc.d.ts +2 -0
  7. package/dist/src/adapters/pi/rpc.js +9 -2
  8. package/dist/src/adapters/pi/rpc.js.map +1 -1
  9. package/dist/src/agents/import.js +44 -32
  10. package/dist/src/agents/import.js.map +1 -1
  11. package/dist/src/agents/recommended-personas.d.ts +42 -0
  12. package/dist/src/agents/recommended-personas.js +135 -0
  13. package/dist/src/agents/recommended-personas.js.map +1 -0
  14. package/dist/src/analysis/hooks.d.ts +1 -0
  15. package/dist/src/analysis/hooks.js +20 -1
  16. package/dist/src/analysis/hooks.js.map +1 -1
  17. package/dist/src/ars/bridge.d.ts +20 -0
  18. package/dist/src/ars/bridge.js +132 -0
  19. package/dist/src/ars/bridge.js.map +1 -0
  20. package/dist/src/ars/contracts.d.ts +55 -0
  21. package/dist/src/ars/contracts.js +2 -0
  22. package/dist/src/ars/contracts.js.map +1 -0
  23. package/dist/src/ars/panel-plan.d.ts +4 -0
  24. package/dist/src/ars/panel-plan.js +11 -0
  25. package/dist/src/ars/panel-plan.js.map +1 -0
  26. package/dist/src/ars/pi-panel-executor.d.ts +30 -0
  27. package/dist/src/ars/pi-panel-executor.js +140 -0
  28. package/dist/src/ars/pi-panel-executor.js.map +1 -0
  29. package/dist/src/ars/profile.d.ts +36 -0
  30. package/dist/src/ars/profile.js +119 -0
  31. package/dist/src/ars/profile.js.map +1 -0
  32. package/dist/src/ars/re-review.d.ts +22 -0
  33. package/dist/src/ars/re-review.js +172 -0
  34. package/dist/src/ars/re-review.js.map +1 -0
  35. package/dist/src/branding.d.ts +2 -4
  36. package/dist/src/branding.js +3 -5
  37. package/dist/src/branding.js.map +1 -1
  38. package/dist/src/bundled-tools.d.ts +3 -0
  39. package/dist/src/bundled-tools.js +21 -0
  40. package/dist/src/bundled-tools.js.map +1 -0
  41. package/dist/src/chat.js +6 -5
  42. package/dist/src/chat.js.map +1 -1
  43. package/dist/src/cli.js +6 -3
  44. package/dist/src/cli.js.map +1 -1
  45. package/dist/src/creation/contracts.d.ts +41 -0
  46. package/dist/src/creation/contracts.js +2 -0
  47. package/dist/src/creation/contracts.js.map +1 -0
  48. package/dist/src/creation/service.d.ts +7 -0
  49. package/dist/src/creation/service.js +208 -0
  50. package/dist/src/creation/service.js.map +1 -0
  51. package/dist/src/index.d.ts +11 -0
  52. package/dist/src/index.js +11 -0
  53. package/dist/src/index.js.map +1 -1
  54. package/dist/src/install/installer.js +32 -10
  55. package/dist/src/install/installer.js.map +1 -1
  56. package/dist/src/orchestration/bundled-personas.d.ts +16 -0
  57. package/dist/src/orchestration/bundled-personas.js +101 -0
  58. package/dist/src/orchestration/bundled-personas.js.map +1 -0
  59. package/dist/src/orchestration/effects.d.ts +8 -0
  60. package/dist/src/orchestration/effects.js +39 -0
  61. package/dist/src/orchestration/effects.js.map +1 -0
  62. package/dist/src/orchestration/personas.d.ts +16 -0
  63. package/dist/src/orchestration/personas.js +64 -0
  64. package/dist/src/orchestration/personas.js.map +1 -0
  65. package/dist/src/orchestration/pi-executor.d.ts +9 -4
  66. package/dist/src/orchestration/pi-executor.js +55 -22
  67. package/dist/src/orchestration/pi-executor.js.map +1 -1
  68. package/dist/src/orchestration/research-agents.d.ts +3 -7
  69. package/dist/src/orchestration/research-agents.js +5 -69
  70. package/dist/src/orchestration/research-agents.js.map +1 -1
  71. package/dist/src/panel/server.js +19 -4
  72. package/dist/src/panel/server.js.map +1 -1
  73. package/dist/src/project/paths.d.ts +3 -0
  74. package/dist/src/project/paths.js +7 -0
  75. package/dist/src/project/paths.js.map +1 -1
  76. package/dist/src/rules/user-rules.d.ts +8 -0
  77. package/dist/src/rules/user-rules.js +36 -0
  78. package/dist/src/rules/user-rules.js.map +1 -0
  79. package/dist/src/skills/contracts.d.ts +4 -4
  80. package/dist/src/skills/recommended.js +1 -1
  81. package/dist/src/skills/registry.js +49 -28
  82. package/dist/src/skills/registry.js.map +1 -1
  83. package/dist/src/style/cli-ui.d.ts +14 -0
  84. package/dist/src/style/cli-ui.js +45 -1
  85. package/dist/src/style/cli-ui.js.map +1 -1
  86. package/dist/src/tui/skill-manager.js +1 -1
  87. package/dist/src/tui/skill-manager.js.map +1 -1
  88. package/dist/src/updates/update.js +9 -3
  89. package/dist/src/updates/update.js.map +1 -1
  90. package/package.json +15 -3
  91. package/scripts/rebrand-pi.mjs +6 -0
  92. package/skills/recommended/catalog.json +2 -11
  93. package/vendor/ars/.claude/CLAUDE.md +371 -0
  94. package/vendor/ars/.command-invariants.toml +24 -0
  95. package/vendor/ars/CITATION.cff +35 -0
  96. package/vendor/ars/LICENSE +417 -0
  97. package/vendor/ars/MODE_REGISTRY.md +76 -0
  98. package/vendor/ars/NOTICE.md +26 -0
  99. package/vendor/ars/POSITIONING.md +99 -0
  100. package/vendor/ars/PSYCLAW_SOURCE.json +10 -0
  101. package/vendor/ars/README.md +751 -0
  102. package/vendor/ars/SECURITY.md +52 -0
  103. package/vendor/ars/THIRD_PARTY.md +70 -0
  104. package/vendor/ars/academic-paper/SKILL.md +542 -0
  105. package/vendor/ars/academic-paper/agents/abstract_bilingual_agent.md +171 -0
  106. package/vendor/ars/academic-paper/agents/argument_builder_agent.md +276 -0
  107. package/vendor/ars/academic-paper/agents/citation_compliance_agent.md +422 -0
  108. package/vendor/ars/academic-paper/agents/draft_writer_agent.md +656 -0
  109. package/vendor/ars/academic-paper/agents/formatter_agent.md +999 -0
  110. package/vendor/ars/academic-paper/agents/intake_agent.md +393 -0
  111. package/vendor/ars/academic-paper/agents/literature_strategist_agent.md +626 -0
  112. package/vendor/ars/academic-paper/agents/peer_reviewer_agent.md +516 -0
  113. package/vendor/ars/academic-paper/agents/revision_coach_agent.md +334 -0
  114. package/vendor/ars/academic-paper/agents/socratic_mentor_agent.md +527 -0
  115. package/vendor/ars/academic-paper/agents/structure_architect_agent.md +401 -0
  116. package/vendor/ars/academic-paper/agents/visualization_agent.md +441 -0
  117. package/vendor/ars/academic-paper/examples/chinese_paper_example.md +278 -0
  118. package/vendor/ars/academic-paper/examples/clinical_citation_verification_checklist.md +95 -0
  119. package/vendor/ars/academic-paper/examples/clinical_epistemic_status_example.md +100 -0
  120. package/vendor/ars/academic-paper/examples/commitment_ledger_example.md +147 -0
  121. package/vendor/ars/academic-paper/examples/imrad_hei_example.md +234 -0
  122. package/vendor/ars/academic-paper/examples/literature_review_example.md +260 -0
  123. package/vendor/ars/academic-paper/examples/plan_mode_guided_writing.md +600 -0
  124. package/vendor/ars/academic-paper/examples/revision_mode_example.md +344 -0
  125. package/vendor/ars/academic-paper/examples/revision_recovery_example.md +506 -0
  126. package/vendor/ars/academic-paper/examples/version_family_reconciliation_example.md +89 -0
  127. package/vendor/ars/academic-paper/references/abstract_writing_guide.md +169 -0
  128. package/vendor/ars/academic-paper/references/academic_writing_style.md +188 -0
  129. package/vendor/ars/academic-paper/references/anti_leakage_protocol.md +83 -0
  130. package/vendor/ars/academic-paper/references/apa7_chinese_citation_guide.md +364 -0
  131. package/vendor/ars/academic-paper/references/apa7_extended_guide.md +198 -0
  132. package/vendor/ars/academic-paper/references/changelog.md +11 -0
  133. package/vendor/ars/academic-paper/references/citation_format_switcher.md +228 -0
  134. package/vendor/ars/academic-paper/references/committee_correspondence_protocol.md +158 -0
  135. package/vendor/ars/academic-paper/references/credit_authorship_guide.md +308 -0
  136. package/vendor/ars/academic-paper/references/disclosure_mode_protocol.md +478 -0
  137. package/vendor/ars/academic-paper/references/domain_evidence_profiles.md +38 -0
  138. package/vendor/ars/academic-paper/references/failure_paths.md +349 -0
  139. package/vendor/ars/academic-paper/references/funding_statement_guide.md +319 -0
  140. package/vendor/ars/academic-paper/references/hei_domain_glossary.md +169 -0
  141. package/vendor/ars/academic-paper/references/intro_title_rhetoric_guide.md +114 -0
  142. package/vendor/ars/academic-paper/references/journal_submission_guide.md +249 -0
  143. package/vendor/ars/academic-paper/references/latex_template_reference.md +378 -0
  144. package/vendor/ars/academic-paper/references/mode_selection_guide.md +378 -0
  145. package/vendor/ars/academic-paper/references/paper_structure_patterns.md +330 -0
  146. package/vendor/ars/academic-paper/references/plan_mode_protocol.md +112 -0
  147. package/vendor/ars/academic-paper/references/policy_anchor_disclosure_protocol.md +200 -0
  148. package/vendor/ars/academic-paper/references/policy_anchor_table.md +157 -0
  149. package/vendor/ars/academic-paper/references/revision_patch_protocol.md +173 -0
  150. package/vendor/ars/academic-paper/references/statistical_visualization_standards.md +750 -0
  151. package/vendor/ars/academic-paper/references/venue_disclosure_policies.md +259 -0
  152. package/vendor/ars/academic-paper/references/vlm_figure_verification.md +126 -0
  153. package/vendor/ars/academic-paper/references/workflow_phase_details.md +135 -0
  154. package/vendor/ars/academic-paper/references/writing_judgment_framework.md +59 -0
  155. package/vendor/ars/academic-paper/references/writing_quality_check.md +173 -0
  156. package/vendor/ars/academic-paper/templates/bilingual_abstract_template.md +78 -0
  157. package/vendor/ars/academic-paper/templates/case_study_template.md +129 -0
  158. package/vendor/ars/academic-paper/templates/conference_paper_template.md +108 -0
  159. package/vendor/ars/academic-paper/templates/credit_statement_template.md +132 -0
  160. package/vendor/ars/academic-paper/templates/funding_statement_template.md +290 -0
  161. package/vendor/ars/academic-paper/templates/imrad_template.md +183 -0
  162. package/vendor/ars/academic-paper/templates/latex_article_template.tex +199 -0
  163. package/vendor/ars/academic-paper/templates/literature_review_template.md +135 -0
  164. package/vendor/ars/academic-paper/templates/policy_brief_template.md +139 -0
  165. package/vendor/ars/academic-paper/templates/revision_tracking_template.md +199 -0
  166. package/vendor/ars/academic-paper/templates/theoretical_paper_template.md +119 -0
  167. package/vendor/ars/academic-paper-reviewer/SKILL.md +491 -0
  168. package/vendor/ars/academic-paper-reviewer/agents/devils_advocate_reviewer_agent.md +443 -0
  169. package/vendor/ars/academic-paper-reviewer/agents/domain_reviewer_agent.md +412 -0
  170. package/vendor/ars/academic-paper-reviewer/agents/editorial_synthesizer_agent.md +478 -0
  171. package/vendor/ars/academic-paper-reviewer/agents/eic_agent.md +339 -0
  172. package/vendor/ars/academic-paper-reviewer/agents/field_analyst_agent.md +221 -0
  173. package/vendor/ars/academic-paper-reviewer/agents/methodology_reviewer_agent.md +449 -0
  174. package/vendor/ars/academic-paper-reviewer/agents/perspective_reviewer_agent.md +427 -0
  175. package/vendor/ars/academic-paper-reviewer/examples/hei_paper_review_example.md +391 -0
  176. package/vendor/ars/academic-paper-reviewer/examples/interdisciplinary_review_example.md +299 -0
  177. package/vendor/ars/academic-paper-reviewer/examples/subclaim_decomposition_example.md +80 -0
  178. package/vendor/ars/academic-paper-reviewer/references/calibration_mode_protocol.md +256 -0
  179. package/vendor/ars/academic-paper-reviewer/references/changelog.md +10 -0
  180. package/vendor/ars/academic-paper-reviewer/references/editorial_decision_standards.md +236 -0
  181. package/vendor/ars/academic-paper-reviewer/references/guided_mode_protocol.md +34 -0
  182. package/vendor/ars/academic-paper-reviewer/references/integration_guide.md +15 -0
  183. package/vendor/ars/academic-paper-reviewer/references/quality_rubrics.md +84 -0
  184. package/vendor/ars/academic-paper-reviewer/references/re_review_mode_protocol.md +340 -0
  185. package/vendor/ars/academic-paper-reviewer/references/review_criteria_framework.md +98 -0
  186. package/vendor/ars/academic-paper-reviewer/references/review_panel_provenance_protocol.md +197 -0
  187. package/vendor/ars/academic-paper-reviewer/references/review_quality_thinking.md +58 -0
  188. package/vendor/ars/academic-paper-reviewer/references/reviewer_sprint_prompt_source.md +324 -0
  189. package/vendor/ars/academic-paper-reviewer/references/sprint_contract_protocol.md +296 -0
  190. package/vendor/ars/academic-paper-reviewer/references/statistical_reporting_standards.md +505 -0
  191. package/vendor/ars/academic-paper-reviewer/references/top_journals_by_field.md +206 -0
  192. package/vendor/ars/academic-paper-reviewer/templates/editorial_decision_template.md +235 -0
  193. package/vendor/ars/academic-paper-reviewer/templates/peer_review_report_template.md +305 -0
  194. package/vendor/ars/academic-paper-reviewer/templates/revision_response_template.md +248 -0
  195. package/vendor/ars/academic-pipeline/SKILL.md +736 -0
  196. package/vendor/ars/academic-pipeline/agents/claim_ref_alignment_audit_agent.md +382 -0
  197. package/vendor/ars/academic-pipeline/agents/collaboration_depth_agent.md +164 -0
  198. package/vendor/ars/academic-pipeline/agents/integrity_verification_agent.md +870 -0
  199. package/vendor/ars/academic-pipeline/agents/pipeline_orchestrator_agent.md +1379 -0
  200. package/vendor/ars/academic-pipeline/agents/state_tracker_agent.md +622 -0
  201. package/vendor/ars/academic-pipeline/examples/full_pipeline_example.md +482 -0
  202. package/vendor/ars/academic-pipeline/examples/integrity_failure_recovery.md +389 -0
  203. package/vendor/ars/academic-pipeline/examples/mid_entry_example.md +414 -0
  204. package/vendor/ars/academic-pipeline/references/adapters/.gitkeep +0 -0
  205. package/vendor/ars/academic-pipeline/references/adapters/overview.md +153 -0
  206. package/vendor/ars/academic-pipeline/references/ai_research_failure_modes.md +185 -0
  207. package/vendor/ars/academic-pipeline/references/changelog.md +14 -0
  208. package/vendor/ars/academic-pipeline/references/claim_audit_calibration_protocol.md +175 -0
  209. package/vendor/ars/academic-pipeline/references/claim_verification_protocol.md +282 -0
  210. package/vendor/ars/academic-pipeline/references/external_review_protocol.md +131 -0
  211. package/vendor/ars/academic-pipeline/references/integrity_review_protocol.md +110 -0
  212. package/vendor/ars/academic-pipeline/references/literature_corpus_consumers.md +193 -0
  213. package/vendor/ars/academic-pipeline/references/mode_advisor.md +135 -0
  214. package/vendor/ars/academic-pipeline/references/passport_as_reset_boundary.md +132 -0
  215. package/vendor/ars/academic-pipeline/references/pipeline_state_machine.md +405 -0
  216. package/vendor/ars/academic-pipeline/references/plagiarism_detection_protocol.md +239 -0
  217. package/vendor/ars/academic-pipeline/references/process_summary_protocol.md +209 -0
  218. package/vendor/ars/academic-pipeline/references/progress_dashboard_template.md +38 -0
  219. package/vendor/ars/academic-pipeline/references/reinforcement_content.md +15 -0
  220. package/vendor/ars/academic-pipeline/references/reproducibility_audit.md +55 -0
  221. package/vendor/ars/academic-pipeline/references/score_trajectory_protocol.md +78 -0
  222. package/vendor/ars/academic-pipeline/references/team_collaboration_protocol.md +261 -0
  223. package/vendor/ars/academic-pipeline/references/two_stage_review_protocol.md +27 -0
  224. package/vendor/ars/academic-pipeline/templates/pipeline_status_template.md +146 -0
  225. package/vendor/ars/agents/report_compiler_agent.md +341 -0
  226. package/vendor/ars/agents/research_architect_agent.md +298 -0
  227. package/vendor/ars/agents/synthesis_agent.md +356 -0
  228. package/vendor/ars/commands/ars-3w.md +10 -0
  229. package/vendor/ars/commands/ars-abstract.md +10 -0
  230. package/vendor/ars/commands/ars-cache-invalidate.md +20 -0
  231. package/vendor/ars/commands/ars-citation-check.md +10 -0
  232. package/vendor/ars/commands/ars-disclosure.md +10 -0
  233. package/vendor/ars/commands/ars-format-convert.md +10 -0
  234. package/vendor/ars/commands/ars-full.md +9 -0
  235. package/vendor/ars/commands/ars-lit-review.md +12 -0
  236. package/vendor/ars/commands/ars-mark-read.md +18 -0
  237. package/vendor/ars/commands/ars-outline.md +10 -0
  238. package/vendor/ars/commands/ars-plan.md +10 -0
  239. package/vendor/ars/commands/ars-rebuttal-audit.md +12 -0
  240. package/vendor/ars/commands/ars-reviewer.md +9 -0
  241. package/vendor/ars/commands/ars-revision-coach.md +9 -0
  242. package/vendor/ars/commands/ars-revision.md +10 -0
  243. package/vendor/ars/commands/ars-unmark-read.md +16 -0
  244. package/vendor/ars/deep-research/SKILL.md +600 -0
  245. package/vendor/ars/deep-research/agents/bibliography_agent.md +473 -0
  246. package/vendor/ars/deep-research/agents/devils_advocate_agent.md +192 -0
  247. package/vendor/ars/deep-research/agents/editor_in_chief_agent.md +167 -0
  248. package/vendor/ars/deep-research/agents/ethics_review_agent.md +267 -0
  249. package/vendor/ars/deep-research/agents/meta_analysis_agent.md +325 -0
  250. package/vendor/ars/deep-research/agents/monitoring_agent.md +209 -0
  251. package/vendor/ars/deep-research/agents/report_compiler_agent.md +341 -0
  252. package/vendor/ars/deep-research/agents/research_architect_agent.md +298 -0
  253. package/vendor/ars/deep-research/agents/research_question_agent.md +216 -0
  254. package/vendor/ars/deep-research/agents/risk_of_bias_agent.md +231 -0
  255. package/vendor/ars/deep-research/agents/socratic_mentor_agent.md +764 -0
  256. package/vendor/ars/deep-research/agents/source_verification_agent.md +219 -0
  257. package/vendor/ars/deep-research/agents/synthesis_agent.md +356 -0
  258. package/vendor/ars/deep-research/agents/timeline_extraction_agent.md +99 -0
  259. package/vendor/ars/deep-research/examples/exploratory_research.md +157 -0
  260. package/vendor/ars/deep-research/examples/fact_check_mode.md +173 -0
  261. package/vendor/ars/deep-research/examples/handoff_to_paper.md +318 -0
  262. package/vendor/ars/deep-research/examples/idea_diversity_coverage_gap_advisory.md +64 -0
  263. package/vendor/ars/deep-research/examples/policy_analysis.md +161 -0
  264. package/vendor/ars/deep-research/examples/review_mode.md +253 -0
  265. package/vendor/ars/deep-research/examples/socratic_guided_research.md +331 -0
  266. package/vendor/ars/deep-research/examples/systematic_review.md +133 -0
  267. package/vendor/ars/deep-research/references/apa7_style_guide.md +162 -0
  268. package/vendor/ars/deep-research/references/argumentation_reasoning_framework.md +68 -0
  269. package/vendor/ars/deep-research/references/arxiv_api_protocol.md +76 -0
  270. package/vendor/ars/deep-research/references/changelog.md +22 -0
  271. package/vendor/ars/deep-research/references/chinese_literature_api_protocol.md +317 -0
  272. package/vendor/ars/deep-research/references/cross_agent_quality_definitions.md +14 -0
  273. package/vendor/ars/deep-research/references/crossref_api_protocol.md +84 -0
  274. package/vendor/ars/deep-research/references/equator_reporting_guidelines.md +482 -0
  275. package/vendor/ars/deep-research/references/ethics_checklist.md +282 -0
  276. package/vendor/ars/deep-research/references/failure_paths.md +355 -0
  277. package/vendor/ars/deep-research/references/interdisciplinary_bridges.md +292 -0
  278. package/vendor/ars/deep-research/references/irb_decision_tree.md +315 -0
  279. package/vendor/ars/deep-research/references/literature_monitoring_strategies.md +263 -0
  280. package/vendor/ars/deep-research/references/logical_fallacies.md +192 -0
  281. package/vendor/ars/deep-research/references/methodology_patterns.md +462 -0
  282. package/vendor/ars/deep-research/references/mode_selection_guide.md +331 -0
  283. package/vendor/ars/deep-research/references/openalex_api_protocol.md +82 -0
  284. package/vendor/ars/deep-research/references/preregistration_guide.md +324 -0
  285. package/vendor/ars/deep-research/references/semantic_scholar_api_protocol.md +107 -0
  286. package/vendor/ars/deep-research/references/socratic_mode_protocol.md +99 -0
  287. package/vendor/ars/deep-research/references/socratic_questioning_framework.md +232 -0
  288. package/vendor/ars/deep-research/references/source_quality_hierarchy.md +188 -0
  289. package/vendor/ars/deep-research/references/systematic_review_protocol.md +95 -0
  290. package/vendor/ars/deep-research/references/systematic_review_toolkit.md +353 -0
  291. package/vendor/ars/deep-research/templates/evidence_assessment_template.md +127 -0
  292. package/vendor/ars/deep-research/templates/literature_matrix_template.md +85 -0
  293. package/vendor/ars/deep-research/templates/preregistration_template.md +318 -0
  294. package/vendor/ars/deep-research/templates/prisma_protocol_template.md +248 -0
  295. package/vendor/ars/deep-research/templates/prisma_report_template.md +415 -0
  296. package/vendor/ars/deep-research/templates/research_brief_template.md +93 -0
  297. package/vendor/ars/package.json +24 -0
  298. package/vendor/ars/pi/README.md +161 -0
  299. package/vendor/ars/pi/package.json +26 -0
  300. package/vendor/ars/pi/wrapper.js +193 -0
  301. package/vendor/ars/pi/wrapper.test.mjs +201 -0
  302. package/vendor/ars/pyproject.toml +2 -0
  303. package/vendor/ars/requirements-pdf-content-classifier.txt +5 -0
  304. package/vendor/ars/scripts/_block_parser.py +396 -0
  305. package/vendor/ars/scripts/_ci_pytest_manifest.toml +661 -0
  306. package/vendor/ars/scripts/_claim_audit_constants.py +268 -0
  307. package/vendor/ars/scripts/_e4_evidence.py +110 -0
  308. package/vendor/ars/scripts/_eval_threshold_gate.py +73 -0
  309. package/vendor/ars/scripts/_markdown_lint_util.py +224 -0
  310. package/vendor/ars/scripts/_next_verified_at_ms.py +176 -0
  311. package/vendor/ars/scripts/_passport_yaml.py +53 -0
  312. package/vendor/ars/scripts/_skill_lint.py +254 -0
  313. package/vendor/ars/scripts/_text_similarity.py +141 -0
  314. package/vendor/ars/scripts/adapters/README.md +89 -0
  315. package/vendor/ars/scripts/adapters/_common.py +209 -0
  316. package/vendor/ars/scripts/adapters/examples/folder_scan/expected_passport.yaml +25 -0
  317. package/vendor/ars/scripts/adapters/examples/folder_scan/expected_rejection_log.yaml +18 -0
  318. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/Chen2024_AIAssessment.pdf +0 -0
  319. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/Wang_2023_formative_feedback.pdf +0 -0
  320. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/paper1.pdf +0 -0
  321. 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
  322. package/vendor/ars/scripts/adapters/examples/obsidian/expected_passport.yaml +40 -0
  323. package/vendor/ars/scripts/adapters/examples/obsidian/expected_rejection_log.yaml +13 -0
  324. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/.gitkeep +0 -0
  325. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/.obsidian/app.json +1 -0
  326. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/_templates/tmpl.md +7 -0
  327. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/chen2024ai.md +13 -0
  328. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/invalid.md +1 -0
  329. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/wang2023formative.md +10 -0
  330. package/vendor/ars/scripts/adapters/examples/zotero/expected_passport.yaml +34 -0
  331. package/vendor/ars/scripts/adapters/examples/zotero/expected_rejection_log.yaml +34 -0
  332. package/vendor/ars/scripts/adapters/examples/zotero/input_fixture/.gitkeep +0 -0
  333. package/vendor/ars/scripts/adapters/examples/zotero/input_fixture/export.json +43 -0
  334. package/vendor/ars/scripts/adapters/folder_scan.py +214 -0
  335. package/vendor/ars/scripts/adapters/obsidian.py +336 -0
  336. package/vendor/ars/scripts/adapters/tests/.gitkeep +0 -0
  337. package/vendor/ars/scripts/adapters/tests/conftest.py +77 -0
  338. package/vendor/ars/scripts/adapters/tests/test_check_corpus_consumer_protocol.py +632 -0
  339. package/vendor/ars/scripts/adapters/tests/test_check_literature_corpus_schema.py +631 -0
  340. package/vendor/ars/scripts/adapters/tests/test_common.py +365 -0
  341. package/vendor/ars/scripts/adapters/tests/test_conftest.py +96 -0
  342. package/vendor/ars/scripts/adapters/tests/test_folder_scan.py +260 -0
  343. package/vendor/ars/scripts/adapters/tests/test_literature_corpus_entry_schema.py +745 -0
  344. package/vendor/ars/scripts/adapters/tests/test_obsidian.py +357 -0
  345. package/vendor/ars/scripts/adapters/tests/test_rejection_log_schema.py +271 -0
  346. package/vendor/ars/scripts/adapters/tests/test_sync_adapter_docs.py +88 -0
  347. package/vendor/ars/scripts/adapters/tests/test_zotero.py +454 -0
  348. package/vendor/ars/scripts/adapters/zotero.py +318 -0
  349. package/vendor/ars/scripts/adjudication_activity.py +1592 -0
  350. package/vendor/ars/scripts/announce-ars-loaded.sh +144 -0
  351. package/vendor/ars/scripts/ars_anchorize_draft.py +170 -0
  352. package/vendor/ars/scripts/ars_apply_revision_patch.py +912 -0
  353. package/vendor/ars/scripts/ars_cache_invalidate.py +40 -0
  354. package/vendor/ars/scripts/ars_mark_read.py +521 -0
  355. package/vendor/ars/scripts/ars_phase_scope_manifest.json +33 -0
  356. package/vendor/ars/scripts/ars_update_check.sh +215 -0
  357. package/vendor/ars/scripts/ars_write_scope_guard.py +506 -0
  358. package/vendor/ars/scripts/arxiv_client.py +222 -0
  359. package/vendor/ars/scripts/audit_snapshot.py +572 -0
  360. package/vendor/ars/scripts/bibliographic_integrity_signals.py +800 -0
  361. package/vendor/ars/scripts/bootstrap_timeline_yaml.py +146 -0
  362. package/vendor/ars/scripts/build_claim_standing_candidate_ledger.py +1238 -0
  363. package/vendor/ars/scripts/build_claim_standing_query_plan.py +643 -0
  364. package/vendor/ars/scripts/build_content_coverage_advisory.py +1205 -0
  365. package/vendor/ars/scripts/build_cross_document_consistency_advisory.py +2332 -0
  366. package/vendor/ars/scripts/build_review_pathway_rule_trace.py +830 -0
  367. package/vendor/ars/scripts/build_submission_packet_manifest.py +2310 -0
  368. package/vendor/ars/scripts/check_215_field_norm.py +173 -0
  369. package/vendor/ars/scripts/check_216_surface_form.py +250 -0
  370. package/vendor/ars/scripts/check_268_nested_commitment_ledger.py +180 -0
  371. package/vendor/ars/scripts/check_390_revision_patch_discipline.py +296 -0
  372. package/vendor/ars/scripts/check_392_citation_verification_intake.py +126 -0
  373. package/vendor/ars/scripts/check_394_submission_policy.py +178 -0
  374. package/vendor/ars/scripts/check_439_format_profile.py +307 -0
  375. package/vendor/ars/scripts/check_619_disclosure_closeout.py +237 -0
  376. package/vendor/ars/scripts/check_630_codex_subscription_transport.py +456 -0
  377. package/vendor/ars/scripts/check_669_review_pathway_rule_trace.py +662 -0
  378. package/vendor/ars/scripts/check_670_revision_roadmap_integration.py +518 -0
  379. package/vendor/ars/scripts/check_673_adjudication_activity.py +684 -0
  380. package/vendor/ars/scripts/check_684_review_criteria_binding.py +557 -0
  381. package/vendor/ars/scripts/check_agents_mirror_sync.py +115 -0
  382. package/vendor/ars/scripts/check_audit_artifact_consistency.py +2313 -0
  383. package/vendor/ars/scripts/check_benchmark_report.py +79 -0
  384. package/vendor/ars/scripts/check_bibliographic_integrity_signals.py +831 -0
  385. package/vendor/ars/scripts/check_calibration_tiers.py +235 -0
  386. package/vendor/ars/scripts/check_changelog_covers_merges.py +289 -0
  387. package/vendor/ars/scripts/check_ci_pytest_manifest.py +204 -0
  388. package/vendor/ars/scripts/check_claim_audit_consistency.py +1664 -0
  389. package/vendor/ars/scripts/check_claim_standing_candidate_ledger_integration.py +500 -0
  390. package/vendor/ars/scripts/check_claim_standing_freshness.py +253 -0
  391. package/vendor/ars/scripts/check_claim_standing_transmissions.py +449 -0
  392. package/vendor/ars/scripts/check_collaboration_depth_rubric.py +180 -0
  393. package/vendor/ars/scripts/check_command_frontmatter_name.py +116 -0
  394. package/vendor/ars/scripts/check_committee_correspondence.py +333 -0
  395. package/vendor/ars/scripts/check_compliance_report.py +108 -0
  396. package/vendor/ars/scripts/check_content_coverage_advisory_integration.py +796 -0
  397. package/vendor/ars/scripts/check_control_availability.py +172 -0
  398. package/vendor/ars/scripts/check_corpus_consumer_protocol.py +404 -0
  399. package/vendor/ars/scripts/check_cross_document_consistency_advisory_integration.py +1191 -0
  400. package/vendor/ars/scripts/check_cross_model_handoff_contract.py +234 -0
  401. package/vendor/ars/scripts/check_cross_model_verification_sync.py +261 -0
  402. package/vendor/ars/scripts/check_data_access_level.py +131 -0
  403. package/vendor/ars/scripts/check_data_flows.py +252 -0
  404. package/vendor/ars/scripts/check_decision_contract.py +464 -0
  405. package/vendor/ars/scripts/check_degradation_registry.py +326 -0
  406. package/vendor/ars/scripts/check_distribution_surface_claims.py +226 -0
  407. package/vendor/ars/scripts/check_domain_evidence_profile.py +538 -0
  408. package/vendor/ars/scripts/check_e4_promotion.py +195 -0
  409. package/vendor/ars/scripts/check_evals_gold_set.py +279 -0
  410. package/vendor/ars/scripts/check_evidence_row_integration.py +396 -0
  411. package/vendor/ars/scripts/check_experiment_provenance.py +117 -0
  412. package/vendor/ars/scripts/check_field_norm_severity.py +144 -0
  413. package/vendor/ars/scripts/check_firm_rules_sync.py +375 -0
  414. package/vendor/ars/scripts/check_heldout_measurement_report.py +1179 -0
  415. package/vendor/ars/scripts/check_human_subjects_output_contract.py +139 -0
  416. package/vendor/ars/scripts/check_human_subjects_reference_migration.py +844 -0
  417. package/vendor/ars/scripts/check_indirect_prompt_injection_no_call.py +328 -0
  418. package/vendor/ars/scripts/check_instruction_data_boundary.py +236 -0
  419. package/vendor/ars/scripts/check_judge_prompt_version.py +125 -0
  420. package/vendor/ars/scripts/check_literature_corpus_schema.py +402 -0
  421. package/vendor/ars/scripts/check_model_tiering.py +223 -0
  422. package/vendor/ars/scripts/check_panel_synthesis.py +1331 -0
  423. package/vendor/ars/scripts/check_passport_reset_contract.py +214 -0
  424. package/vendor/ars/scripts/check_pattern_eval_manifest.py +422 -0
  425. package/vendor/ars/scripts/check_persuasion_invariance_fixtures.py +637 -0
  426. package/vendor/ars/scripts/check_phase_conformance.py +2180 -0
  427. package/vendor/ars/scripts/check_pipeline_boundary_semantics.py +600 -0
  428. package/vendor/ars/scripts/check_pipeline_integrity.py +340 -0
  429. package/vendor/ars/scripts/check_policy_anchor_protocol.py +159 -0
  430. package/vendor/ars/scripts/check_policy_anchor_table.py +286 -0
  431. package/vendor/ars/scripts/check_preprint_venues_consistency.py +123 -0
  432. package/vendor/ars/scripts/check_prisma_trAIce_freshness.py +84 -0
  433. package/vendor/ars/scripts/check_promotion_bakeoff_preregistration.py +1303 -0
  434. package/vendor/ars/scripts/check_ranking_lift.py +323 -0
  435. package/vendor/ars/scripts/check_re_review_synthesis.py +2719 -0
  436. package/vendor/ars/scripts/check_receipt_enum_sync.py +203 -0
  437. package/vendor/ars/scripts/check_repro_lock.py +85 -0
  438. package/vendor/ars/scripts/check_review_pathway_output.py +276 -0
  439. package/vendor/ars/scripts/check_reviewer_data_fences.py +224 -0
  440. package/vendor/ars/scripts/check_reviewer_finding_contract.py +783 -0
  441. package/vendor/ars/scripts/check_reviewer_role_label.py +447 -0
  442. package/vendor/ars/scripts/check_reviewer_scoring_honesty.py +292 -0
  443. package/vendor/ars/scripts/check_reviewer_sprint_prompt_sync.py +409 -0
  444. package/vendor/ars/scripts/check_revision_claim_drift_suite_v2.py +1622 -0
  445. package/vendor/ars/scripts/check_revision_token_conservation.py +263 -0
  446. package/vendor/ars/scripts/check_risk_register.py +280 -0
  447. package/vendor/ars/scripts/check_role_scoped_contract.py +702 -0
  448. package/vendor/ars/scripts/check_rq_framing_patterns.py +184 -0
  449. package/vendor/ars/scripts/check_rubric_weight_consistency.py +32 -0
  450. package/vendor/ars/scripts/check_seeded_defect_fixtures.py +425 -0
  451. package/vendor/ars/scripts/check_setup_cross_model_parity.py +139 -0
  452. package/vendor/ars/scripts/check_spec_consistency.py +1249 -0
  453. package/vendor/ars/scripts/check_sprint_contract.py +371 -0
  454. package/vendor/ars/scripts/check_stage_capability_matrix.py +784 -0
  455. package/vendor/ars/scripts/check_submission_packet_manifest_integration.py +358 -0
  456. package/vendor/ars/scripts/check_surface_form_parity.py +445 -0
  457. package/vendor/ars/scripts/check_task_type.py +22 -0
  458. package/vendor/ars/scripts/check_tools_allowlist.py +538 -0
  459. package/vendor/ars/scripts/check_tortured_phrase_screening_integration.py +1974 -0
  460. package/vendor/ars/scripts/check_v3_10_134_write_scope.py +286 -0
  461. package/vendor/ars/scripts/check_v3_10_policy.py +656 -0
  462. package/vendor/ars/scripts/check_v3_6_6_ab_manifest.py +364 -0
  463. package/vendor/ars/scripts/check_v3_6_7_pattern_protection.py +1366 -0
  464. package/vendor/ars/scripts/check_v3_6_8_audit_scope_block.py +464 -0
  465. package/vendor/ars/scripts/check_v3_6_8_cite_provenance_pipeline.py +231 -0
  466. package/vendor/ars/scripts/check_v3_6_8_frontmatter_trust_schema.py +248 -0
  467. package/vendor/ars/scripts/check_v3_6_8_mark_read_commands.py +79 -0
  468. package/vendor/ars/scripts/check_v3_6_8_pattern_protection.py +941 -0
  469. package/vendor/ars/scripts/check_v3_7_3_three_layer_citation.py +318 -0
  470. package/vendor/ars/scripts/check_v3_8_annotation_literal_sync.py +228 -0
  471. package/vendor/ars/scripts/check_v3_9_0_triangulation.py +366 -0
  472. package/vendor/ars/scripts/check_v3_9_2_phase_boundary.py +270 -0
  473. package/vendor/ars/scripts/check_v3_9_4_temporal_verification.py +139 -0
  474. package/vendor/ars/scripts/check_venue_disclosure_policies.py +63 -0
  475. package/vendor/ars/scripts/check_version_consistency.py +826 -0
  476. package/vendor/ars/scripts/check_workflow_classification.py +223 -0
  477. package/vendor/ars/scripts/chinese_literature_client.py +1938 -0
  478. package/vendor/ars/scripts/citation_verification_summary.py +85 -0
  479. package/vendor/ars/scripts/claim_audit_calibration.py +517 -0
  480. package/vendor/ars/scripts/claim_audit_finalizer.py +456 -0
  481. package/vendor/ars/scripts/claim_audit_pipeline.py +1594 -0
  482. package/vendor/ars/scripts/claim_registry_coverage.py +493 -0
  483. package/vendor/ars/scripts/claim_standing_discovery.py +784 -0
  484. package/vendor/ars/scripts/claim_standing_stance_runner.py +758 -0
  485. package/vendor/ars/scripts/claim_standing_stance_scorer.py +239 -0
  486. package/vendor/ars/scripts/claim_strength_drift_disposition.py +666 -0
  487. package/vendor/ars/scripts/contamination_signals.py +689 -0
  488. package/vendor/ars/scripts/corpus_consumer_manifest.json +19 -0
  489. package/vendor/ars/scripts/cross_model_codex_transport.py +1374 -0
  490. package/vendor/ars/scripts/cross_model_codex_verify.sh +6 -0
  491. package/vendor/ars/scripts/cross_model_handoff.py +359 -0
  492. package/vendor/ars/scripts/cross_model_smoke_test.sh +183 -0
  493. package/vendor/ars/scripts/cross_model_smoke_test_codex.sh +35 -0
  494. package/vendor/ars/scripts/cross_model_verification/gemini_is_grounded.jq +45 -0
  495. package/vendor/ars/scripts/cross_model_verification/gemini_sources.jq +45 -0
  496. package/vendor/ars/scripts/cross_model_verification/normalize_compat_verdict.py +57 -0
  497. package/vendor/ars/scripts/cross_model_verification/openai_has_completed_web_search.jq +15 -0
  498. package/vendor/ars/scripts/cross_model_verification/openai_sources.jq +17 -0
  499. package/vendor/ars/scripts/cross_model_verification/openai_text.jq +11 -0
  500. package/vendor/ars/scripts/crossref_client.py +225 -0
  501. package/vendor/ars/scripts/dispatch_e4_panel.py +2731 -0
  502. package/vendor/ars/scripts/evidence_rows.py +2043 -0
  503. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3.json +32 -0
  504. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_input.json +27 -0
  505. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_prime.json +32 -0
  506. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_prime_input.json +27 -0
  507. package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_override.json +50 -0
  508. package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_override_action.json +14 -0
  509. package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_pass.json +44 -0
  510. package/vendor/ars/scripts/fixtures/adjudication_activity/explicit_user_request_log.json +26 -0
  511. package/vendor/ars/scripts/fixtures/adjudication_activity/mandatory_checkpoint_log.json +116 -0
  512. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/README.md +56 -0
  513. 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
  514. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
  515. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
  516. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.verdict.yaml +19 -0
  517. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a7_orphan_completion/2026-04-30T15-22-04Z-d8f3.jsonl +3 -0
  518. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +26 -0
  519. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
  520. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
  521. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.verdict.yaml +19 -0
  522. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +24 -0
  523. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
  524. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
  525. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.verdict.yaml +12 -0
  526. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/retraction.json +46 -0
  527. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/retraction_check_attestation.json +44 -0
  528. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase.json +44 -0
  529. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase_v1_2_abstract_missing.json +107 -0
  530. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase_v1_2_detected.json +124 -0
  531. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/expected_outcomes.json +77 -0
  532. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/manifest.yaml +37 -0
  533. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/001-valid-doi-test.json +20 -0
  534. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/002-valid-arxiv-test.json +19 -0
  535. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/003-fabricated-test.json +20 -0
  536. package/vendor/ars/scripts/fixtures/claim_audit_calibration/gold_set.json +344 -0
  537. package/vendor/ars/scripts/fixtures/claim_standing_candidate_ledger/query_plan.json +108 -0
  538. package/vendor/ars/scripts/fixtures/claim_standing_candidate_ledger/retrieval_input.json +179 -0
  539. package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/concern_tracker.json +129 -0
  540. package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/response_skeleton.md +19 -0
  541. package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/source_letter.txt +9 -0
  542. package/vendor/ars/scripts/fixtures/content_coverage_advisory/base_draft.json +48 -0
  543. package/vendor/ars/scripts/fixtures/content_coverage_advisory/base_inventory.json +38 -0
  544. package/vendor/ars/scripts/fixtures/content_coverage_advisory/packet/consent.txt +1 -0
  545. package/vendor/ars/scripts/fixtures/content_coverage_advisory/session_sources.json +3 -0
  546. package/vendor/ars/scripts/fixtures/cross_document_consistency/README.md +6 -0
  547. package/vendor/ars/scripts/fixtures/cross_document_consistency/accepted_draft.md +35 -0
  548. package/vendor/ars/scripts/fixtures/cross_document_consistency/cases.json +75 -0
  549. package/vendor/ars/scripts/fixtures/cross_document_consistency/preregistration.md +5 -0
  550. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/forbidden_event.jsonl +4 -0
  551. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/grounded_verified.jsonl +3 -0
  552. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/malformed.jsonl +3 -0
  553. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/missing_search.jsonl +2 -0
  554. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/multiple_finals.jsonl +4 -0
  555. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/not_found.jsonl +3 -0
  556. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/unbound_source.jsonl +3 -0
  557. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/wrong_search_shape.jsonl +3 -0
  558. package/vendor/ars/scripts/fixtures/evidence_rows/phase_e_inputs.json +114 -0
  559. package/vendor/ars/scripts/fixtures/evidence_rows/session_sources.json +5 -0
  560. package/vendor/ars/scripts/fixtures/human_subjects_authority/cross-border-us-tw-gdpr.json +71 -0
  561. package/vendor/ars/scripts/fixtures/human_subjects_authority/gdpr-member-state-unresolved.json +62 -0
  562. package/vendor/ars/scripts/fixtures/human_subjects_authority/missing-data-axis.json +45 -0
  563. package/vendor/ars/scripts/fixtures/human_subjects_authority/no-profile.json +33 -0
  564. package/vendor/ars/scripts/fixtures/human_subjects_authority/tw-gdpr-two-axis.json +74 -0
  565. package/vendor/ars/scripts/fixtures/human_subjects_authority/us-gdpr-two-axis.json +74 -0
  566. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/lint-near-misses.json +72 -0
  567. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/no-profile-request.json +18 -0
  568. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/tw-candidates-request.json +63 -0
  569. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/us-candidates-request.json +77 -0
  570. package/vendor/ars/scripts/fixtures/review_target_context/exact-declaration.json +24 -0
  571. package/vendor/ars/scripts/fixtures/review_target_context/field-general-declaration.json +24 -0
  572. package/vendor/ars/scripts/fixtures/review_target_context/msr-2027-technical-full-declaration.json +24 -0
  573. package/vendor/ars/scripts/fixtures/review_target_context/synthetic-registry.json +92 -0
  574. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/README.md +21 -0
  575. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_attested_only.json +55 -0
  576. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_machine_supported.json +49 -0
  577. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_not_isolated.json +56 -0
  578. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_unknown.json +49 -0
  579. package/vendor/ars/scripts/fixtures/submission_package/clean/paper.md +16 -0
  580. package/vendor/ars/scripts/fixtures/submission_package/clean/references.bib +13 -0
  581. package/vendor/ars/scripts/fixtures/submission_package/fallback_authoryear/paper.md +11 -0
  582. package/vendor/ars/scripts/fixtures/submission_package/fallback_authoryear/references.bib +13 -0
  583. package/vendor/ars/scripts/fixtures/submission_package/fallback_latex/paper.tex +9 -0
  584. package/vendor/ars/scripts/fixtures/submission_package/fallback_latex/references.bib +13 -0
  585. package/vendor/ars/scripts/fixtures/submission_package/marker_no_join/paper.md +7 -0
  586. package/vendor/ars/scripts/fixtures/submission_package/orphan_intext/paper.md +8 -0
  587. package/vendor/ars/scripts/fixtures/submission_package/orphan_intext/references.bib +6 -0
  588. package/vendor/ars/scripts/fixtures/submission_package/passports/corpus_only.yaml +9 -0
  589. package/vendor/ars/scripts/fixtures/submission_package/passports/summary_join.yaml +16 -0
  590. package/vendor/ars/scripts/fixtures/submission_package/profiles/full.yaml +15 -0
  591. package/vendor/ars/scripts/fixtures/submission_package/profiles/tight.yaml +15 -0
  592. package/vendor/ars/scripts/fixtures/submission_package/summary_join/paper.md +7 -0
  593. package/vendor/ars/scripts/fixtures/submission_package/uncited_reference/paper.md +9 -0
  594. package/vendor/ars/scripts/fixtures/submission_package/uncited_reference/references.bib +13 -0
  595. package/vendor/ars/scripts/fixtures/submission_package/venue_clean/paper.md +26 -0
  596. package/vendor/ars/scripts/fixtures/submission_package/venue_clean/references.bib +13 -0
  597. package/vendor/ars/scripts/fixtures/submission_package/venue_violations/paper.md +20 -0
  598. package/vendor/ars/scripts/fixtures/submission_package/venue_violations/references.bib +13 -0
  599. package/vendor/ars/scripts/fixtures/submission_packet_manifest/base_inventory.json +101 -0
  600. package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/consent-materials.txt +3 -0
  601. package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/training-certificate.txt +2 -0
  602. package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/tw-consent-materials.txt +3 -0
  603. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/corpus_input.yaml +30 -0
  604. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/own_draft.md +36 -0
  605. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/own_draft.tex +21 -0
  606. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/seed_expectations.json +218 -0
  607. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/snapshot.json +164 -0
  608. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/snapshot_manifest.json +29 -0
  609. package/vendor/ars/scripts/fixtures/transport_bodies/README.md +55 -0
  610. package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/empty_feed.xml +10 -0
  611. package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/error_5xx.html +7 -0
  612. package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/id_hit.xml +26 -0
  613. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/README.md +69 -0
  614. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/cnki_landing_page.html +13 -0
  615. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/error_5xx.html +2 -0
  616. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_ambiguous.json +16 -0
  617. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_hit.json +16 -0
  618. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_zero.json +14 -0
  619. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coverage_hit.json +15 -0
  620. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coverage_zero.json +13 -0
  621. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_hit.json +43 -0
  622. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_issn_mismatch.json +31 -0
  623. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_no_doi.json +31 -0
  624. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_unknown_ra.json +34 -0
  625. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_year_mismatch.json +31 -0
  626. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_absent.json +4 -0
  627. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_exists.json +16 -0
  628. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_internal_error.json +4 -0
  629. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/istic_csl_hit.json +25 -0
  630. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/istic_csl_other_title.json +18 -0
  631. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_cnki.json +6 -0
  632. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_crossref.json +6 -0
  633. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_istic.json +6 -0
  634. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_unknown_prefix.json +6 -0
  635. package/vendor/ars/scripts/fixtures/transport_bodies/crossref/doi_hit.json +24 -0
  636. package/vendor/ars/scripts/fixtures/transport_bodies/crossref/error_5xx.html +7 -0
  637. package/vendor/ars/scripts/fixtures/transport_bodies/crossref/title_search_miss.json +15 -0
  638. package/vendor/ars/scripts/fixtures/transport_bodies/openalex/doi_hit.json +29 -0
  639. package/vendor/ars/scripts/fixtures/transport_bodies/openalex/error_5xx.json +4 -0
  640. package/vendor/ars/scripts/fixtures/transport_bodies/openalex/title_search_miss.json +11 -0
  641. package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/doi_hit.json +14 -0
  642. package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/error_5xx.json +3 -0
  643. package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/title_search_miss.json +5 -0
  644. package/vendor/ars/scripts/human_read_attestation_resolver.py +467 -0
  645. package/vendor/ars/scripts/ideation_diversity_assignment_gate.py +430 -0
  646. package/vendor/ars/scripts/inquiry_branch_ledger.py +2540 -0
  647. package/vendor/ars/scripts/legacy/ars_apply_revision_patch_v1_0.py +715 -0
  648. package/vendor/ars/scripts/legacy/check_re_review_synthesis_v1_0.py +2215 -0
  649. package/vendor/ars/scripts/migrate_literature_corpus_to_v3_10.py +223 -0
  650. package/vendor/ars/scripts/migrate_literature_corpus_to_v3_7_3.py +277 -0
  651. package/vendor/ars/scripts/migrate_literature_corpus_to_v3_9_0.py +304 -0
  652. package/vendor/ars/scripts/model_tiering_manifest.json +46 -0
  653. package/vendor/ars/scripts/openalex_client.py +232 -0
  654. package/vendor/ars/scripts/parse_audit_verdict.py +802 -0
  655. package/vendor/ars/scripts/pdf_content_classifier_worker.py +176 -0
  656. package/vendor/ars/scripts/pdf_read_preflight.py +1455 -0
  657. package/vendor/ars/scripts/policy_anchor_disclosure_referee.py +354 -0
  658. package/vendor/ars/scripts/recompute_receipts.py +1414 -0
  659. package/vendor/ars/scripts/render_claim_standing_view.py +374 -0
  660. package/vendor/ars/scripts/render_eval_comment.py +130 -0
  661. package/vendor/ars/scripts/render_harness_retirement_issue.py +167 -0
  662. package/vendor/ars/scripts/repro_lock_validation.py +90 -0
  663. package/vendor/ars/scripts/research_workflow_profile.py +1079 -0
  664. package/vendor/ars/scripts/resolve_human_subjects_authority.py +1158 -0
  665. package/vendor/ars/scripts/resolve_review_target_context.py +683 -0
  666. package/vendor/ars/scripts/resume_e4_record.py +509 -0
  667. package/vendor/ars/scripts/retraction_status.py +484 -0
  668. package/vendor/ars/scripts/review_criteria_binding.py +889 -0
  669. package/vendor/ars/scripts/review_panel_provenance.py +744 -0
  670. package/vendor/ars/scripts/revision_roadmap.py +1967 -0
  671. package/vendor/ars/scripts/run_ci_pytest_manifest.py +116 -0
  672. package/vendor/ars/scripts/run_codex_audit.sh +1191 -0
  673. package/vendor/ars/scripts/run_evals.py +513 -0
  674. package/vendor/ars/scripts/run_ideation_diversity_no_call.py +3505 -0
  675. package/vendor/ars/scripts/run_indirect_prompt_injection_no_call.py +3330 -0
  676. package/vendor/ars/scripts/run_indirect_prompt_injection_probe.py +399 -0
  677. package/vendor/ars/scripts/run_review_criteria_constructive_value.py +1895 -0
  678. package/vendor/ars/scripts/run_role_topology_utility_dry_run.py +606 -0
  679. package/vendor/ars/scripts/score_review_criteria_constructive_value.py +617 -0
  680. package/vendor/ars/scripts/semantic_scholar_client.py +291 -0
  681. package/vendor/ars/scripts/slr_lineage.py +59 -0
  682. package/vendor/ars/scripts/sync_adapter_docs.py +118 -0
  683. package/vendor/ars/scripts/temporal_integrity_audit.py +840 -0
  684. package/vendor/ars/scripts/test_431_exact_or_bust.py +253 -0
  685. package/vendor/ars/scripts/test__eval_threshold_gate.py +125 -0
  686. package/vendor/ars/scripts/test__markdown_lint_util.py +129 -0
  687. package/vendor/ars/scripts/test__next_verified_at_ms.py +256 -0
  688. package/vendor/ars/scripts/test_adjacent_framing_probe_lint.py +171 -0
  689. package/vendor/ars/scripts/test_adjudication_activity.py +1515 -0
  690. package/vendor/ars/scripts/test_ars_anchorize_draft.py +178 -0
  691. package/vendor/ars/scripts/test_ars_apply_revision_patch.py +1335 -0
  692. package/vendor/ars/scripts/test_ars_cache_invalidate.py +51 -0
  693. package/vendor/ars/scripts/test_ars_mark_read.py +910 -0
  694. package/vendor/ars/scripts/test_ars_update_check.py +816 -0
  695. package/vendor/ars/scripts/test_ars_write_scope_guard.py +790 -0
  696. package/vendor/ars/scripts/test_arxiv_client.py +374 -0
  697. package/vendor/ars/scripts/test_audit_schemas.py +560 -0
  698. package/vendor/ars/scripts/test_audit_snapshot_render_section_0.py +105 -0
  699. package/vendor/ars/scripts/test_block_parser.py +259 -0
  700. package/vendor/ars/scripts/test_bootstrap_timeline_yaml.py +148 -0
  701. package/vendor/ars/scripts/test_build_claim_standing_candidate_ledger.py +1203 -0
  702. package/vendor/ars/scripts/test_build_claim_standing_query_plan.py +791 -0
  703. package/vendor/ars/scripts/test_build_submission_packet_manifest.py +2644 -0
  704. package/vendor/ars/scripts/test_check_215_field_norm.py +238 -0
  705. package/vendor/ars/scripts/test_check_216_surface_form.py +341 -0
  706. package/vendor/ars/scripts/test_check_268_nested_commitment_ledger.py +194 -0
  707. package/vendor/ars/scripts/test_check_390_revision_patch_discipline.py +279 -0
  708. package/vendor/ars/scripts/test_check_392_citation_verification_intake.py +144 -0
  709. package/vendor/ars/scripts/test_check_394_submission_policy.py +200 -0
  710. package/vendor/ars/scripts/test_check_439_format_profile.py +252 -0
  711. package/vendor/ars/scripts/test_check_619_disclosure_closeout.py +158 -0
  712. package/vendor/ars/scripts/test_check_630_codex_subscription_transport.py +245 -0
  713. package/vendor/ars/scripts/test_check_669_review_pathway_rule_trace.py +304 -0
  714. package/vendor/ars/scripts/test_check_670_revision_roadmap_integration.py +260 -0
  715. package/vendor/ars/scripts/test_check_673_adjudication_activity.py +367 -0
  716. package/vendor/ars/scripts/test_check_684_review_criteria_binding.py +274 -0
  717. package/vendor/ars/scripts/test_check_agents_mirror_sync.py +137 -0
  718. package/vendor/ars/scripts/test_check_audit_artifact_consistency.py +2133 -0
  719. package/vendor/ars/scripts/test_check_benchmark_report.py +117 -0
  720. package/vendor/ars/scripts/test_check_bibliographic_integrity_signals.py +609 -0
  721. package/vendor/ars/scripts/test_check_calibration_tiers.py +376 -0
  722. package/vendor/ars/scripts/test_check_changelog_covers_merges.py +509 -0
  723. package/vendor/ars/scripts/test_check_ci_pytest_manifest.py +425 -0
  724. package/vendor/ars/scripts/test_check_claim_standing_candidate_ledger_integration.py +256 -0
  725. package/vendor/ars/scripts/test_check_claim_standing_freshness.py +277 -0
  726. package/vendor/ars/scripts/test_check_collaboration_depth_rubric.py +239 -0
  727. package/vendor/ars/scripts/test_check_command_frontmatter_name.py +178 -0
  728. package/vendor/ars/scripts/test_check_committee_correspondence.py +298 -0
  729. package/vendor/ars/scripts/test_check_compliance_report.py +381 -0
  730. package/vendor/ars/scripts/test_check_content_coverage_advisory_integration.py +308 -0
  731. package/vendor/ars/scripts/test_check_control_availability.py +361 -0
  732. package/vendor/ars/scripts/test_check_cross_document_consistency_advisory_integration.py +698 -0
  733. package/vendor/ars/scripts/test_check_cross_model_handoff_contract.py +301 -0
  734. package/vendor/ars/scripts/test_check_cross_model_verification_sync.py +203 -0
  735. package/vendor/ars/scripts/test_check_data_access_level.py +227 -0
  736. package/vendor/ars/scripts/test_check_data_flows.py +382 -0
  737. package/vendor/ars/scripts/test_check_decision_contract.py +387 -0
  738. package/vendor/ars/scripts/test_check_degradation_registry.py +270 -0
  739. package/vendor/ars/scripts/test_check_distribution_surface_claims.py +225 -0
  740. package/vendor/ars/scripts/test_check_domain_evidence_profile.py +439 -0
  741. package/vendor/ars/scripts/test_check_e4_promotion.py +163 -0
  742. package/vendor/ars/scripts/test_check_evals_gold_set.py +312 -0
  743. package/vendor/ars/scripts/test_check_evidence_row_integration.py +187 -0
  744. package/vendor/ars/scripts/test_check_field_norm_severity.py +173 -0
  745. package/vendor/ars/scripts/test_check_firm_rules_sync.py +342 -0
  746. package/vendor/ars/scripts/test_check_heldout_measurement_report.py +1508 -0
  747. package/vendor/ars/scripts/test_check_human_subjects_output_contract.py +129 -0
  748. package/vendor/ars/scripts/test_check_human_subjects_reference_migration.py +756 -0
  749. package/vendor/ars/scripts/test_check_instruction_data_boundary.py +204 -0
  750. package/vendor/ars/scripts/test_check_judge_prompt_version.py +90 -0
  751. package/vendor/ars/scripts/test_check_model_tiering.py +236 -0
  752. package/vendor/ars/scripts/test_check_panel_synthesis.py +1658 -0
  753. package/vendor/ars/scripts/test_check_passport_reset_contract.py +249 -0
  754. package/vendor/ars/scripts/test_check_pattern_eval_manifest.py +381 -0
  755. package/vendor/ars/scripts/test_check_persuasion_invariance_fixtures.py +591 -0
  756. package/vendor/ars/scripts/test_check_phase_conformance.py +4366 -0
  757. package/vendor/ars/scripts/test_check_pipeline_boundary_semantics.py +931 -0
  758. package/vendor/ars/scripts/test_check_pipeline_integrity.py +243 -0
  759. package/vendor/ars/scripts/test_check_policy_anchor_protocol.py +236 -0
  760. package/vendor/ars/scripts/test_check_policy_anchor_table.py +295 -0
  761. package/vendor/ars/scripts/test_check_prisma_trAIce_freshness.py +68 -0
  762. package/vendor/ars/scripts/test_check_promotion_bakeoff_preregistration.py +799 -0
  763. package/vendor/ars/scripts/test_check_ranking_lift.py +377 -0
  764. package/vendor/ars/scripts/test_check_re_review_synthesis.py +3398 -0
  765. package/vendor/ars/scripts/test_check_receipt_enum_sync.py +176 -0
  766. package/vendor/ars/scripts/test_check_repro_lock.py +107 -0
  767. package/vendor/ars/scripts/test_check_reviewer_data_fences.py +334 -0
  768. package/vendor/ars/scripts/test_check_reviewer_finding_contract.py +1107 -0
  769. package/vendor/ars/scripts/test_check_reviewer_role_label.py +491 -0
  770. package/vendor/ars/scripts/test_check_reviewer_scoring_honesty.py +222 -0
  771. package/vendor/ars/scripts/test_check_reviewer_sprint_prompt_sync.py +373 -0
  772. package/vendor/ars/scripts/test_check_revision_claim_drift_suite_v2.py +1925 -0
  773. package/vendor/ars/scripts/test_check_revision_token_conservation.py +398 -0
  774. package/vendor/ars/scripts/test_check_risk_register.py +336 -0
  775. package/vendor/ars/scripts/test_check_role_scoped_contract.py +1104 -0
  776. package/vendor/ars/scripts/test_check_rq_framing_patterns.py +110 -0
  777. package/vendor/ars/scripts/test_check_rubric_weight_consistency.py +32 -0
  778. package/vendor/ars/scripts/test_check_seeded_defect_fixtures.py +392 -0
  779. package/vendor/ars/scripts/test_check_setup_cross_model_parity.py +121 -0
  780. package/vendor/ars/scripts/test_check_spec_consistency.py +1081 -0
  781. package/vendor/ars/scripts/test_check_sprint_contract.py +458 -0
  782. package/vendor/ars/scripts/test_check_stage_capability_matrix.py +698 -0
  783. package/vendor/ars/scripts/test_check_submission_packet_manifest_integration.py +177 -0
  784. package/vendor/ars/scripts/test_check_surface_form_parity.py +417 -0
  785. package/vendor/ars/scripts/test_check_task_type.py +116 -0
  786. package/vendor/ars/scripts/test_check_tools_allowlist.py +835 -0
  787. package/vendor/ars/scripts/test_check_tortured_phrase_screening_integration.py +1306 -0
  788. package/vendor/ars/scripts/test_check_v3_10_134_write_scope.py +251 -0
  789. package/vendor/ars/scripts/test_check_v3_10_policy.py +547 -0
  790. package/vendor/ars/scripts/test_check_v3_6_7_pattern_protection.py +960 -0
  791. package/vendor/ars/scripts/test_check_v3_6_8_audit_scope_block.py +1000 -0
  792. package/vendor/ars/scripts/test_check_v3_6_8_cite_provenance_pipeline.py +454 -0
  793. package/vendor/ars/scripts/test_check_v3_6_8_frontmatter_trust_schema.py +581 -0
  794. package/vendor/ars/scripts/test_check_v3_6_8_mark_read_commands.py +120 -0
  795. package/vendor/ars/scripts/test_check_v3_6_8_pattern_protection.py +1138 -0
  796. package/vendor/ars/scripts/test_check_v3_7_3_three_layer_citation.py +566 -0
  797. package/vendor/ars/scripts/test_check_v3_8_annotation_literal_sync.py +262 -0
  798. package/vendor/ars/scripts/test_check_v3_9_0_triangulation.py +321 -0
  799. package/vendor/ars/scripts/test_check_v3_9_2_phase_boundary.py +190 -0
  800. package/vendor/ars/scripts/test_check_v3_9_4_temporal_verification.py +471 -0
  801. package/vendor/ars/scripts/test_check_version_consistency.py +1462 -0
  802. package/vendor/ars/scripts/test_check_workflow_classification.py +225 -0
  803. package/vendor/ars/scripts/test_chinese_literature_client.py +1889 -0
  804. package/vendor/ars/scripts/test_citation_existence_policy.py +480 -0
  805. package/vendor/ars/scripts/test_citation_verification_summary.py +342 -0
  806. package/vendor/ars/scripts/test_claim_audit_calibration.py +882 -0
  807. package/vendor/ars/scripts/test_claim_audit_finalizer.py +979 -0
  808. package/vendor/ars/scripts/test_claim_audit_pipeline.py +2398 -0
  809. package/vendor/ars/scripts/test_claim_audit_schema.py +1778 -0
  810. package/vendor/ars/scripts/test_claim_intent_manifest.py +666 -0
  811. package/vendor/ars/scripts/test_claim_registry_coverage.py +246 -0
  812. package/vendor/ars/scripts/test_claim_standing_discovery.py +579 -0
  813. package/vendor/ars/scripts/test_claim_standing_pipeline_wiring.py +290 -0
  814. package/vendor/ars/scripts/test_claim_standing_stance_assets.py +399 -0
  815. package/vendor/ars/scripts/test_claim_standing_stance_contracts.py +383 -0
  816. package/vendor/ars/scripts/test_claim_standing_stance_runner.py +349 -0
  817. package/vendor/ars/scripts/test_claim_standing_transmissions.py +605 -0
  818. package/vendor/ars/scripts/test_claim_strength_drift_disposition.py +517 -0
  819. package/vendor/ars/scripts/test_claim_verification_coverage_contract.py +104 -0
  820. package/vendor/ars/scripts/test_contamination_signals.py +1086 -0
  821. package/vendor/ars/scripts/test_content_coverage_advisory.py +1767 -0
  822. package/vendor/ars/scripts/test_cross_document_consistency_advisory.py +1270 -0
  823. package/vendor/ars/scripts/test_cross_model_codex_transport.py +1152 -0
  824. package/vendor/ars/scripts/test_cross_model_handoff.py +571 -0
  825. package/vendor/ars/scripts/test_cross_model_verification_guards.py +792 -0
  826. package/vendor/ars/scripts/test_crossref_client.py +393 -0
  827. package/vendor/ars/scripts/test_dispatch_e4_panel.py +4235 -0
  828. package/vendor/ars/scripts/test_e2e_claim_audit.py +540 -0
  829. package/vendor/ars/scripts/test_eval_harness_workflow.py +140 -0
  830. package/vendor/ars/scripts/test_evals_citation_extraction.py +150 -0
  831. package/vendor/ars/scripts/test_evals_lift_report_schema.py +108 -0
  832. package/vendor/ars/scripts/test_evidence_rows.py +2593 -0
  833. package/vendor/ars/scripts/test_experiment_provenance.py +915 -0
  834. package/vendor/ars/scripts/test_human_read_attestation_resolver.py +477 -0
  835. package/vendor/ars/scripts/test_ideation_diversity_assignment_gate.py +615 -0
  836. package/vendor/ars/scripts/test_indirect_prompt_injection_behavior_probe.py +250 -0
  837. package/vendor/ars/scripts/test_inquiry_branch_ledger.py +2296 -0
  838. package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_10.py +248 -0
  839. package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_7_3.py +545 -0
  840. package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_9_0.py +497 -0
  841. package/vendor/ars/scripts/test_normalize_compat_verdict.py +149 -0
  842. package/vendor/ars/scripts/test_openalex_client.py +490 -0
  843. package/vendor/ars/scripts/test_passport_yaml.py +104 -0
  844. package/vendor/ars/scripts/test_pattern_eval_runtime.py +1295 -0
  845. package/vendor/ars/scripts/test_pdf_read_preflight.py +1943 -0
  846. package/vendor/ars/scripts/test_policy_anchor_disclosure.py +666 -0
  847. package/vendor/ars/scripts/test_reading_probe_lint.py +218 -0
  848. package/vendor/ars/scripts/test_recompute_receipts.py +778 -0
  849. package/vendor/ars/scripts/test_render_claim_standing_view.py +192 -0
  850. package/vendor/ars/scripts/test_render_eval_comment.py +162 -0
  851. package/vendor/ars/scripts/test_render_harness_retirement_issue.py +110 -0
  852. package/vendor/ars/scripts/test_repro_lock_validation_drift.py +100 -0
  853. package/vendor/ars/scripts/test_research_workflow_profile.py +734 -0
  854. package/vendor/ars/scripts/test_resolve_human_subjects_authority.py +1219 -0
  855. package/vendor/ars/scripts/test_resolve_review_target_context.py +703 -0
  856. package/vendor/ars/scripts/test_resume_e4_record.py +315 -0
  857. package/vendor/ars/scripts/test_retraction_status.py +456 -0
  858. package/vendor/ars/scripts/test_review_criteria_binding.py +629 -0
  859. package/vendor/ars/scripts/test_review_panel_provenance.py +565 -0
  860. package/vendor/ars/scripts/test_review_pathway_rule_trace.py +821 -0
  861. package/vendor/ars/scripts/test_revision_roadmap.py +1255 -0
  862. package/vendor/ars/scripts/test_run_ci_pytest_manifest.py +186 -0
  863. package/vendor/ars/scripts/test_run_codex_audit_e2e.py +369 -0
  864. package/vendor/ars/scripts/test_run_evals.py +430 -0
  865. package/vendor/ars/scripts/test_run_guard_launcher.py +500 -0
  866. package/vendor/ars/scripts/test_run_ideation_diversity_no_call.py +1833 -0
  867. package/vendor/ars/scripts/test_run_indirect_prompt_injection_no_call.py +1889 -0
  868. package/vendor/ars/scripts/test_run_review_criteria_constructive_value.py +586 -0
  869. package/vendor/ars/scripts/test_run_role_topology_utility_dry_run.py +428 -0
  870. package/vendor/ars/scripts/test_score_review_criteria_constructive_value.py +340 -0
  871. package/vendor/ars/scripts/test_semantic_scholar_client.py +554 -0
  872. package/vendor/ars/scripts/test_slr_lineage_emission.py +230 -0
  873. package/vendor/ars/scripts/test_socratic_rq_non_generation_contract.py +173 -0
  874. package/vendor/ars/scripts/test_temporal_integrity_audit.py +438 -0
  875. package/vendor/ars/scripts/test_text_similarity.py +95 -0
  876. package/vendor/ars/scripts/test_title_fuzzy_false_positive.py +111 -0
  877. package/vendor/ars/scripts/test_tortured_phrase_screening.py +2959 -0
  878. package/vendor/ars/scripts/test_transport_fixture_citation_gate.py +338 -0
  879. package/vendor/ars/scripts/test_uncited_assertion.py +558 -0
  880. package/vendor/ars/scripts/test_v3_6_7_phase_6_6.py +1279 -0
  881. package/vendor/ars/scripts/test_validate_compliance_fixtures.py +36 -0
  882. package/vendor/ars/scripts/test_validate_ideation_diversity_assets.py +230 -0
  883. package/vendor/ars/scripts/test_venue_disclosure_contract.py +755 -0
  884. package/vendor/ars/scripts/test_verification_cache.py +280 -0
  885. package/vendor/ars/scripts/test_verification_gate.py +461 -0
  886. package/vendor/ars/scripts/test_verify_passport_cli.py +123 -0
  887. package/vendor/ars/scripts/test_verify_submission_package.py +1407 -0
  888. package/vendor/ars/scripts/test_version_records_schema.py +211 -0
  889. package/vendor/ars/scripts/tortured_phrase_screening.py +3502 -0
  890. package/vendor/ars/scripts/uncited_assertion_detector.py +254 -0
  891. package/vendor/ars/scripts/v3_6_7_inversion_manifest.json +9 -0
  892. package/vendor/ars/scripts/v3_6_8_inversion_manifest.json +10 -0
  893. package/vendor/ars/scripts/validate_claim_standing_stance_assets.py +249 -0
  894. package/vendor/ars/scripts/validate_compliance_fixtures.py +56 -0
  895. package/vendor/ars/scripts/validate_ideation_diversity_assets.py +303 -0
  896. package/vendor/ars/scripts/venue_disclosure_contract_harness.py +837 -0
  897. package/vendor/ars/scripts/verification_cache.py +276 -0
  898. package/vendor/ars/scripts/verification_gate/__init__.py +345 -0
  899. package/vendor/ars/scripts/verify_passport.py +133 -0
  900. package/vendor/ars/scripts/verify_submission_package.py +1657 -0
  901. package/vendor/ars/shared/agents/compliance_agent.md +136 -0
  902. package/vendor/ars/shared/artifact_reproducibility_pattern.md +173 -0
  903. package/vendor/ars/shared/benchmark_report.schema.json +81 -0
  904. package/vendor/ars/shared/benchmark_report_pattern.md +180 -0
  905. package/vendor/ars/shared/bibliographic_integrity_signals.md +142 -0
  906. package/vendor/ars/shared/collaboration_depth_rubric.md +154 -0
  907. package/vendor/ars/shared/compliance_checkpoint_protocol.md +162 -0
  908. package/vendor/ars/shared/compliance_report.schema.json +187 -0
  909. package/vendor/ars/shared/contracts/README.md +938 -0
  910. package/vendor/ars/shared/contracts/activity/adjudication_activity_input.schema.json +555 -0
  911. package/vendor/ars/shared/contracts/activity/adjudication_activity_store.schema.json +560 -0
  912. package/vendor/ars/shared/contracts/audit/audit_jsonl.schema.json +128 -0
  913. package/vendor/ars/shared/contracts/audit/audit_sidecar.schema.json +169 -0
  914. package/vendor/ars/shared/contracts/audit/audit_verdict.schema.json +133 -0
  915. package/vendor/ars/shared/contracts/audit/cross_document_consistency_advisory.schema.json +480 -0
  916. package/vendor/ars/shared/contracts/audit/cross_document_consistency_advisory_draft.schema.json +564 -0
  917. package/vendor/ars/shared/contracts/audit/cross_document_source_manifest.schema.json +202 -0
  918. package/vendor/ars/shared/contracts/audit/tortured_phrase_advisory.schema.json +1362 -0
  919. package/vendor/ars/shared/contracts/audit/tortured_phrase_snapshot.schema.json +208 -0
  920. package/vendor/ars/shared/contracts/audit/tortured_phrase_snapshot_manifest.schema.json +335 -0
  921. package/vendor/ars/shared/contracts/capability/stage_capability_matrix.json +434 -0
  922. package/vendor/ars/shared/contracts/claim_standing/candidate_ledger.schema.json +705 -0
  923. package/vendor/ars/shared/contracts/claim_standing/query_plan.schema.json +389 -0
  924. package/vendor/ars/shared/contracts/claim_standing/query_plan_v1_1.schema.json +782 -0
  925. package/vendor/ars/shared/contracts/claim_standing/retrieval_input.schema.json +440 -0
  926. package/vendor/ars/shared/contracts/claim_standing/stance_record.schema.json +460 -0
  927. package/vendor/ars/shared/contracts/claim_standing/transmission_ledger.schema.json +282 -0
  928. package/vendor/ars/shared/contracts/cross_model/codex_citation_receipt.schema.json +157 -0
  929. package/vendor/ars/shared/contracts/cross_model/codex_citation_request.schema.json +23 -0
  930. package/vendor/ars/shared/contracts/cross_model/promotion_bakeoff_sealed_commitment.schema.json +55 -0
  931. package/vendor/ars/shared/contracts/cross_model/promotion_bakeoff_sealed_reveal.schema.json +50 -0
  932. package/vendor/ars/shared/contracts/degradation_registry.json +428 -0
  933. package/vendor/ars/shared/contracts/evaluator/full.json +126 -0
  934. package/vendor/ars/shared/contracts/evidence/claim_registry.schema.json +50 -0
  935. package/vendor/ars/shared/contracts/evidence/claim_registry_coverage_report.schema.json +79 -0
  936. package/vendor/ars/shared/contracts/evidence/evidence_row.schema.json +504 -0
  937. package/vendor/ars/shared/contracts/evidence/evidence_row_v1_1.schema.json +364 -0
  938. package/vendor/ars/shared/contracts/evidence/evidence_row_v1_2.schema.json +714 -0
  939. package/vendor/ars/shared/contracts/evidence/evidence_row_v1_3.schema.json +524 -0
  940. package/vendor/ars/shared/contracts/human_subjects/authority_profile_registry.schema.json +497 -0
  941. package/vendor/ars/shared/contracts/human_subjects/committee_correspondence.schema.json +253 -0
  942. package/vendor/ars/shared/contracts/human_subjects/content_coverage_advisory.schema.json +620 -0
  943. package/vendor/ars/shared/contracts/human_subjects/irb_context_record.schema.json +329 -0
  944. package/vendor/ars/shared/contracts/human_subjects/resolved_authority_context.schema.json +347 -0
  945. package/vendor/ars/shared/contracts/human_subjects/review_pathway_rule_trace.schema.json +265 -0
  946. package/vendor/ars/shared/contracts/human_subjects/review_pathway_trace_request.schema.json +127 -0
  947. package/vendor/ars/shared/contracts/human_subjects/submission_packet_inventory.schema.json +240 -0
  948. package/vendor/ars/shared/contracts/human_subjects/submission_packet_manifest.schema.json +999 -0
  949. package/vendor/ars/shared/contracts/passport/audit_artifact_entry.schema.json +266 -0
  950. package/vendor/ars/shared/contracts/passport/bibliographic_integrity_signal.schema.json +1676 -0
  951. package/vendor/ars/shared/contracts/passport/citation_provenance.schema.json +107 -0
  952. package/vendor/ars/shared/contracts/passport/citation_verification_summary.schema.json +164 -0
  953. package/vendor/ars/shared/contracts/passport/claim_audit_result.schema.json +124 -0
  954. package/vendor/ars/shared/contracts/passport/claim_drift.schema.json +58 -0
  955. package/vendor/ars/shared/contracts/passport/claim_intent_manifest.schema.json +107 -0
  956. package/vendor/ars/shared/contracts/passport/constraint_violation.schema.json +65 -0
  957. package/vendor/ars/shared/contracts/passport/experiment_alignment_result.schema.json +69 -0
  958. package/vendor/ars/shared/contracts/passport/experiment_provenance_entry.schema.json +169 -0
  959. package/vendor/ars/shared/contracts/passport/human_read_log.schema.json +86 -0
  960. package/vendor/ars/shared/contracts/passport/inquiry_ledger_ref.schema.json +22 -0
  961. package/vendor/ars/shared/contracts/passport/literature_corpus_entry.schema.json +650 -0
  962. package/vendor/ars/shared/contracts/passport/preregistration_artifact.schema.json +132 -0
  963. package/vendor/ars/shared/contracts/passport/rejection_log.schema.json +89 -0
  964. package/vendor/ars/shared/contracts/passport/reset_ledger_entry.schema.json +158 -0
  965. package/vendor/ars/shared/contracts/passport/temporal_audit_results.schema.json +208 -0
  966. package/vendor/ars/shared/contracts/passport/terminal_policies.schema.json +50 -0
  967. package/vendor/ars/shared/contracts/passport/timeline.schema.json +102 -0
  968. package/vendor/ars/shared/contracts/passport/uncited_assertion.schema.json +56 -0
  969. package/vendor/ars/shared/contracts/passport/uncited_audit_failure.schema.json +72 -0
  970. package/vendor/ars/shared/contracts/passport/user_attested_read_resolution.schema.json +87 -0
  971. package/vendor/ars/shared/contracts/passport/version_records.schema.json +138 -0
  972. package/vendor/ars/shared/contracts/patch/block_manifest.schema.json +45 -0
  973. package/vendor/ars/shared/contracts/patch/legacy/v1_0/revision_patch.schema.json +110 -0
  974. package/vendor/ars/shared/contracts/patch/revision_patch.schema.json +250 -0
  975. package/vendor/ars/shared/contracts/pdf/pdf_content_classifier_diagnostic.schema.json +39 -0
  976. package/vendor/ars/shared/contracts/pdf/pdf_content_classifier_worker.schema.json +88 -0
  977. package/vendor/ars/shared/contracts/pdf/pdf_read_preflight.schema.json +227 -0
  978. package/vendor/ars/shared/contracts/re_review/input_manifest.schema.json +161 -0
  979. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/input_manifest.schema.json +134 -0
  980. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/precommitment.schema.json +193 -0
  981. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/traceability.schema.json +841 -0
  982. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/verdict_record.schema.json +244 -0
  983. package/vendor/ars/shared/contracts/re_review/precommitment.schema.json +193 -0
  984. package/vendor/ars/shared/contracts/re_review/traceability.schema.json +916 -0
  985. package/vendor/ars/shared/contracts/re_review/verdict_record.schema.json +245 -0
  986. package/vendor/ars/shared/contracts/research_workflow/inquiry_branch_ledger.schema.json +449 -0
  987. package/vendor/ars/shared/contracts/research_workflow/research_workflow_profile.schema.json +222 -0
  988. package/vendor/ars/shared/contracts/research_workflow/research_workflow_profile_selection_receipt.schema.json +111 -0
  989. package/vendor/ars/shared/contracts/review_target/constructive_review_findings.schema.json +172 -0
  990. package/vendor/ars/shared/contracts/review_target/criteria_registry.schema.json +97 -0
  991. package/vendor/ars/shared/contracts/review_target/review_criteria_binding_manifest.schema.json +235 -0
  992. package/vendor/ars/shared/contracts/review_target/review_criteria_source_receipt.schema.json +158 -0
  993. package/vendor/ars/shared/contracts/review_target/review_target_context.schema.json +106 -0
  994. package/vendor/ars/shared/contracts/review_target/review_target_declaration.schema.json +118 -0
  995. package/vendor/ars/shared/contracts/reviewer/full.json +114 -0
  996. package/vendor/ars/shared/contracts/reviewer/methodology_focus.json +75 -0
  997. package/vendor/ars/shared/contracts/reviewer/review_panel_provenance.schema.json +263 -0
  998. package/vendor/ars/shared/contracts/reviewer/review_panel_provenance_carrier.schema.json +122 -0
  999. package/vendor/ars/shared/contracts/reviewer/review_panel_provenance_input.schema.json +120 -0
  1000. package/vendor/ars/shared/contracts/revision/author_adjudication.schema.json +251 -0
  1001. package/vendor/ars/shared/contracts/revision/author_adjudication_input.schema.json +25 -0
  1002. package/vendor/ars/shared/contracts/revision/claim_strength_drift_disposition.schema.json +97 -0
  1003. package/vendor/ars/shared/contracts/revision/claim_strength_drift_disposition_input.schema.json +80 -0
  1004. package/vendor/ars/shared/contracts/revision/claim_strength_drift_findings.schema.json +139 -0
  1005. package/vendor/ars/shared/contracts/revision/claim_surface_manifest.schema.json +99 -0
  1006. package/vendor/ars/shared/contracts/revision/integrity_correction_authorization.schema.json +140 -0
  1007. package/vendor/ars/shared/contracts/revision/integrity_correction_authorization_input.schema.json +28 -0
  1008. package/vendor/ars/shared/contracts/revision/integrity_correction_list.schema.json +48 -0
  1009. package/vendor/ars/shared/contracts/revision/integrity_pass_receipt.schema.json +16 -0
  1010. package/vendor/ars/shared/contracts/revision/revision_evidence_bundle.schema.json +134 -0
  1011. package/vendor/ars/shared/contracts/revision/revision_roadmap.schema.json +334 -0
  1012. package/vendor/ars/shared/contracts/submission/format_profile.example.yaml +33 -0
  1013. package/vendor/ars/shared/contracts/submission/format_profile.schema.json +102 -0
  1014. package/vendor/ars/shared/contracts/submission/submission_verification_report.schema.json +233 -0
  1015. package/vendor/ars/shared/contracts/submission/venue_profile.schema.json +114 -0
  1016. package/vendor/ars/shared/contracts/writer/full.json +87 -0
  1017. package/vendor/ars/shared/cross_model_verification.md +714 -0
  1018. package/vendor/ars/shared/evals_lift_report.schema.json +141 -0
  1019. package/vendor/ars/shared/ground_truth_isolation_pattern.md +275 -0
  1020. package/vendor/ars/shared/handoff_schemas.md +1209 -0
  1021. package/vendor/ars/shared/human_subjects_authority_registry.json +1278 -0
  1022. package/vendor/ars/shared/mode_spectrum.md +57 -0
  1023. package/vendor/ars/shared/model_tiering.md +83 -0
  1024. package/vendor/ars/shared/policy_data/nature_policy.md +56 -0
  1025. package/vendor/ars/shared/prisma_trAIce_protocol.md +157 -0
  1026. package/vendor/ars/shared/raise_framework.md +129 -0
  1027. package/vendor/ars/shared/references/authority_content_coverage_advisory_protocol.md +275 -0
  1028. package/vendor/ars/shared/references/claim_standing_candidate_ledger_protocol.md +66 -0
  1029. package/vendor/ars/shared/references/claim_strength_ladder.md +93 -0
  1030. package/vendor/ars/shared/references/cross_document_consistency_advisory_protocol.md +263 -0
  1031. package/vendor/ars/shared/references/evidence_row_protocol.md +260 -0
  1032. package/vendor/ars/shared/references/firm_rules.md +90 -0
  1033. package/vendor/ars/shared/references/human_subjects_authority_protocol.md +274 -0
  1034. package/vendor/ars/shared/references/intent_clarification_protocol.md +168 -0
  1035. package/vendor/ars/shared/references/irb_terminology_glossary.md +229 -0
  1036. package/vendor/ars/shared/references/protected_hedging_phrases.md +118 -0
  1037. package/vendor/ars/shared/references/psychometric_terminology_glossary.md +109 -0
  1038. package/vendor/ars/shared/references/review_criteria_consumer_protocol.md +238 -0
  1039. package/vendor/ars/shared/references/review_pathway_rule_trace_protocol.md +166 -0
  1040. package/vendor/ars/shared/references/submission_packet_manifest_protocol.md +292 -0
  1041. package/vendor/ars/shared/references/word_count_conventions.md +124 -0
  1042. package/vendor/ars/shared/research_workflow_profiles/field_general.json +1 -0
  1043. package/vendor/ars/shared/review_criteria_registry.json +207 -0
  1044. package/vendor/ars/shared/review_criteria_sources/msr-2027-technical-papers.2026-08-24.json +46 -0
  1045. package/vendor/ars/shared/review_criteria_sources/sigsoft-empirical-standards.2026-08-24.json +31 -0
  1046. package/vendor/ars/shared/sprint_contract.schema.json +482 -0
  1047. package/vendor/ars/shared/style_calibration_protocol.md +151 -0
  1048. package/vendor/ars/shared/templates/codex_audit_multifile_template.md +263 -0
  1049. package/vendor/ars/tools/release-discipline/.toolkit-version +1 -0
  1050. package/vendor/ars/tools/release-discipline/README.md +4 -0
  1051. package/vendor/ars/tools/release-discipline/scripts/_release_doc_alignment_schema.py +1011 -0
  1052. package/vendor/ars/tools/release-discipline/scripts/check_command_invariants.py +497 -0
  1053. package/vendor/ars/tools/release-discipline/scripts/check_release_doc_alignment.py +263 -0
  1054. package/vendor/ars/tools/release-discipline/scripts/sync-toolkit.sh +147 -0
  1055. package/vendor/windows/NOTICE.md +12 -0
  1056. package/vendor/windows/arm64/fd.exe +0 -0
  1057. package/vendor/windows/arm64/licenses/fd/LICENSE-APACHE +201 -0
  1058. package/vendor/windows/arm64/licenses/fd/LICENSE-MIT +21 -0
  1059. package/vendor/windows/arm64/licenses/ripgrep/COPYING +3 -0
  1060. package/vendor/windows/arm64/licenses/ripgrep/LICENSE-MIT +21 -0
  1061. package/vendor/windows/arm64/licenses/ripgrep/UNLICENSE +24 -0
  1062. package/vendor/windows/arm64/rg.exe +0 -0
  1063. package/vendor/windows/x64/fd.exe +0 -0
  1064. package/vendor/windows/x64/licenses/fd/LICENSE-APACHE +201 -0
  1065. package/vendor/windows/x64/licenses/fd/LICENSE-MIT +21 -0
  1066. package/vendor/windows/x64/licenses/ripgrep/COPYING +3 -0
  1067. package/vendor/windows/x64/licenses/ripgrep/LICENSE-MIT +21 -0
  1068. package/vendor/windows/x64/licenses/ripgrep/UNLICENSE +24 -0
  1069. package/vendor/windows/x64/rg.exe +0 -0
@@ -0,0 +1,1664 @@
1
+ #!/usr/bin/env python3
2
+ """check_claim_audit_consistency.py — ARS v3.8 claim-faithfulness audit lint.
3
+
4
+ Enforces the 39 cross-field invariants spanning the six aggregates that
5
+ claim_ref_alignment_audit_agent populates:
6
+
7
+ claim_audit_results[] — INV-1..INV-19 (§3.1)
8
+ claim_intent_manifests[] — M-INV-1..M-INV-4 (§3.2)
9
+ uncited_assertions[] — U-INV-1..U-INV-4 (§3.3)
10
+ claim_drifts[] — D-INV-1..D-INV-4 (§3.4)
11
+ constraint_violations[] — CV-INV-1..CV-INV-4 (§3.5)
12
+ audit_sampling_summaries[] — S-INV-1..S-INV-4 (§4 step 3)
13
+
14
+ Plus the §3.1 allowed (judgment, audit_status, defect_stage) matrix, and
15
+ schema-shape validation for every entry in each aggregate against the
16
+ corresponding shipped JSON Schema.
17
+
18
+ Cross-field invariants are intentionally NOT expressed in JSON Schema —
19
+ the matrix is conditional and the cross-array (scoped_manifest_id, *)
20
+ integrity checks span aggregates. This lint is the contract under test
21
+ in scripts/test_claim_audit_schema.py.
22
+
23
+ CLI:
24
+ python3 scripts/check_claim_audit_consistency.py --passport <path>
25
+
26
+ Exit codes:
27
+ 0 no findings
28
+ 1 one or more invariant violations (printed to stdout, one per line)
29
+ 2 internal error (passport not found / malformed JSON / schema not found)
30
+
31
+ See docs/design/2026-05-15-issue-103-claim-alignment-audit-spec.md
32
+ §3.1-§3.5 and §6 for the full invariant catalogue.
33
+ """
34
+ from __future__ import annotations
35
+
36
+ import argparse
37
+ import hashlib
38
+ import json
39
+ import sys
40
+ import urllib.parse
41
+ from dataclasses import dataclass
42
+ from pathlib import Path
43
+ from typing import Any, Iterable
44
+
45
+ from jsonschema import Draft202012Validator
46
+
47
+ # Allow both CLI invocations (`python3 scripts/check_claim_audit_consistency.py`)
48
+ # AND package-style invocations (`python -m unittest scripts.test_*`) to resolve
49
+ # the shared constants module. The CLI path puts scripts/ on sys.path, the
50
+ # unittest path puts repo root on sys.path — the insert below covers the gap.
51
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
52
+ from _claim_audit_constants import ( # noqa: E402
53
+ INV6_RATIONALE_PREFIX,
54
+ INV14_FAULT_CLASS_TAGS,
55
+ RE_CLAIM_ID,
56
+ RE_MNC_CONSTRAINT,
57
+ RE_NC_CONSTRAINT,
58
+ RE_NC_INNER_HYPHEN,
59
+ SENTINEL_MANIFEST_ID,
60
+ SUBCLAIM_NON_SUPPORTED,
61
+ )
62
+
63
+ REPO_ROOT = Path(__file__).resolve().parent.parent
64
+ PASSPORT_SCHEMAS = REPO_ROOT / "shared/contracts/passport"
65
+
66
+ SCHEMA_FILES = {
67
+ "claim_audit_result": PASSPORT_SCHEMAS / "claim_audit_result.schema.json",
68
+ "claim_intent_manifest": PASSPORT_SCHEMAS / "claim_intent_manifest.schema.json",
69
+ "uncited_assertion": PASSPORT_SCHEMAS / "uncited_assertion.schema.json",
70
+ "claim_drift": PASSPORT_SCHEMAS / "claim_drift.schema.json",
71
+ "constraint_violation": PASSPORT_SCHEMAS / "constraint_violation.schema.json",
72
+ "uncited_audit_failure": PASSPORT_SCHEMAS / "uncited_audit_failure.schema.json",
73
+ "experiment_provenance_entry": PASSPORT_SCHEMAS / "experiment_provenance_entry.schema.json",
74
+ "experiment_alignment_result": PASSPORT_SCHEMAS / "experiment_alignment_result.schema.json",
75
+ }
76
+
77
+ # Inline schema for audit_sampling_summary (spec §4 step 3) — not a shipped file.
78
+ AUDIT_SAMPLING_SUMMARY_SCHEMA: dict[str, Any] = {
79
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
80
+ "type": "object",
81
+ "additionalProperties": False,
82
+ "required": [
83
+ "audit_run_id",
84
+ "max_claims_per_paper",
85
+ "total_citation_count",
86
+ "audited_count",
87
+ "audited_indices",
88
+ "sampling_strategy",
89
+ "emitted_at",
90
+ ],
91
+ "properties": {
92
+ "audit_run_id": {
93
+ "type": "string",
94
+ "pattern": r"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z-[0-9a-f]{4}$",
95
+ },
96
+ "max_claims_per_paper": {"type": "integer", "minimum": 1},
97
+ "total_citation_count": {"type": "integer", "minimum": 0},
98
+ "audited_count": {"type": "integer", "minimum": 0},
99
+ "audited_indices": {
100
+ "type": "array",
101
+ "items": {"type": "integer", "minimum": 0},
102
+ },
103
+ "sampling_strategy": {"const": "stratified_buckets_v1"},
104
+ "emitted_at": {"type": "string", "format": "date-time"},
105
+ },
106
+ }
107
+
108
+ # Allowed (judgment, audit_status, defect_stage) triples — §3.1 table.
109
+ # Negative-constraint-violation rows still permit any ref_retrieval_method
110
+ # in this matrix; INV-7/INV-8 further restrict the surrounding shape.
111
+ ALLOWED_MATRIX: set[tuple[str, str, Any]] = {
112
+ ("SUPPORTED", "completed", None),
113
+ ("AMBIGUOUS", "completed", "source_description"),
114
+ ("AMBIGUOUS", "completed", "citation_anchor"),
115
+ ("AMBIGUOUS", "completed", "synthesis_overclaim"),
116
+ ("AMBIGUOUS", "completed", None),
117
+ ("UNSUPPORTED", "completed", "source_description"),
118
+ ("UNSUPPORTED", "completed", "metadata"),
119
+ ("UNSUPPORTED", "completed", "citation_anchor"),
120
+ ("UNSUPPORTED", "completed", "synthesis_overclaim"),
121
+ ("UNSUPPORTED", "completed", "negative_constraint_violation"),
122
+ ("RETRIEVAL_FAILED", "completed", "retrieval_existence"),
123
+ ("RETRIEVAL_FAILED", "inconclusive", "not_applicable"),
124
+ }
125
+
126
+ @dataclass(frozen=True)
127
+ class Finding:
128
+ invariant: str
129
+ detail: str
130
+
131
+ def render(self) -> str:
132
+ return f"{self.invariant}: {self.detail}"
133
+
134
+
135
+ # ---------------------------------------------------------------------------
136
+ # Schema loaders (cached once per process to amortise check_schema cost).
137
+ # ---------------------------------------------------------------------------
138
+
139
+ _VALIDATORS: dict[str, Draft202012Validator] = {}
140
+
141
+
142
+ def _validator(name: str) -> Draft202012Validator:
143
+ if name not in _VALIDATORS:
144
+ if name == "audit_sampling_summary":
145
+ schema = AUDIT_SAMPLING_SUMMARY_SCHEMA
146
+ else:
147
+ path = SCHEMA_FILES[name]
148
+ if not path.is_file():
149
+ raise FileNotFoundError(f"schema missing: {path}")
150
+ schema = json.loads(path.read_text(encoding="utf-8"))
151
+ Draft202012Validator.check_schema(schema)
152
+ _VALIDATORS[name] = Draft202012Validator(
153
+ schema, format_checker=Draft202012Validator.FORMAT_CHECKER
154
+ )
155
+ return _VALIDATORS[name]
156
+
157
+
158
+ # ---------------------------------------------------------------------------
159
+ # claim_audit_result invariants (INV-1..INV-19).
160
+ # Each helper returns a list of Findings; the caller aggregates across rows.
161
+ # ---------------------------------------------------------------------------
162
+
163
+
164
+ def _check_inv_1(e: dict[str, Any]) -> list[Finding]:
165
+ """SUPPORTED -> defect_stage=null AND violated_constraint_id=null AND completed."""
166
+ if e.get("judgment") != "SUPPORTED":
167
+ return []
168
+ findings: list[Finding] = []
169
+ if e.get("defect_stage") is not None:
170
+ findings.append(Finding("INV-1", f"SUPPORTED row has non-null defect_stage={e['defect_stage']!r}"))
171
+ if e.get("violated_constraint_id") not in (None, ""):
172
+ findings.append(Finding("INV-1", "SUPPORTED row has non-null violated_constraint_id"))
173
+ if e.get("audit_status") != "completed":
174
+ findings.append(Finding("INV-1", f"SUPPORTED row has audit_status={e.get('audit_status')!r}"))
175
+ return findings
176
+
177
+
178
+ def _check_inv_2(e: dict[str, Any]) -> list[Finding]:
179
+ """UNSUPPORTED -> defect_stage in 5-set AND completed."""
180
+ if e.get("judgment") != "UNSUPPORTED":
181
+ return []
182
+ allowed = {
183
+ "source_description",
184
+ "metadata",
185
+ "citation_anchor",
186
+ "synthesis_overclaim",
187
+ "negative_constraint_violation",
188
+ }
189
+ findings: list[Finding] = []
190
+ if e.get("defect_stage") not in allowed:
191
+ findings.append(
192
+ Finding(
193
+ "INV-2",
194
+ f"UNSUPPORTED row has defect_stage={e.get('defect_stage')!r}; must be one of {sorted(allowed)}",
195
+ )
196
+ )
197
+ if e.get("audit_status") != "completed":
198
+ findings.append(Finding("INV-2", f"UNSUPPORTED row has audit_status={e.get('audit_status')!r}"))
199
+ return findings
200
+
201
+
202
+ def _check_inv_3(e: dict[str, Any]) -> list[Finding]:
203
+ """AMBIGUOUS -> defect_stage in {source_description, citation_anchor, synthesis_overclaim, None}."""
204
+ if e.get("judgment") != "AMBIGUOUS":
205
+ return []
206
+ allowed = {"source_description", "citation_anchor", "synthesis_overclaim", None}
207
+ findings: list[Finding] = []
208
+ if e.get("defect_stage") not in allowed:
209
+ findings.append(
210
+ Finding(
211
+ "INV-3",
212
+ f"AMBIGUOUS row has defect_stage={e.get('defect_stage')!r}; must be one of "
213
+ f"{sorted([x for x in allowed if x is not None]) + ['null']}",
214
+ )
215
+ )
216
+ if e.get("audit_status") != "completed":
217
+ findings.append(Finding("INV-3", f"AMBIGUOUS row has audit_status={e.get('audit_status')!r}"))
218
+ return findings
219
+
220
+
221
+ def _check_inv_4(e: dict[str, Any]) -> list[Finding]:
222
+ """RETRIEVAL_FAILED + inconclusive -> defect_stage=not_applicable."""
223
+ if e.get("judgment") != "RETRIEVAL_FAILED":
224
+ return []
225
+ if e.get("audit_status") != "inconclusive":
226
+ return []
227
+ if e.get("defect_stage") != "not_applicable":
228
+ return [
229
+ Finding(
230
+ "INV-4",
231
+ f"RETRIEVAL_FAILED+inconclusive row has defect_stage={e.get('defect_stage')!r}; must be not_applicable",
232
+ )
233
+ ]
234
+ return []
235
+
236
+
237
+ def _check_inv_5(e: dict[str, Any]) -> list[Finding]:
238
+ """RETRIEVAL_FAILED + completed -> defect_stage=retrieval_existence."""
239
+ if e.get("judgment") != "RETRIEVAL_FAILED":
240
+ return []
241
+ if e.get("audit_status") != "completed":
242
+ return []
243
+ if e.get("defect_stage") != "retrieval_existence":
244
+ return [
245
+ Finding(
246
+ "INV-5",
247
+ f"RETRIEVAL_FAILED+completed row has defect_stage={e.get('defect_stage')!r}; must be retrieval_existence",
248
+ )
249
+ ]
250
+ return []
251
+
252
+
253
+ def _check_inv_6(e: dict[str, Any]) -> list[Finding]:
254
+ """anchor_kind=none firm rule (D1)."""
255
+ if e.get("anchor_kind") != "none":
256
+ return []
257
+ findings: list[Finding] = []
258
+ if e.get("judgment") != "RETRIEVAL_FAILED":
259
+ findings.append(Finding("INV-6", f"anchor=none row has judgment={e.get('judgment')!r}; must be RETRIEVAL_FAILED"))
260
+ if e.get("audit_status") != "inconclusive":
261
+ findings.append(Finding("INV-6", f"anchor=none row has audit_status={e.get('audit_status')!r}; must be inconclusive"))
262
+ if e.get("defect_stage") != "not_applicable":
263
+ findings.append(Finding("INV-6", f"anchor=none row has defect_stage={e.get('defect_stage')!r}; must be not_applicable"))
264
+ if e.get("ref_retrieval_method") != "not_attempted":
265
+ findings.append(
266
+ Finding(
267
+ "INV-6",
268
+ f"anchor=none row has ref_retrieval_method={e.get('ref_retrieval_method')!r}; must be not_attempted",
269
+ )
270
+ )
271
+ # Schema mandates the empty sentinel for anchor_value when anchor_kind=none.
272
+ # A residual stale anchor (e.g. "123") here violates the schema contract;
273
+ # pipeline _anchorless_entry pins "" but the lint must enforce it for
274
+ # passports authored outside the pipeline (manual fixtures, replays).
275
+ if e.get("anchor_value", "") != "":
276
+ findings.append(
277
+ Finding(
278
+ "INV-6",
279
+ f"anchor=none row has non-empty anchor_value={e.get('anchor_value')!r}; must be empty sentinel string",
280
+ )
281
+ )
282
+ rationale = e.get("rationale") or ""
283
+ if not rationale.startswith(INV6_RATIONALE_PREFIX):
284
+ findings.append(
285
+ Finding(
286
+ "INV-6",
287
+ f"anchor=none row rationale must start with {INV6_RATIONALE_PREFIX!r}; got {rationale[:60]!r}",
288
+ )
289
+ )
290
+ return findings
291
+
292
+
293
+ def _check_inv_7(e: dict[str, Any]) -> list[Finding]:
294
+ """defect_stage=negative_constraint_violation -> violated_constraint_id != null."""
295
+ if e.get("defect_stage") != "negative_constraint_violation":
296
+ return []
297
+ if e.get("violated_constraint_id") in (None, ""):
298
+ return [Finding("INV-7", "negative_constraint_violation row has null violated_constraint_id")]
299
+ return []
300
+
301
+
302
+ def _check_inv_8(e: dict[str, Any]) -> list[Finding]:
303
+ """defect_stage=negative_constraint_violation -> judgment=UNSUPPORTED."""
304
+ if e.get("defect_stage") != "negative_constraint_violation":
305
+ return []
306
+ if e.get("judgment") != "UNSUPPORTED":
307
+ return [
308
+ Finding(
309
+ "INV-8",
310
+ f"negative_constraint_violation row has judgment={e.get('judgment')!r}; must be UNSUPPORTED",
311
+ )
312
+ ]
313
+ return []
314
+
315
+
316
+ def _check_inv_9(e: dict[str, Any]) -> list[Finding]:
317
+ """upstream_dispute != null -> defect_stage substantive."""
318
+ if not e.get("upstream_dispute"):
319
+ return []
320
+ stage = e.get("defect_stage")
321
+ if stage in (None, "not_applicable"):
322
+ return [
323
+ Finding(
324
+ "INV-9",
325
+ f"upstream_dispute set on non-substantive row (defect_stage={stage!r})",
326
+ )
327
+ ]
328
+ return []
329
+
330
+
331
+ def _check_inv_10(e: dict[str, Any]) -> list[Finding]:
332
+ """ref_retrieval_method=failed -> RETRIEVAL_FAILED + inconclusive + not_applicable."""
333
+ if e.get("ref_retrieval_method") != "failed":
334
+ return []
335
+ findings: list[Finding] = []
336
+ if e.get("judgment") != "RETRIEVAL_FAILED":
337
+ findings.append(Finding("INV-10", f"method=failed but judgment={e.get('judgment')!r}"))
338
+ if e.get("audit_status") != "inconclusive":
339
+ findings.append(Finding("INV-10", f"method=failed but audit_status={e.get('audit_status')!r}"))
340
+ if e.get("defect_stage") != "not_applicable":
341
+ findings.append(Finding("INV-10", f"method=failed but defect_stage={e.get('defect_stage')!r}"))
342
+ return findings
343
+
344
+
345
+ def _check_inv_11(e: dict[str, Any]) -> list[Finding]:
346
+ """ref_retrieval_method=not_attempted ↔ anchor_kind=none."""
347
+ method = e.get("ref_retrieval_method")
348
+ kind = e.get("anchor_kind")
349
+ if method == "not_attempted" and kind != "none":
350
+ return [
351
+ Finding(
352
+ "INV-11",
353
+ f"method=not_attempted but anchor_kind={kind!r}; must be none",
354
+ )
355
+ ]
356
+ if kind == "none" and method != "not_attempted":
357
+ return [
358
+ Finding(
359
+ "INV-11",
360
+ f"anchor=none but ref_retrieval_method={method!r}; must be not_attempted",
361
+ )
362
+ ]
363
+ return []
364
+
365
+
366
+ def _check_inv_12(e: dict[str, Any]) -> list[Finding]:
367
+ """ref_retrieval_method=not_found ↔ RETRIEVAL_FAILED + completed + retrieval_existence."""
368
+ method = e.get("ref_retrieval_method")
369
+ triple = (e.get("judgment"), e.get("audit_status"), e.get("defect_stage"))
370
+ fabricated = ("RETRIEVAL_FAILED", "completed", "retrieval_existence")
371
+ if method == "not_found" and triple != fabricated:
372
+ return [
373
+ Finding(
374
+ "INV-12",
375
+ f"method=not_found but triple={triple!r}; must be {fabricated!r}",
376
+ )
377
+ ]
378
+ if triple == fabricated and method != "not_found":
379
+ return [
380
+ Finding(
381
+ "INV-12",
382
+ f"fabricated triple but method={method!r}; must be not_found",
383
+ )
384
+ ]
385
+ return []
386
+
387
+
388
+ def _check_inv_13(e: dict[str, Any]) -> list[Finding]:
389
+ """defect_stage=metadata -> UNSUPPORTED + completed + method in {api, manual_pdf}."""
390
+ if e.get("defect_stage") != "metadata":
391
+ return []
392
+ findings: list[Finding] = []
393
+ if e.get("judgment") != "UNSUPPORTED":
394
+ findings.append(Finding("INV-13", f"metadata row has judgment={e.get('judgment')!r}"))
395
+ if e.get("audit_status") != "completed":
396
+ findings.append(Finding("INV-13", f"metadata row has audit_status={e.get('audit_status')!r}"))
397
+ if e.get("ref_retrieval_method") not in ("api", "manual_pdf"):
398
+ findings.append(
399
+ Finding(
400
+ "INV-13",
401
+ f"metadata row has ref_retrieval_method={e.get('ref_retrieval_method')!r}; must be api or manual_pdf",
402
+ )
403
+ )
404
+ return findings
405
+
406
+
407
+ def _check_inv_14(e: dict[str, Any]) -> list[Finding]:
408
+ """audit_tool_failure -> RETRIEVAL_FAILED + inconclusive + not_applicable + rationale fault-class prefix."""
409
+ if e.get("ref_retrieval_method") != "audit_tool_failure":
410
+ return []
411
+ findings: list[Finding] = []
412
+ if e.get("judgment") != "RETRIEVAL_FAILED":
413
+ findings.append(Finding("INV-14", f"audit_tool_failure row has judgment={e.get('judgment')!r}"))
414
+ if e.get("audit_status") != "inconclusive":
415
+ findings.append(Finding("INV-14", f"audit_tool_failure row has audit_status={e.get('audit_status')!r}"))
416
+ if e.get("defect_stage") != "not_applicable":
417
+ findings.append(Finding("INV-14", f"audit_tool_failure row has defect_stage={e.get('defect_stage')!r}"))
418
+ rationale = e.get("rationale") or ""
419
+ if not any(rationale.startswith(f"{tag}:") for tag in INV14_FAULT_CLASS_TAGS):
420
+ findings.append(
421
+ Finding(
422
+ "INV-14",
423
+ f"audit_tool_failure rationale must begin with one of "
424
+ f"{INV14_FAULT_CLASS_TAGS}+':' got {rationale[:60]!r}",
425
+ )
426
+ )
427
+ return findings
428
+
429
+
430
+ def _check_inv_15(
431
+ e: dict[str, Any],
432
+ manifest_index: dict[str, set[str]],
433
+ ) -> list[Finding]:
434
+ """(scoped_manifest_id, claim_id) MUST resolve OR sentinel."""
435
+ sm = e.get("scoped_manifest_id")
436
+ cid = e.get("claim_id")
437
+ if sm == SENTINEL_MANIFEST_ID:
438
+ return []
439
+ if sm not in manifest_index:
440
+ return [
441
+ Finding(
442
+ "INV-15",
443
+ f"dangling scoped_manifest_id={sm!r}; no matching claim_intent_manifests[] entry",
444
+ )
445
+ ]
446
+ if cid not in manifest_index[sm]:
447
+ return [
448
+ Finding(
449
+ "INV-15",
450
+ f"(scoped_manifest_id={sm!r}, claim_id={cid!r}) pair not present in matching manifest",
451
+ )
452
+ ]
453
+ return []
454
+
455
+
456
+ def _check_inv_16(e: dict[str, Any]) -> list[Finding]:
457
+ """anchor_kind != none -> URL-decoded anchor_value non-empty after strip.
458
+
459
+ Schema mandates non-empty after URL-decoding so URL-encoded whitespace
460
+ (e.g. `%20`, `%09`) cannot bypass the firm rule. `.strip()` on the raw
461
+ string would accept `"%20"` as a non-empty anchor; decoding first makes
462
+ the check faithful to the contract.
463
+ """
464
+ kind = e.get("anchor_kind")
465
+ if kind in (None, "none"):
466
+ return []
467
+ value = e.get("anchor_value")
468
+ if not isinstance(value, str):
469
+ return [
470
+ Finding(
471
+ "INV-16",
472
+ f"anchor_kind={kind!r} has non-string anchor_value (type={type(value).__name__})",
473
+ )
474
+ ]
475
+ if urllib.parse.unquote(value).strip() == "":
476
+ return [
477
+ Finding(
478
+ "INV-16",
479
+ f"anchor_kind={kind!r} has empty anchor_value (URL-decoded whitespace-only or missing)",
480
+ )
481
+ ]
482
+ return []
483
+
484
+
485
+ def _check_inv_18(e: dict[str, Any]) -> list[Finding]:
486
+ """(RETRIEVAL_FAILED, inconclusive, not_applicable) -> method in {not_attempted, failed, audit_tool_failure}."""
487
+ triple = (e.get("judgment"), e.get("audit_status"), e.get("defect_stage"))
488
+ if triple != ("RETRIEVAL_FAILED", "inconclusive", "not_applicable"):
489
+ return []
490
+ allowed = {"not_attempted", "failed", "audit_tool_failure"}
491
+ method = e.get("ref_retrieval_method")
492
+ if method not in allowed:
493
+ return [
494
+ Finding(
495
+ "INV-18",
496
+ f"(RETRIEVAL_FAILED, inconclusive, not_applicable) row has method={method!r}; must be one of {sorted(allowed)}",
497
+ )
498
+ ]
499
+ return []
500
+
501
+
502
+ def _check_inv_19(e: dict[str, Any]) -> list[Finding]:
503
+ """sub_claim_breakdown present -> normalized-PARTIAL shape pinned (#213).
504
+
505
+ Presence of the breakdown is the machine-readable partial-support signal.
506
+ When present it must pin the full B1 normalization: judgment=UNSUPPORTED,
507
+ defect_stage=source_description, and the breakdown is *true-partial* —
508
+ >=2 items with >=1 SUPPORTED AND >=1 non-SUPPORTED sub_verdict. The
509
+ SUPPORTED-AND-non-SUPPORTED pair is what distinguishes a genuine partial
510
+ from an all-supported or all-unsupported decomposition; "non-SUPPORTED
511
+ alone" would wrongly admit an all-UNSUPPORTED breakdown.
512
+
513
+ The "what counts as non-SUPPORTED" decision is the shared
514
+ `_claim_audit_constants.SUBCLAIM_NON_SUPPORTED` constant (the literal most
515
+ likely to drift) — `is_true_partial_breakdown` in that module is the bool
516
+ form used by the runtime + calibration. This lint deliberately re-expresses
517
+ the same mix test inline (rather than calling the bool helper) because it
518
+ must emit two DISTINCT findings — "needs >=1 SUPPORTED" and "needs >=1
519
+ non-SUPPORTED" — which a single bool cannot carry. The shared constant keeps
520
+ the verdict set in sync; the granular split is lint-specific.
521
+ """
522
+ if "sub_claim_breakdown" not in e:
523
+ return []
524
+ bd = e["sub_claim_breakdown"]
525
+ findings: list[Finding] = []
526
+ if e.get("judgment") != "UNSUPPORTED":
527
+ findings.append(
528
+ Finding("INV-19", f"sub_claim_breakdown present but judgment={e.get('judgment')!r}; must be UNSUPPORTED")
529
+ )
530
+ if e.get("defect_stage") != "source_description":
531
+ findings.append(
532
+ Finding(
533
+ "INV-19",
534
+ f"sub_claim_breakdown present but defect_stage={e.get('defect_stage')!r}; must be source_description",
535
+ )
536
+ )
537
+ if not isinstance(bd, list) or len(bd) < 2:
538
+ findings.append(Finding("INV-19", "sub_claim_breakdown must have >=2 items (not a true partial)"))
539
+ return findings
540
+ verdicts = [item.get("sub_verdict") for item in _iter_dicts(bd)]
541
+ # "non-SUPPORTED" counts only the *valid* opposing verdicts (the shared
542
+ # SUBCLAIM_NON_SUPPORTED set). A missing or out-of-enum sub_verdict must NOT
543
+ # be read as non-SUPPORTED — that would let `[SUPPORTED, <missing>]`
544
+ # masquerade as true-partial (round-2 review #1). The granular split here is
545
+ # equivalent to `is_true_partial_breakdown(bd)` but yields distinct findings.
546
+ if not any(v == "SUPPORTED" for v in verdicts):
547
+ findings.append(Finding("INV-19", "sub_claim_breakdown is not true-partial: needs >=1 SUPPORTED sub_verdict"))
548
+ if not any(v in SUBCLAIM_NON_SUPPORTED for v in verdicts):
549
+ findings.append(
550
+ Finding("INV-19", "sub_claim_breakdown is not true-partial: needs >=1 non-SUPPORTED sub_verdict")
551
+ )
552
+ return findings
553
+
554
+
555
+ # INV-17 surfaces on malformed NC constraint ids encountered in manifests
556
+ # (schema also rejects the wrong shape; lint surfaces the explicit tag).
557
+ def _check_inv_17_for_manifest(manifest: dict[str, Any]) -> list[Finding]:
558
+ findings: list[Finding] = []
559
+ for claim in _iter_dicts(manifest.get("claims")):
560
+ for nc in _iter_dicts(claim.get("negative_constraints")):
561
+ cid_raw = nc.get("constraint_id", "")
562
+ cid = cid_raw if isinstance(cid_raw, str) else ""
563
+ if RE_NC_INNER_HYPHEN.match(cid):
564
+ findings.append(
565
+ Finding(
566
+ "INV-17",
567
+ f"NC constraint_id={cid!r} uses NC-C-{{n}} form; canonical is NC-C{{n}}-{{m}} (no hyphen between C and digits)",
568
+ )
569
+ )
570
+ return findings
571
+
572
+
573
+ def _check_matrix(e: dict[str, Any]) -> list[Finding]:
574
+ triple = (e.get("judgment"), e.get("audit_status"), e.get("defect_stage"))
575
+ if triple not in ALLOWED_MATRIX:
576
+ return [
577
+ Finding(
578
+ "matrix",
579
+ f"triple {triple!r} not in §3.1 allowed (judgment, audit_status, defect_stage) matrix",
580
+ )
581
+ ]
582
+ return []
583
+
584
+
585
+ # ---------------------------------------------------------------------------
586
+ # claim_intent_manifest invariants (M-INV-1..M-INV-4).
587
+ # ---------------------------------------------------------------------------
588
+
589
+
590
+ def _iter_dicts(value: Any) -> list[dict[str, Any]]:
591
+ """Filter a nested 'list of dict' field to actual dict entries.
592
+
593
+ Mirrors `_coerce_aggregate` (which protects top-level aggregates) at the
594
+ nested level: invariant walkers that descend into `claims[]`,
595
+ `negative_constraints[]`, `manifest_negative_constraints[]` etc. expect
596
+ a list of dicts. Schema validation surfaces the type mismatch separately
597
+ as a clean finding; this guard prevents the walker from crashing on a
598
+ string-instead-of-list (`for claim in "broken":`) or a non-dict entry
599
+ (`claim.get(...)`) so the CLI returns actionable lint findings rather
600
+ than a traceback (#119 + #120 P2-2 — Step 13 R6 codex P2).
601
+ """
602
+ if not isinstance(value, list):
603
+ return []
604
+ return [e for e in value if isinstance(e, dict)]
605
+
606
+
607
+ def _check_manifest_invariants(manifests: list[dict[str, Any]]) -> list[Finding]:
608
+ findings: list[Finding] = []
609
+ # M-INV-4: manifest_id uniqueness across passport.
610
+ # Skip non-string manifest_ids per v3.8.1 round 2 — schema records the
611
+ # type mismatch separately; unhashable list/dict would crash this loop.
612
+ seen_ids: dict[str, int] = {}
613
+ for i, m in enumerate(manifests):
614
+ mid = m.get("manifest_id")
615
+ if not isinstance(mid, str):
616
+ continue
617
+ if mid in seen_ids:
618
+ findings.append(
619
+ Finding(
620
+ "M-INV-4",
621
+ f"duplicate manifest_id={mid!r} (also at claim_intent_manifests[{seen_ids[mid]}])",
622
+ )
623
+ )
624
+ else:
625
+ seen_ids[mid] = i
626
+
627
+ for i, m in enumerate(manifests):
628
+ # M-INV-1: claim_id uniqueness within one manifest.
629
+ # Skip non-string claim_ids: schema validator records the type
630
+ # mismatch separately; treating an unhashable list/dict as a dict
631
+ # key here would raise TypeError and crash the lint instead of
632
+ # returning actionable findings (codex round 2 P2 / v3.8.1).
633
+ claim_ids: dict[str, int] = {}
634
+ for j, claim in enumerate(_iter_dicts(m.get("claims"))):
635
+ cid = claim.get("claim_id")
636
+ if not isinstance(cid, str):
637
+ continue
638
+ if cid in claim_ids:
639
+ findings.append(
640
+ Finding(
641
+ "M-INV-1",
642
+ f"duplicate claim_id={cid!r} within manifest_id={m.get('manifest_id')!r} "
643
+ f"(also at claims[{claim_ids[cid]}])",
644
+ )
645
+ )
646
+ else:
647
+ claim_ids[cid] = j
648
+
649
+ # M-INV-2: NC-C{n}-{m} must scope under a claims[] entry with C-{n}.
650
+ for j, claim in enumerate(_iter_dicts(m.get("claims"))):
651
+ cid_raw = claim.get("claim_id") or ""
652
+ cid = cid_raw if isinstance(cid_raw, str) else ""
653
+ cid_match = RE_CLAIM_ID.match(cid)
654
+ cid_digits = cid_match.group(1) if cid_match else None
655
+ for nc in _iter_dicts(claim.get("negative_constraints")):
656
+ nc_id_raw = nc.get("constraint_id", "")
657
+ nc_id = nc_id_raw if isinstance(nc_id_raw, str) else ""
658
+ nc_match = RE_NC_CONSTRAINT.match(nc_id)
659
+ if nc_match and nc_match.group(1) != cid_digits:
660
+ findings.append(
661
+ Finding(
662
+ "M-INV-2",
663
+ f"NC constraint_id={nc_id!r} scoped under claim_id={cid!r}; "
664
+ f"NC digits {nc_match.group(1)!r} must match claim digits {cid_digits!r}",
665
+ )
666
+ )
667
+
668
+ # M-INV-3: claim-level NC cannot reuse an MNC-* id (only ADD via NC-C{n}-{m}).
669
+ # Per spec §3.2: "claim-level can ADD via NC-C{n}-{m}, never via MNC-*" —
670
+ # the check fires on any claim-level negative_constraint whose id matches
671
+ # the MNC-* pattern, regardless of whether that exact id appears in
672
+ # manifest_negative_constraints[]. (Previous revisions built an unused
673
+ # `mnc_ids` set here that also crashed on schema-invalid unhashable
674
+ # MNC ids — codex round 3 P2; removed in v3.8.1.)
675
+ for j, claim in enumerate(_iter_dicts(m.get("claims"))):
676
+ for nc in _iter_dicts(claim.get("negative_constraints")):
677
+ nc_id_raw = nc.get("constraint_id", "")
678
+ nc_id = nc_id_raw if isinstance(nc_id_raw, str) else ""
679
+ if RE_MNC_CONSTRAINT.match(nc_id):
680
+ findings.append(
681
+ Finding(
682
+ "M-INV-3",
683
+ f"claim-level negative_constraint reuses MNC-* id={nc_id!r}; "
684
+ "claim-level can ADD via NC-C{n}-{m}, never via MNC-*",
685
+ )
686
+ )
687
+
688
+ findings.extend(_check_inv_17_for_manifest(m))
689
+
690
+ return findings
691
+
692
+
693
+ def _build_manifest_index(manifests: list[dict[str, Any]]) -> dict[str, set[str]]:
694
+ """Build {manifest_id: {claim_id, ...}} for INV-15 / U-INV-4 / D-INV-2 / CV-INV-2 lookups."""
695
+ index: dict[str, set[str]] = {}
696
+ for m in manifests:
697
+ mid = m.get("manifest_id")
698
+ # #130: skip non-string manifest_id (list/dict would raise
699
+ # TypeError: unhashable type on setdefault). Schema validator
700
+ # already records the type mismatch as a separate finding;
701
+ # the cross-field invariant pass just needs to walk past it.
702
+ if not isinstance(mid, str) or not mid:
703
+ continue
704
+ index.setdefault(mid, set())
705
+ for c in _iter_dicts(m.get("claims")):
706
+ cid = c.get("claim_id")
707
+ if isinstance(cid, str) and cid:
708
+ index[mid].add(cid)
709
+ return index
710
+
711
+
712
+ def _build_manifest_constraint_index(
713
+ manifests: list[dict[str, Any]],
714
+ ) -> dict[str, dict[str, dict[str, Any]]]:
715
+ """Build {manifest_id: {constraint_id: {kind: 'MNC'|'NC', claim_id: C-{n}|None}}}.
716
+
717
+ Used by CV-INV-2 to resolve violated_constraint_id against the active manifest.
718
+ """
719
+ out: dict[str, dict[str, dict[str, Any]]] = {}
720
+ for m in manifests:
721
+ mid = m.get("manifest_id")
722
+ # #130: same guard as _build_manifest_index — non-string mid
723
+ # would raise TypeError on `out[mid] = bucket` (unhashable).
724
+ if not isinstance(mid, str) or not mid:
725
+ continue
726
+ bucket: dict[str, dict[str, Any]] = {}
727
+ for mnc in _iter_dicts(m.get("manifest_negative_constraints")):
728
+ cid = mnc.get("constraint_id")
729
+ if isinstance(cid, str) and cid:
730
+ bucket[cid] = {"kind": "MNC", "claim_id": None}
731
+ for claim in _iter_dicts(m.get("claims")):
732
+ parent = claim.get("claim_id")
733
+ for nc in _iter_dicts(claim.get("negative_constraints")):
734
+ cid = nc.get("constraint_id")
735
+ if isinstance(cid, str) and cid:
736
+ bucket[cid] = {"kind": "NC", "claim_id": parent}
737
+ out[mid] = bucket
738
+ return out
739
+
740
+
741
+ # ---------------------------------------------------------------------------
742
+ # experiment provenance / alignment invariants (#260).
743
+ # EP-INV-1 experiment_id unique within the passport
744
+ # EP-INV-2 planned_experiment_ids[] resolve to an experiment_provenance entry
745
+ # (doubles as the rename + forward-reference dangling-pointer guard)
746
+ # EP-INV-3 planned_experiment_ids present => owning claim is empirical
747
+ # EP-INV-4 experiment_intake_declaration <-> experiment_provenance symmetry
748
+ # EP-INV-5 experiment_intake_declaration well-formedness when present
749
+ # (status enum / declared_by==scholar / declared_at date-time)
750
+ # EA-INV-1 experiment_alignment_results[].finding_id unique
751
+ # EA-INV-2 alignment row (scoped_manifest_id, claim_id) + experiment_id resolve
752
+ # These are cross-array integrity checks that JSON Schema cannot express; they
753
+ # mirror the INV-15 / U-INV-4 (scoped_manifest_id, *) resolution pattern.
754
+ # ---------------------------------------------------------------------------
755
+
756
+
757
+ def _build_claim_detail_index(
758
+ manifests: list[dict[str, Any]],
759
+ ) -> dict[tuple[str, str], dict[str, Any]]:
760
+ """Build {(manifest_id, claim_id): {evidence_kind, planned_experiment_ids}}.
761
+
762
+ Used by EP-INV-3 (planned_experiment_ids => empirical) which needs the
763
+ per-claim intended_evidence_kind, a field _build_manifest_index discards.
764
+ """
765
+ out: dict[tuple[str, str], dict[str, Any]] = {}
766
+ for m in manifests:
767
+ mid = m.get("manifest_id")
768
+ if not isinstance(mid, str) or not mid:
769
+ continue
770
+ for c in _iter_dicts(m.get("claims")):
771
+ cid = c.get("claim_id")
772
+ if not isinstance(cid, str) or not cid:
773
+ continue
774
+ out[(mid, cid)] = {
775
+ "intended_evidence_kind": c.get("intended_evidence_kind"),
776
+ "planned_experiment_ids": c.get("planned_experiment_ids"),
777
+ }
778
+ return out
779
+
780
+
781
+ def _build_experiment_id_set(provenance: list[dict[str, Any]]) -> set[str]:
782
+ """The set of well-formed experiment_id values across experiment_provenance[]."""
783
+ ids: set[str] = set()
784
+ for e in provenance:
785
+ eid = e.get("experiment_id")
786
+ if isinstance(eid, str) and eid:
787
+ ids.add(eid)
788
+ return ids
789
+
790
+
791
+ def _check_experiment_provenance_invariants(
792
+ provenance: list[dict[str, Any]],
793
+ manifests: list[dict[str, Any]],
794
+ declaration: Any,
795
+ ) -> list[Finding]:
796
+ findings: list[Finding] = []
797
+
798
+ # EP-INV-1: experiment_id unique within the passport.
799
+ seen: dict[str, int] = {}
800
+ for i, e in enumerate(provenance):
801
+ eid = e.get("experiment_id")
802
+ if not isinstance(eid, str) or not eid:
803
+ continue # schema-shape surfaces malformed/missing ids separately
804
+ if eid in seen:
805
+ findings.append(
806
+ Finding("EP-INV-1", f"duplicate experiment_id={eid!r} (also at experiment_provenance[{seen[eid]}])")
807
+ )
808
+ else:
809
+ seen[eid] = i
810
+
811
+ experiment_ids = set(seen.keys())
812
+ claim_detail = _build_claim_detail_index(manifests)
813
+
814
+ # EP-INV-2 + EP-INV-3: walk each manifest claim's planned_experiment_ids.
815
+ for (mid, cid), detail in claim_detail.items():
816
+ planned = detail.get("planned_experiment_ids")
817
+ if planned is None:
818
+ continue # optional-absent — nothing to check
819
+ if not isinstance(planned, list):
820
+ continue # schema-shape surfaces the type error
821
+ # EP-INV-2: every value resolves to exactly one provenance entry.
822
+ for pid in planned:
823
+ if not isinstance(pid, str):
824
+ continue
825
+ if pid not in experiment_ids:
826
+ findings.append(
827
+ Finding(
828
+ "EP-INV-2",
829
+ f"planned_experiment_ids value {pid!r} on (manifest={mid!r}, claim={cid!r}) "
830
+ f"resolves to no experiment_provenance[] entry (dangling: rename-without-re-emit "
831
+ f"or writer ran ahead of intake)",
832
+ )
833
+ )
834
+ # EP-INV-3: presence => owning claim is empirical. Mixed evidence
835
+ # (planned_refs AND planned_experiment_ids) is allowed — this only
836
+ # forbids experiment ids on a non-empirical claim.
837
+ kind = detail.get("intended_evidence_kind")
838
+ if kind != "empirical":
839
+ findings.append(
840
+ Finding(
841
+ "EP-INV-3",
842
+ f"planned_experiment_ids present on (manifest={mid!r}, claim={cid!r}) but "
843
+ f"intended_evidence_kind={kind!r}; experiment ids require empirical (mixed "
844
+ f"literature+experiment is allowed, non-empirical is not)",
845
+ )
846
+ )
847
+
848
+ # EP-INV-4: declaration <-> provenance symmetry. The deterministic
849
+ # structural half of D7 FAIL conditions #2 and #3.
850
+ status = None
851
+ if isinstance(declaration, dict):
852
+ status = declaration.get("status")
853
+ has_provenance = len(provenance) > 0
854
+ if status == "experiments_declared" and not has_provenance:
855
+ findings.append(
856
+ Finding(
857
+ "EP-INV-4",
858
+ "experiment_intake_declaration.status == experiments_declared but "
859
+ "experiment_provenance[] is absent/empty",
860
+ )
861
+ )
862
+ if status == "no_experiments_declared" and has_provenance:
863
+ findings.append(
864
+ Finding(
865
+ "EP-INV-4",
866
+ "experiment_intake_declaration.status == no_experiments_declared but "
867
+ "experiment_provenance[] is non-empty (a populated array contradicts the declaration)",
868
+ )
869
+ )
870
+
871
+ # EP-INV-5: when a declaration IS present, it must be well-formed. A
872
+ # malformed declaration (bad status enum, wrong declared_by, missing/empty
873
+ # declared_at) would otherwise pass silently — the symmetry checks above
874
+ # only fire on the two known status literals, so `status: "garbage"` slips
875
+ # through with no finding. This is the deterministic shape half of D7 the
876
+ # symmetry check alone does not cover; it does NOT decide presence/absence
877
+ # (the ars_version legacy gate, a Stage-1 gate check, owns that).
878
+ if declaration is not None:
879
+ if not isinstance(declaration, dict):
880
+ findings.append(
881
+ Finding(
882
+ "EP-INV-5",
883
+ f"experiment_intake_declaration must be a mapping; got {type(declaration).__name__}",
884
+ )
885
+ )
886
+ else:
887
+ allowed_status = {
888
+ "experiments_declared",
889
+ "no_experiments_declared",
890
+ "legacy_unknown",
891
+ }
892
+ if status not in allowed_status:
893
+ findings.append(
894
+ Finding(
895
+ "EP-INV-5",
896
+ f"experiment_intake_declaration.status={status!r} not in "
897
+ f"{sorted(allowed_status)}",
898
+ )
899
+ )
900
+ declared_by = declaration.get("declared_by")
901
+ if declared_by != "scholar":
902
+ findings.append(
903
+ Finding(
904
+ "EP-INV-5",
905
+ f"experiment_intake_declaration.declared_by={declared_by!r}; must be "
906
+ "'scholar' (an intake decision, never an agent emission)",
907
+ )
908
+ )
909
+ declared_at = declaration.get("declared_at")
910
+ if not isinstance(declared_at, str) or not declared_at:
911
+ findings.append(
912
+ Finding(
913
+ "EP-INV-5",
914
+ "experiment_intake_declaration.declared_at must be a non-empty "
915
+ f"date-time string; got {declared_at!r}",
916
+ )
917
+ )
918
+
919
+ return findings
920
+
921
+
922
+ def _check_experiment_alignment_invariants(
923
+ entries: list[dict[str, Any]],
924
+ manifest_index: dict[str, set[str]],
925
+ experiment_ids: set[str],
926
+ ) -> list[Finding]:
927
+ findings: list[Finding] = []
928
+
929
+ # EA-INV-1: finding_id uniqueness.
930
+ seen: dict[str, int] = {}
931
+ for i, e in enumerate(entries):
932
+ fid = e.get("finding_id")
933
+ if not isinstance(fid, str):
934
+ continue
935
+ if fid in seen:
936
+ findings.append(
937
+ Finding("EA-INV-1", f"duplicate finding_id={fid!r} (also at experiment_alignment_results[{seen[fid]}])")
938
+ )
939
+ else:
940
+ seen[fid] = i
941
+
942
+ # EA-INV-2: (scoped_manifest_id, claim_id) resolves to a real manifest claim
943
+ # AND experiment_id resolves to a real experiment_provenance[] entry. A
944
+ # dangling id is a structural FAIL here (and at EP-INV-2 from the manifest
945
+ # side) — never represented as a judge verdict.
946
+ for e in entries:
947
+ fid = e.get("finding_id")
948
+ smid = e.get("scoped_manifest_id")
949
+ cid = e.get("claim_id")
950
+ if not (isinstance(smid, str) and isinstance(cid, str)):
951
+ continue # schema-shape surfaces the type error
952
+ if smid not in manifest_index or cid not in manifest_index[smid]:
953
+ findings.append(
954
+ Finding(
955
+ "EA-INV-2",
956
+ f"alignment row finding_id={fid!r} has ({smid!r}, {cid!r}) "
957
+ f"not present in any claim_intent_manifests[] entry",
958
+ )
959
+ )
960
+ eid = e.get("experiment_id")
961
+ if isinstance(eid, str) and eid and eid not in experiment_ids:
962
+ findings.append(
963
+ Finding(
964
+ "EA-INV-2",
965
+ f"alignment row finding_id={fid!r} references experiment_id={eid!r} "
966
+ f"with no matching experiment_provenance[] entry (structural FAIL, not a verdict)",
967
+ )
968
+ )
969
+
970
+ return findings
971
+
972
+
973
+ # ---------------------------------------------------------------------------
974
+ # uncited_assertion invariants (U-INV-1..U-INV-4).
975
+ # ---------------------------------------------------------------------------
976
+
977
+
978
+ def _check_uncited_invariants(
979
+ entries: list[dict[str, Any]],
980
+ manifest_index: dict[str, set[str]],
981
+ ) -> list[Finding]:
982
+ findings: list[Finding] = []
983
+ # U-INV-1: finding_id uniqueness. Non-string ids skipped per v3.8.1 round 2.
984
+ seen: dict[str, int] = {}
985
+ for i, e in enumerate(entries):
986
+ fid = e.get("finding_id")
987
+ if not isinstance(fid, str):
988
+ continue
989
+ if fid in seen:
990
+ findings.append(
991
+ Finding("U-INV-1", f"duplicate finding_id={fid!r} (also at uncited_assertions[{seen[fid]}])")
992
+ )
993
+ else:
994
+ seen[fid] = i
995
+
996
+ for e in entries:
997
+ # U-INV-2: trigger_tokens non-empty (schema also enforces minItems=1).
998
+ tokens = e.get("trigger_tokens", [])
999
+ if not isinstance(tokens, list) or len(tokens) == 0:
1000
+ findings.append(Finding("U-INV-2", f"empty trigger_tokens on finding_id={e.get('finding_id')!r}"))
1001
+
1002
+ # U-INV-3: rule_version literal.
1003
+ if e.get("rule_version") != "D4-c-v1":
1004
+ findings.append(
1005
+ Finding(
1006
+ "U-INV-3",
1007
+ f"rule_version={e.get('rule_version')!r} on finding_id={e.get('finding_id')!r}; must equal D4-c-v1",
1008
+ )
1009
+ )
1010
+
1011
+ # U-INV-4: cross-array integrity.
1012
+ mcid = e.get("manifest_claim_id")
1013
+ smid = e.get("scoped_manifest_id")
1014
+ if mcid is None and smid is None:
1015
+ continue
1016
+ if mcid is not None and smid is None:
1017
+ findings.append(
1018
+ Finding(
1019
+ "U-INV-4",
1020
+ f"manifest_claim_id set but scoped_manifest_id null on finding_id={e.get('finding_id')!r}",
1021
+ )
1022
+ )
1023
+ continue
1024
+ if mcid is None and smid is not None:
1025
+ findings.append(
1026
+ Finding(
1027
+ "U-INV-4",
1028
+ f"scoped_manifest_id set but manifest_claim_id null on finding_id={e.get('finding_id')!r}",
1029
+ )
1030
+ )
1031
+ continue
1032
+ # Both non-null: must resolve.
1033
+ if smid not in manifest_index or mcid not in manifest_index[smid]:
1034
+ findings.append(
1035
+ Finding(
1036
+ "U-INV-4",
1037
+ f"dangling ({smid!r}, {mcid!r}) on finding_id={e.get('finding_id')!r}",
1038
+ )
1039
+ )
1040
+
1041
+ return findings
1042
+
1043
+
1044
+ # ---------------------------------------------------------------------------
1045
+ # claim_drift invariants (D-INV-1..D-INV-4).
1046
+ # ---------------------------------------------------------------------------
1047
+
1048
+
1049
+ def _check_drift_invariants(
1050
+ entries: list[dict[str, Any]],
1051
+ uncited_entries: list[dict[str, Any]],
1052
+ manifest_index: dict[str, set[str]],
1053
+ ) -> list[Finding]:
1054
+ findings: list[Finding] = []
1055
+
1056
+ # D-INV-1: finding_id uniqueness. Non-string ids skipped per v3.8.1 round 2.
1057
+ seen: dict[str, int] = {}
1058
+ for i, e in enumerate(entries):
1059
+ fid = e.get("finding_id")
1060
+ if not isinstance(fid, str):
1061
+ continue
1062
+ if fid in seen:
1063
+ findings.append(
1064
+ Finding("D-INV-1", f"duplicate finding_id={fid!r} (also at claim_drifts[{seen[fid]}])")
1065
+ )
1066
+ else:
1067
+ seen[fid] = i
1068
+
1069
+ for e in entries:
1070
+ kind = e.get("drift_kind")
1071
+ mcid = e.get("manifest_claim_id")
1072
+ smid = e.get("scoped_manifest_id")
1073
+ section = e.get("section_path")
1074
+ fid = e.get("finding_id")
1075
+
1076
+ # D-INV-2 by drift_kind.
1077
+ if kind == "INTENDED_NOT_EMITTED":
1078
+ if mcid is None or smid is None:
1079
+ findings.append(
1080
+ Finding(
1081
+ "D-INV-2",
1082
+ f"INTENDED_NOT_EMITTED finding_id={fid!r} missing manifest_claim_id/scoped_manifest_id",
1083
+ )
1084
+ )
1085
+ else:
1086
+ if smid not in manifest_index or mcid not in manifest_index[smid]:
1087
+ findings.append(
1088
+ Finding(
1089
+ "D-INV-2",
1090
+ f"INTENDED_NOT_EMITTED finding_id={fid!r}: dangling ({smid!r}, {mcid!r})",
1091
+ )
1092
+ )
1093
+ elif kind == "EMITTED_NOT_INTENDED":
1094
+ if mcid is not None or smid is not None:
1095
+ findings.append(
1096
+ Finding(
1097
+ "D-INV-2",
1098
+ f"EMITTED_NOT_INTENDED finding_id={fid!r} has non-null manifest_claim_id/scoped_manifest_id",
1099
+ )
1100
+ )
1101
+ if not section:
1102
+ findings.append(
1103
+ Finding(
1104
+ "D-INV-2",
1105
+ f"EMITTED_NOT_INTENDED finding_id={fid!r} missing section_path",
1106
+ )
1107
+ )
1108
+
1109
+ # D-INV-3: rule_version literal.
1110
+ if e.get("rule_version") != "D4-a-v1":
1111
+ findings.append(
1112
+ Finding(
1113
+ "D-INV-3",
1114
+ f"rule_version={e.get('rule_version')!r} on finding_id={fid!r}; must equal D4-a-v1",
1115
+ )
1116
+ )
1117
+
1118
+ # D-INV-4: a sentence cannot appear in BOTH uncited_assertions[] AND claim_drifts[].
1119
+ uncited_texts = {u.get("sentence_text") for u in uncited_entries if u.get("sentence_text")}
1120
+ for e in entries:
1121
+ text = e.get("claim_text")
1122
+ if text and text in uncited_texts:
1123
+ findings.append(
1124
+ Finding(
1125
+ "D-INV-4",
1126
+ f"finding_id={e.get('finding_id')!r}: claim_text overlaps with an uncited_assertion sentence",
1127
+ )
1128
+ )
1129
+
1130
+ return findings
1131
+
1132
+
1133
+ # ---------------------------------------------------------------------------
1134
+ # constraint_violation invariants (CV-INV-1..CV-INV-4).
1135
+ # ---------------------------------------------------------------------------
1136
+
1137
+
1138
+ def _check_constraint_violation_invariants(
1139
+ entries: list[dict[str, Any]],
1140
+ constraint_index: dict[str, dict[str, dict[str, Any]]],
1141
+ ) -> list[Finding]:
1142
+ findings: list[Finding] = []
1143
+
1144
+ # CV-INV-1: finding_id uniqueness. Non-string ids skipped per v3.8.1 round 2.
1145
+ seen: dict[str, int] = {}
1146
+ for i, e in enumerate(entries):
1147
+ fid = e.get("finding_id")
1148
+ if not isinstance(fid, str):
1149
+ continue
1150
+ if fid in seen:
1151
+ findings.append(
1152
+ Finding(
1153
+ "CV-INV-1",
1154
+ f"duplicate finding_id={fid!r} (also at constraint_violations[{seen[fid]}])",
1155
+ )
1156
+ )
1157
+ else:
1158
+ seen[fid] = i
1159
+
1160
+ # CV-INV-4: a sentence MUST NOT appear in constraint_violations[] more than once
1161
+ # per (scoped_manifest_id, section_path, claim_text_hash, violated_constraint_id).
1162
+ # Step 13 R8 codex P2-1: scope dedupe by manifest_id so two manifests in the
1163
+ # same passport carrying colliding MNC-* / NC-* ids — each legitimately
1164
+ # violated on the same sentence text — do not false-positive as duplicates.
1165
+ # M-INV-4 permits manifest_id uniqueness across passport but constraint_id
1166
+ # uniqueness only within a manifest; dedupe must respect the same scope.
1167
+ # v3.8.1 round 2: coerce each key component to str so schema-invalid
1168
+ # non-string values (lists/dicts) don't crash hashlib.encode() or hash().
1169
+ def _safe_str(value: Any) -> str:
1170
+ return value if isinstance(value, str) else ""
1171
+
1172
+ dedup: dict[tuple[str, str, str, str], int] = {}
1173
+ for i, e in enumerate(entries):
1174
+ key = (
1175
+ _safe_str(e.get("scoped_manifest_id")),
1176
+ _safe_str(e.get("section_path")),
1177
+ hashlib.sha256(_safe_str(e.get("claim_text")).encode("utf-8")).hexdigest(),
1178
+ _safe_str(e.get("violated_constraint_id")),
1179
+ )
1180
+ if key in dedup:
1181
+ findings.append(
1182
+ Finding(
1183
+ "CV-INV-4",
1184
+ f"finding_id={e.get('finding_id')!r}: duplicate (manifest, section, claim, constraint) with constraint_violations[{dedup[key]}]",
1185
+ )
1186
+ )
1187
+ else:
1188
+ dedup[key] = i
1189
+
1190
+ for e in entries:
1191
+ cid = e.get("violated_constraint_id") or ""
1192
+ smid = e.get("scoped_manifest_id")
1193
+ mcid = e.get("manifest_claim_id")
1194
+ fid = e.get("finding_id")
1195
+
1196
+ # CV-INV-2: (scoped_manifest_id, violated_constraint_id) MUST resolve.
1197
+ if smid not in constraint_index or cid not in constraint_index.get(smid, {}):
1198
+ findings.append(
1199
+ Finding(
1200
+ "CV-INV-2",
1201
+ f"finding_id={fid!r}: ({smid!r}, {cid!r}) not present in any manifest's constraint set",
1202
+ )
1203
+ )
1204
+ else:
1205
+ # CV-INV-2 (NC variant): manifest_claim_id MUST equal the C-{n} extracted from NC id.
1206
+ slot = constraint_index[smid][cid]
1207
+ if slot["kind"] == "NC":
1208
+ expected_claim = slot["claim_id"]
1209
+ if mcid != expected_claim:
1210
+ findings.append(
1211
+ Finding(
1212
+ "CV-INV-2",
1213
+ f"finding_id={fid!r}: NC violated_constraint_id={cid!r} scopes claim_id={expected_claim!r}, "
1214
+ f"but manifest_claim_id={mcid!r}",
1215
+ )
1216
+ )
1217
+
1218
+ # CV-INV-3: MNC -> manifest_claim_id null; NC -> manifest_claim_id non-null and matches.
1219
+ mnc_match = RE_MNC_CONSTRAINT.match(cid)
1220
+ nc_match = RE_NC_CONSTRAINT.match(cid)
1221
+ if mnc_match and mcid is not None:
1222
+ findings.append(
1223
+ Finding(
1224
+ "CV-INV-3",
1225
+ f"finding_id={fid!r}: MNC-* constraint with non-null manifest_claim_id={mcid!r}",
1226
+ )
1227
+ )
1228
+ if nc_match:
1229
+ expected_digits = nc_match.group(1)
1230
+ if mcid is None:
1231
+ findings.append(
1232
+ Finding(
1233
+ "CV-INV-3",
1234
+ f"finding_id={fid!r}: NC-* constraint with null manifest_claim_id",
1235
+ )
1236
+ )
1237
+ else:
1238
+ mcid_match = RE_CLAIM_ID.match(mcid)
1239
+ actual_digits = mcid_match.group(1) if mcid_match else None
1240
+ if actual_digits != expected_digits:
1241
+ findings.append(
1242
+ Finding(
1243
+ "CV-INV-3",
1244
+ f"finding_id={fid!r}: NC digits {expected_digits!r} vs manifest_claim_id digits {actual_digits!r}",
1245
+ )
1246
+ )
1247
+
1248
+ return findings
1249
+
1250
+
1251
+ # ---------------------------------------------------------------------------
1252
+ # uncited_audit_failure invariants (UAF-INV-1..UAF-INV-6) — v3.8.2 / #118.
1253
+ # Surfaces transient judge outages on the uncited path that pre-v3.8.2 were
1254
+ # silently substituted as NOT_VIOLATED. Mirrors INV-14 semantics on the
1255
+ # cited path but uses a dedicated aggregate because claim_audit_result.ref_slug
1256
+ # is required. See docs/design/2026-05-15-issue-103-claim-alignment-audit-spec.md §3.6.
1257
+ # ---------------------------------------------------------------------------
1258
+
1259
+
1260
+ def _check_uaf_invariants(
1261
+ entries: list[dict[str, Any]],
1262
+ violations: list[dict[str, Any]],
1263
+ manifest_index: dict[str, set[str]],
1264
+ ) -> list[Finding]:
1265
+ findings: list[Finding] = []
1266
+
1267
+ def _safe_str(value: Any) -> str:
1268
+ return value if isinstance(value, str) else ""
1269
+
1270
+ # UAF-INV-1: finding_id uniqueness. Non-string ids skipped per v3.8.1 round 2
1271
+ # hardening pattern; schema validator surfaces type mismatch separately.
1272
+ seen: dict[str, int] = {}
1273
+ for i, e in enumerate(entries):
1274
+ fid = e.get("finding_id")
1275
+ if not isinstance(fid, str):
1276
+ continue
1277
+ if fid in seen:
1278
+ findings.append(
1279
+ Finding(
1280
+ "UAF-INV-1",
1281
+ f"duplicate finding_id={fid!r} (also at uncited_audit_failures[{seen[fid]}])",
1282
+ )
1283
+ )
1284
+ else:
1285
+ seen[fid] = i
1286
+
1287
+ # UAF-INV-4: per-(sentence, manifest) dedup with key
1288
+ # (scoped_manifest_id, section_path, claim_text_hash). Two manifests both
1289
+ # failing on the same sentence text emit two distinct rows legitimately
1290
+ # (cross-manifest scope mirrors CV-INV-4).
1291
+ dedup: dict[tuple[str, str, str], int] = {}
1292
+ for i, e in enumerate(entries):
1293
+ key = (
1294
+ _safe_str(e.get("scoped_manifest_id")),
1295
+ _safe_str(e.get("section_path")),
1296
+ hashlib.sha256(_safe_str(e.get("claim_text")).encode("utf-8")).hexdigest(),
1297
+ )
1298
+ if key in dedup:
1299
+ findings.append(
1300
+ Finding(
1301
+ "UAF-INV-4",
1302
+ f"finding_id={e.get('finding_id')!r}: duplicate (manifest, section, claim) with uncited_audit_failures[{dedup[key]}]",
1303
+ )
1304
+ )
1305
+ else:
1306
+ dedup[key] = i
1307
+
1308
+ for e in entries:
1309
+ fid = e.get("finding_id")
1310
+ smid = e.get("scoped_manifest_id")
1311
+ mcid = e.get("manifest_claim_id")
1312
+ rationale = e.get("rationale") or ""
1313
+ row_fault = e.get("fault_class")
1314
+
1315
+ # UAF-INV-2: scoped_manifest_id cross-array integrity.
1316
+ manifest_resolved = isinstance(smid, str) and smid in manifest_index
1317
+ if not manifest_resolved:
1318
+ findings.append(
1319
+ Finding(
1320
+ "UAF-INV-2",
1321
+ f"finding_id={fid!r}: scoped_manifest_id={smid!r} not present in claim_intent_manifests[]",
1322
+ )
1323
+ )
1324
+ # Don't `continue` here — UAF-INV-5 (rationale prefix) is
1325
+ # orthogonal to manifest integrity and we want both findings
1326
+ # to surface together so the user fixes both in one pass
1327
+ # (Gemini cross-model review P1, 2026-05-17).
1328
+
1329
+ # UAF-INV-3: (scoped_manifest_id, manifest_claim_id) pair integrity
1330
+ # when manifest_claim_id is non-null. Null is legitimate when the
1331
+ # failure was against MNCs only (no claim binding). Skip when the
1332
+ # manifest itself failed to resolve — the pair check would
1333
+ # otherwise always fire and double-report the same root cause.
1334
+ # Guard `mcid` with isinstance(str) before set membership: a
1335
+ # malformed passport with mcid as list/dict (schema flags this
1336
+ # separately) would raise TypeError in `mcid not in ...` and crash
1337
+ # the lint instead of returning a clean finding. Mirrors the v3.8.1
1338
+ # round-2 hardening pattern (Codex R2 P2-2, 2026-05-17).
1339
+ if manifest_resolved and mcid is not None and isinstance(mcid, str):
1340
+ claim_ids = manifest_index.get(smid, set())
1341
+ if mcid not in claim_ids:
1342
+ findings.append(
1343
+ Finding(
1344
+ "UAF-INV-3",
1345
+ f"finding_id={fid!r}: (scoped_manifest_id={smid!r}, manifest_claim_id={mcid!r}) not present in any manifest's claims[]",
1346
+ )
1347
+ )
1348
+
1349
+ # UAF-INV-5: rationale MUST begin with this row's own fault_class
1350
+ # value followed by ":" (and " <detail>" when JudgeInvocationError
1351
+ # carried a non-empty detail). Mirrors INV-14 rationale prefix on
1352
+ # the cited path. We check against the row's fault_class field
1353
+ # (not any known tag) so a row with fault_class judge_timeout but
1354
+ # rationale starting with "judge_api_error: ..." still trips this
1355
+ # invariant — the prefix must match the row.
1356
+ # Guard `rationale` with isinstance(str): the row's raw rationale
1357
+ # may be list/dict on a malformed passport — schema flags it
1358
+ # separately and we should skip cleanly rather than crash on
1359
+ # `.startswith()` (Codex R2 P2-3, 2026-05-17). The `or ""`
1360
+ # initialization above only fallbacks on None/empty-string falsy
1361
+ # values; a truthy dict/list slips through and needs this guard.
1362
+ if (
1363
+ isinstance(row_fault, str)
1364
+ and row_fault in INV14_FAULT_CLASS_TAGS
1365
+ and isinstance(rationale, str)
1366
+ ):
1367
+ expected_prefix = f"{row_fault}:"
1368
+ if not rationale.startswith(expected_prefix):
1369
+ findings.append(
1370
+ Finding(
1371
+ "UAF-INV-5",
1372
+ f"finding_id={fid!r}: rationale must begin with {expected_prefix!r} got {rationale[:60]!r}",
1373
+ )
1374
+ )
1375
+
1376
+ # UAF-INV-6: cross-aggregate exclusivity with constraint_violations[].
1377
+ # A sentence MUST NOT appear in both UAF and CV for the same
1378
+ # (scoped_manifest_id, section_path, claim_text_hash). VIOLATED (positive
1379
+ # verdict) and audit_tool_failure (no verdict) are mutually exclusive
1380
+ # verdict states at per-(sentence, manifest) level.
1381
+ cv_keys: set[tuple[str, str, str]] = set()
1382
+ for cv in violations:
1383
+ cv_keys.add(
1384
+ (
1385
+ _safe_str(cv.get("scoped_manifest_id")),
1386
+ _safe_str(cv.get("section_path")),
1387
+ hashlib.sha256(_safe_str(cv.get("claim_text")).encode("utf-8")).hexdigest(),
1388
+ )
1389
+ )
1390
+ for uaf in entries:
1391
+ key = (
1392
+ _safe_str(uaf.get("scoped_manifest_id")),
1393
+ _safe_str(uaf.get("section_path")),
1394
+ hashlib.sha256(_safe_str(uaf.get("claim_text")).encode("utf-8")).hexdigest(),
1395
+ )
1396
+ if key in cv_keys:
1397
+ findings.append(
1398
+ Finding(
1399
+ "UAF-INV-6",
1400
+ f"finding_id={uaf.get('finding_id')!r}: (manifest, section, claim) overlaps a constraint_violations[] row — UAF and CV are mutually exclusive verdict states",
1401
+ )
1402
+ )
1403
+
1404
+ return findings
1405
+
1406
+
1407
+ # ---------------------------------------------------------------------------
1408
+ # audit_sampling_summary invariants (S-INV-1..S-INV-4).
1409
+ # ---------------------------------------------------------------------------
1410
+
1411
+
1412
+ def _check_sampling_invariants(entries: list[dict[str, Any]]) -> list[Finding]:
1413
+ findings: list[Finding] = []
1414
+ for e in entries:
1415
+ indices = e.get("audited_indices", []) or []
1416
+ count = e.get("audited_count")
1417
+ cap = e.get("max_claims_per_paper")
1418
+ total = e.get("total_citation_count")
1419
+ run_id = e.get("audit_run_id")
1420
+
1421
+ # S-INV-1: audited_count == |audited_indices|.
1422
+ if isinstance(indices, list) and count != len(indices):
1423
+ findings.append(
1424
+ Finding(
1425
+ "S-INV-1",
1426
+ f"audit_run_id={run_id!r}: audited_count={count!r} != len(audited_indices)={len(indices)}",
1427
+ )
1428
+ )
1429
+
1430
+ # S-INV-2: count ≤ cap AND count ≤ total.
1431
+ if isinstance(count, int):
1432
+ if isinstance(cap, int) and count > cap:
1433
+ findings.append(
1434
+ Finding(
1435
+ "S-INV-2",
1436
+ f"audit_run_id={run_id!r}: audited_count={count} > max_claims_per_paper={cap}",
1437
+ )
1438
+ )
1439
+ if isinstance(total, int) and count > total:
1440
+ findings.append(
1441
+ Finding(
1442
+ "S-INV-2",
1443
+ f"audit_run_id={run_id!r}: audited_count={count} > total_citation_count={total}",
1444
+ )
1445
+ )
1446
+
1447
+ # S-INV-4: strictly ascending, no duplicates. Guard against mixed-type
1448
+ # indices (#119 R6 codex P2 + Step 13 R8 P2-2) — schema records the
1449
+ # type mismatch separately; if a non-int slips through we skip the
1450
+ # comparison rather than raising TypeError on `<=` between str and int.
1451
+ if isinstance(indices, list):
1452
+ for i in range(1, len(indices)):
1453
+ a, b = indices[i - 1], indices[i]
1454
+ if not (isinstance(a, int) and isinstance(b, int)):
1455
+ continue
1456
+ if b <= a:
1457
+ findings.append(
1458
+ Finding(
1459
+ "S-INV-4",
1460
+ f"audit_run_id={run_id!r}: audited_indices not strictly ascending at position {i} "
1461
+ f"({a!r} -> {b!r})",
1462
+ )
1463
+ )
1464
+ break
1465
+
1466
+ return findings
1467
+
1468
+
1469
+ # ---------------------------------------------------------------------------
1470
+ # Schema-shape validation (rule 1).
1471
+ # ---------------------------------------------------------------------------
1472
+
1473
+
1474
+ def _validate_against_schema(
1475
+ entries: Any,
1476
+ schema_key: str,
1477
+ aggregate_label: str,
1478
+ ) -> list[Finding]:
1479
+ """Validate every entry against the named schema.
1480
+
1481
+ Accepts `Any` rather than `Iterable[dict]` because callers may hand us a
1482
+ malformed passport — `entries` could be non-list, or a list with
1483
+ non-dict elements. Both must surface as a clean schema finding rather
1484
+ than a TypeError / AttributeError traceback (Step 13 R4 codex P2 #4).
1485
+ """
1486
+ findings: list[Finding] = []
1487
+ if not isinstance(entries, list):
1488
+ findings.append(
1489
+ Finding(
1490
+ "schema",
1491
+ f"{aggregate_label}: aggregate must be a JSON array; got {type(entries).__name__}",
1492
+ )
1493
+ )
1494
+ return findings
1495
+ validator = _validator(schema_key)
1496
+ for i, entry in enumerate(entries):
1497
+ if not isinstance(entry, dict):
1498
+ findings.append(
1499
+ Finding(
1500
+ "schema",
1501
+ f"{aggregate_label}[{i}]: entry must be a JSON object; got {type(entry).__name__}",
1502
+ )
1503
+ )
1504
+ continue
1505
+ for err in validator.iter_errors(entry):
1506
+ findings.append(
1507
+ Finding(
1508
+ "schema",
1509
+ f"{aggregate_label}[{i}] {'.'.join(str(p) for p in err.absolute_path) or '<root>'}: {err.message}",
1510
+ )
1511
+ )
1512
+ return findings
1513
+
1514
+
1515
+ # ---------------------------------------------------------------------------
1516
+ # Driver
1517
+ # ---------------------------------------------------------------------------
1518
+
1519
+
1520
+ def validate_passport(body: Any) -> list[Finding]:
1521
+ """Run all 39 invariants + schema-shape against a passport body. Returns findings."""
1522
+ # Step 13 R7 codex P3: a syntactically valid JSON top level can still
1523
+ # be `[]`, `null`, or a scalar. `.get()` on those raises AttributeError;
1524
+ # surface a clean schema finding instead so the CLI can return findings
1525
+ # rather than tracing back.
1526
+ if not isinstance(body, dict):
1527
+ return [
1528
+ Finding(
1529
+ "schema",
1530
+ f"passport body must be a JSON object; got {type(body).__name__}",
1531
+ )
1532
+ ]
1533
+ # Raw aggregates — preserved for schema-shape validation below (which
1534
+ # records findings on non-list / non-dict-entry inputs). MUST NOT use
1535
+ # `or []` because a falsey but malformed value such as `{}`, `null`, `0`,
1536
+ # or `""` would silently be replaced with an empty list and bypass the
1537
+ # schema gate (Step 13 R5 codex P2 #1). Only a missing key falls back to
1538
+ # the empty-list default.
1539
+ manifests_raw = body.get("claim_intent_manifests", [])
1540
+ results_raw = body.get("claim_audit_results", [])
1541
+ uncited_raw = body.get("uncited_assertions", [])
1542
+ drifts_raw = body.get("claim_drifts", [])
1543
+ violations_raw = body.get("constraint_violations", [])
1544
+ samplings_raw = body.get("audit_sampling_summaries", [])
1545
+ uaf_raw = body.get("uncited_audit_failures", [])
1546
+ provenance_raw = body.get("experiment_provenance", []) # #260
1547
+ alignment_raw = body.get("experiment_alignment_results", []) # #260
1548
+ declaration = body.get("experiment_intake_declaration") # #260 (object | None)
1549
+
1550
+ def _coerce_aggregate(value: Any) -> list[dict[str, Any]]:
1551
+ """Reduce an aggregate to a list of dict entries.
1552
+
1553
+ Schema-shape failures are surfaced separately by
1554
+ `_validate_against_schema` against the raw aggregate. The dict-only
1555
+ invariant loops below attempt `.get()` / dictionary indexing —
1556
+ passing a non-list or a list with non-dict entries would raise
1557
+ AttributeError and crash the lint with a traceback instead of
1558
+ returning a clean failure finding (Step 13 R4 codex P2 #4).
1559
+ """
1560
+ if not isinstance(value, list):
1561
+ return []
1562
+ return [e for e in value if isinstance(e, dict)]
1563
+
1564
+ manifests = _coerce_aggregate(manifests_raw)
1565
+ results = _coerce_aggregate(results_raw)
1566
+ uncited = _coerce_aggregate(uncited_raw)
1567
+ drifts = _coerce_aggregate(drifts_raw)
1568
+ violations = _coerce_aggregate(violations_raw)
1569
+ samplings = _coerce_aggregate(samplings_raw)
1570
+ uaf = _coerce_aggregate(uaf_raw)
1571
+ provenance = _coerce_aggregate(provenance_raw) # #260
1572
+ alignment = _coerce_aggregate(alignment_raw) # #260
1573
+
1574
+ findings: list[Finding] = []
1575
+
1576
+ # Schema-shape — gives the lint a chance to surface malformed entries with
1577
+ # an actionable rendering before cross-field checks fan out spurious tags.
1578
+ findings.extend(_validate_against_schema(manifests_raw, "claim_intent_manifest", "claim_intent_manifests"))
1579
+ findings.extend(_validate_against_schema(results_raw, "claim_audit_result", "claim_audit_results"))
1580
+ findings.extend(_validate_against_schema(uncited_raw, "uncited_assertion", "uncited_assertions"))
1581
+ findings.extend(_validate_against_schema(drifts_raw, "claim_drift", "claim_drifts"))
1582
+ findings.extend(_validate_against_schema(violations_raw, "constraint_violation", "constraint_violations"))
1583
+ findings.extend(_validate_against_schema(samplings_raw, "audit_sampling_summary", "audit_sampling_summaries"))
1584
+ findings.extend(_validate_against_schema(uaf_raw, "uncited_audit_failure", "uncited_audit_failures"))
1585
+ findings.extend(_validate_against_schema(provenance_raw, "experiment_provenance_entry", "experiment_provenance"))
1586
+ findings.extend(_validate_against_schema(alignment_raw, "experiment_alignment_result", "experiment_alignment_results"))
1587
+
1588
+ # Manifest invariants + index for downstream cross-array checks.
1589
+ findings.extend(_check_manifest_invariants(manifests))
1590
+ manifest_index = _build_manifest_index(manifests)
1591
+ constraint_index = _build_manifest_constraint_index(manifests)
1592
+
1593
+ # claim_audit_result invariants.
1594
+ for e in results:
1595
+ findings.extend(_check_inv_1(e))
1596
+ findings.extend(_check_inv_2(e))
1597
+ findings.extend(_check_inv_3(e))
1598
+ findings.extend(_check_inv_4(e))
1599
+ findings.extend(_check_inv_5(e))
1600
+ findings.extend(_check_inv_6(e))
1601
+ findings.extend(_check_inv_7(e))
1602
+ findings.extend(_check_inv_8(e))
1603
+ findings.extend(_check_inv_9(e))
1604
+ findings.extend(_check_inv_10(e))
1605
+ findings.extend(_check_inv_11(e))
1606
+ findings.extend(_check_inv_12(e))
1607
+ findings.extend(_check_inv_13(e))
1608
+ findings.extend(_check_inv_14(e))
1609
+ findings.extend(_check_inv_15(e, manifest_index))
1610
+ findings.extend(_check_inv_16(e))
1611
+ findings.extend(_check_inv_18(e))
1612
+ findings.extend(_check_inv_19(e))
1613
+ findings.extend(_check_matrix(e))
1614
+
1615
+ findings.extend(_check_uncited_invariants(uncited, manifest_index))
1616
+ findings.extend(_check_drift_invariants(drifts, uncited, manifest_index))
1617
+ findings.extend(_check_constraint_violation_invariants(violations, constraint_index))
1618
+ findings.extend(_check_sampling_invariants(samplings))
1619
+ findings.extend(_check_uaf_invariants(uaf, violations, manifest_index))
1620
+
1621
+ # #260 experiment provenance / alignment cross-array invariants.
1622
+ findings.extend(_check_experiment_provenance_invariants(provenance, manifests, declaration))
1623
+ experiment_ids = _build_experiment_id_set(provenance)
1624
+ findings.extend(_check_experiment_alignment_invariants(alignment, manifest_index, experiment_ids))
1625
+
1626
+ return findings
1627
+
1628
+
1629
+ def main(argv: list[str] | None = None) -> int:
1630
+ p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
1631
+ p.add_argument(
1632
+ "--passport",
1633
+ type=Path,
1634
+ required=True,
1635
+ help="Path to a passport-shaped JSON containing the six aggregates the lint reads.",
1636
+ )
1637
+ args = p.parse_args(argv)
1638
+
1639
+ if not args.passport.is_file():
1640
+ print(f"passport not found: {args.passport}", file=sys.stderr)
1641
+ return 2
1642
+
1643
+ try:
1644
+ body = json.loads(args.passport.read_text(encoding="utf-8"))
1645
+ except json.JSONDecodeError as exc:
1646
+ print(f"malformed JSON in {args.passport}: {exc}", file=sys.stderr)
1647
+ return 2
1648
+
1649
+ try:
1650
+ findings = validate_passport(body)
1651
+ except FileNotFoundError as exc:
1652
+ print(f"internal: {exc}", file=sys.stderr)
1653
+ return 2
1654
+
1655
+ if not findings:
1656
+ return 0
1657
+
1658
+ for f in findings:
1659
+ print(f.render())
1660
+ return 1
1661
+
1662
+
1663
+ if __name__ == "__main__":
1664
+ sys.exit(main())