psyclaw 0.27.22 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1048) hide show
  1. package/README.md +11 -5
  2. package/dist/apps/panel/index.html +2 -2
  3. package/dist/src/adapters/pi/extension.js +225 -92
  4. package/dist/src/adapters/pi/extension.js.map +1 -1
  5. package/dist/src/agents/import.js +44 -32
  6. package/dist/src/agents/import.js.map +1 -1
  7. package/dist/src/analysis/hooks.d.ts +1 -0
  8. package/dist/src/analysis/hooks.js +20 -1
  9. package/dist/src/analysis/hooks.js.map +1 -1
  10. package/dist/src/ars/bridge.d.ts +20 -0
  11. package/dist/src/ars/bridge.js +132 -0
  12. package/dist/src/ars/bridge.js.map +1 -0
  13. package/dist/src/ars/contracts.d.ts +55 -0
  14. package/dist/src/ars/contracts.js +2 -0
  15. package/dist/src/ars/contracts.js.map +1 -0
  16. package/dist/src/ars/panel-plan.d.ts +4 -0
  17. package/dist/src/ars/panel-plan.js +11 -0
  18. package/dist/src/ars/panel-plan.js.map +1 -0
  19. package/dist/src/ars/pi-panel-executor.d.ts +30 -0
  20. package/dist/src/ars/pi-panel-executor.js +140 -0
  21. package/dist/src/ars/pi-panel-executor.js.map +1 -0
  22. package/dist/src/ars/profile.d.ts +36 -0
  23. package/dist/src/ars/profile.js +119 -0
  24. package/dist/src/ars/profile.js.map +1 -0
  25. package/dist/src/ars/re-review.d.ts +22 -0
  26. package/dist/src/ars/re-review.js +172 -0
  27. package/dist/src/ars/re-review.js.map +1 -0
  28. package/dist/src/branding.d.ts +2 -4
  29. package/dist/src/branding.js +3 -5
  30. package/dist/src/branding.js.map +1 -1
  31. package/dist/src/bundled-tools.d.ts +3 -0
  32. package/dist/src/bundled-tools.js +21 -0
  33. package/dist/src/bundled-tools.js.map +1 -0
  34. package/dist/src/chat.js +6 -5
  35. package/dist/src/chat.js.map +1 -1
  36. package/dist/src/creation/contracts.d.ts +38 -0
  37. package/dist/src/creation/contracts.js +2 -0
  38. package/dist/src/creation/contracts.js.map +1 -0
  39. package/dist/src/creation/service.d.ts +7 -0
  40. package/dist/src/creation/service.js +196 -0
  41. package/dist/src/creation/service.js.map +1 -0
  42. package/dist/src/index.d.ts +10 -0
  43. package/dist/src/index.js +10 -0
  44. package/dist/src/index.js.map +1 -1
  45. package/dist/src/install/installer.js +32 -10
  46. package/dist/src/install/installer.js.map +1 -1
  47. package/dist/src/orchestration/personas.d.ts +15 -0
  48. package/dist/src/orchestration/personas.js +51 -0
  49. package/dist/src/orchestration/personas.js.map +1 -0
  50. package/dist/src/orchestration/pi-executor.d.ts +1 -0
  51. package/dist/src/orchestration/pi-executor.js +1 -1
  52. package/dist/src/orchestration/pi-executor.js.map +1 -1
  53. package/dist/src/panel/extension.js +29 -8
  54. package/dist/src/panel/extension.js.map +1 -1
  55. package/dist/src/panel/server.js +14 -5
  56. package/dist/src/panel/server.js.map +1 -1
  57. package/dist/src/project/paths.d.ts +3 -0
  58. package/dist/src/project/paths.js +7 -0
  59. package/dist/src/project/paths.js.map +1 -1
  60. package/dist/src/rules/user-rules.d.ts +8 -0
  61. package/dist/src/rules/user-rules.js +36 -0
  62. package/dist/src/rules/user-rules.js.map +1 -0
  63. package/dist/src/skills/contracts.d.ts +4 -4
  64. package/dist/src/skills/recommended.js +1 -1
  65. package/dist/src/skills/registry.js +49 -28
  66. package/dist/src/skills/registry.js.map +1 -1
  67. package/dist/src/style/cli-ui.js +0 -1
  68. package/dist/src/style/cli-ui.js.map +1 -1
  69. package/package.json +14 -3
  70. package/scripts/rebrand-pi.mjs +6 -0
  71. package/skills/recommended/catalog.json +2 -11
  72. package/vendor/ars/.claude/CLAUDE.md +371 -0
  73. package/vendor/ars/.command-invariants.toml +24 -0
  74. package/vendor/ars/CITATION.cff +35 -0
  75. package/vendor/ars/LICENSE +417 -0
  76. package/vendor/ars/MODE_REGISTRY.md +76 -0
  77. package/vendor/ars/NOTICE.md +26 -0
  78. package/vendor/ars/POSITIONING.md +99 -0
  79. package/vendor/ars/PSYCLAW_SOURCE.json +10 -0
  80. package/vendor/ars/README.md +751 -0
  81. package/vendor/ars/SECURITY.md +52 -0
  82. package/vendor/ars/THIRD_PARTY.md +70 -0
  83. package/vendor/ars/academic-paper/SKILL.md +542 -0
  84. package/vendor/ars/academic-paper/agents/abstract_bilingual_agent.md +171 -0
  85. package/vendor/ars/academic-paper/agents/argument_builder_agent.md +276 -0
  86. package/vendor/ars/academic-paper/agents/citation_compliance_agent.md +422 -0
  87. package/vendor/ars/academic-paper/agents/draft_writer_agent.md +656 -0
  88. package/vendor/ars/academic-paper/agents/formatter_agent.md +999 -0
  89. package/vendor/ars/academic-paper/agents/intake_agent.md +393 -0
  90. package/vendor/ars/academic-paper/agents/literature_strategist_agent.md +626 -0
  91. package/vendor/ars/academic-paper/agents/peer_reviewer_agent.md +516 -0
  92. package/vendor/ars/academic-paper/agents/revision_coach_agent.md +334 -0
  93. package/vendor/ars/academic-paper/agents/socratic_mentor_agent.md +527 -0
  94. package/vendor/ars/academic-paper/agents/structure_architect_agent.md +401 -0
  95. package/vendor/ars/academic-paper/agents/visualization_agent.md +441 -0
  96. package/vendor/ars/academic-paper/examples/chinese_paper_example.md +278 -0
  97. package/vendor/ars/academic-paper/examples/clinical_citation_verification_checklist.md +95 -0
  98. package/vendor/ars/academic-paper/examples/clinical_epistemic_status_example.md +100 -0
  99. package/vendor/ars/academic-paper/examples/commitment_ledger_example.md +147 -0
  100. package/vendor/ars/academic-paper/examples/imrad_hei_example.md +234 -0
  101. package/vendor/ars/academic-paper/examples/literature_review_example.md +260 -0
  102. package/vendor/ars/academic-paper/examples/plan_mode_guided_writing.md +600 -0
  103. package/vendor/ars/academic-paper/examples/revision_mode_example.md +344 -0
  104. package/vendor/ars/academic-paper/examples/revision_recovery_example.md +506 -0
  105. package/vendor/ars/academic-paper/examples/version_family_reconciliation_example.md +89 -0
  106. package/vendor/ars/academic-paper/references/abstract_writing_guide.md +169 -0
  107. package/vendor/ars/academic-paper/references/academic_writing_style.md +188 -0
  108. package/vendor/ars/academic-paper/references/anti_leakage_protocol.md +83 -0
  109. package/vendor/ars/academic-paper/references/apa7_chinese_citation_guide.md +364 -0
  110. package/vendor/ars/academic-paper/references/apa7_extended_guide.md +198 -0
  111. package/vendor/ars/academic-paper/references/changelog.md +11 -0
  112. package/vendor/ars/academic-paper/references/citation_format_switcher.md +228 -0
  113. package/vendor/ars/academic-paper/references/committee_correspondence_protocol.md +158 -0
  114. package/vendor/ars/academic-paper/references/credit_authorship_guide.md +308 -0
  115. package/vendor/ars/academic-paper/references/disclosure_mode_protocol.md +478 -0
  116. package/vendor/ars/academic-paper/references/domain_evidence_profiles.md +38 -0
  117. package/vendor/ars/academic-paper/references/failure_paths.md +349 -0
  118. package/vendor/ars/academic-paper/references/funding_statement_guide.md +319 -0
  119. package/vendor/ars/academic-paper/references/hei_domain_glossary.md +169 -0
  120. package/vendor/ars/academic-paper/references/intro_title_rhetoric_guide.md +114 -0
  121. package/vendor/ars/academic-paper/references/journal_submission_guide.md +249 -0
  122. package/vendor/ars/academic-paper/references/latex_template_reference.md +378 -0
  123. package/vendor/ars/academic-paper/references/mode_selection_guide.md +378 -0
  124. package/vendor/ars/academic-paper/references/paper_structure_patterns.md +330 -0
  125. package/vendor/ars/academic-paper/references/plan_mode_protocol.md +112 -0
  126. package/vendor/ars/academic-paper/references/policy_anchor_disclosure_protocol.md +200 -0
  127. package/vendor/ars/academic-paper/references/policy_anchor_table.md +157 -0
  128. package/vendor/ars/academic-paper/references/revision_patch_protocol.md +173 -0
  129. package/vendor/ars/academic-paper/references/statistical_visualization_standards.md +750 -0
  130. package/vendor/ars/academic-paper/references/venue_disclosure_policies.md +259 -0
  131. package/vendor/ars/academic-paper/references/vlm_figure_verification.md +126 -0
  132. package/vendor/ars/academic-paper/references/workflow_phase_details.md +135 -0
  133. package/vendor/ars/academic-paper/references/writing_judgment_framework.md +59 -0
  134. package/vendor/ars/academic-paper/references/writing_quality_check.md +173 -0
  135. package/vendor/ars/academic-paper/templates/bilingual_abstract_template.md +78 -0
  136. package/vendor/ars/academic-paper/templates/case_study_template.md +129 -0
  137. package/vendor/ars/academic-paper/templates/conference_paper_template.md +108 -0
  138. package/vendor/ars/academic-paper/templates/credit_statement_template.md +132 -0
  139. package/vendor/ars/academic-paper/templates/funding_statement_template.md +290 -0
  140. package/vendor/ars/academic-paper/templates/imrad_template.md +183 -0
  141. package/vendor/ars/academic-paper/templates/latex_article_template.tex +199 -0
  142. package/vendor/ars/academic-paper/templates/literature_review_template.md +135 -0
  143. package/vendor/ars/academic-paper/templates/policy_brief_template.md +139 -0
  144. package/vendor/ars/academic-paper/templates/revision_tracking_template.md +199 -0
  145. package/vendor/ars/academic-paper/templates/theoretical_paper_template.md +119 -0
  146. package/vendor/ars/academic-paper-reviewer/SKILL.md +491 -0
  147. package/vendor/ars/academic-paper-reviewer/agents/devils_advocate_reviewer_agent.md +443 -0
  148. package/vendor/ars/academic-paper-reviewer/agents/domain_reviewer_agent.md +412 -0
  149. package/vendor/ars/academic-paper-reviewer/agents/editorial_synthesizer_agent.md +478 -0
  150. package/vendor/ars/academic-paper-reviewer/agents/eic_agent.md +339 -0
  151. package/vendor/ars/academic-paper-reviewer/agents/field_analyst_agent.md +221 -0
  152. package/vendor/ars/academic-paper-reviewer/agents/methodology_reviewer_agent.md +449 -0
  153. package/vendor/ars/academic-paper-reviewer/agents/perspective_reviewer_agent.md +427 -0
  154. package/vendor/ars/academic-paper-reviewer/examples/hei_paper_review_example.md +391 -0
  155. package/vendor/ars/academic-paper-reviewer/examples/interdisciplinary_review_example.md +299 -0
  156. package/vendor/ars/academic-paper-reviewer/examples/subclaim_decomposition_example.md +80 -0
  157. package/vendor/ars/academic-paper-reviewer/references/calibration_mode_protocol.md +256 -0
  158. package/vendor/ars/academic-paper-reviewer/references/changelog.md +10 -0
  159. package/vendor/ars/academic-paper-reviewer/references/editorial_decision_standards.md +236 -0
  160. package/vendor/ars/academic-paper-reviewer/references/guided_mode_protocol.md +34 -0
  161. package/vendor/ars/academic-paper-reviewer/references/integration_guide.md +15 -0
  162. package/vendor/ars/academic-paper-reviewer/references/quality_rubrics.md +84 -0
  163. package/vendor/ars/academic-paper-reviewer/references/re_review_mode_protocol.md +340 -0
  164. package/vendor/ars/academic-paper-reviewer/references/review_criteria_framework.md +98 -0
  165. package/vendor/ars/academic-paper-reviewer/references/review_panel_provenance_protocol.md +197 -0
  166. package/vendor/ars/academic-paper-reviewer/references/review_quality_thinking.md +58 -0
  167. package/vendor/ars/academic-paper-reviewer/references/reviewer_sprint_prompt_source.md +324 -0
  168. package/vendor/ars/academic-paper-reviewer/references/sprint_contract_protocol.md +296 -0
  169. package/vendor/ars/academic-paper-reviewer/references/statistical_reporting_standards.md +505 -0
  170. package/vendor/ars/academic-paper-reviewer/references/top_journals_by_field.md +206 -0
  171. package/vendor/ars/academic-paper-reviewer/templates/editorial_decision_template.md +235 -0
  172. package/vendor/ars/academic-paper-reviewer/templates/peer_review_report_template.md +305 -0
  173. package/vendor/ars/academic-paper-reviewer/templates/revision_response_template.md +248 -0
  174. package/vendor/ars/academic-pipeline/SKILL.md +736 -0
  175. package/vendor/ars/academic-pipeline/agents/claim_ref_alignment_audit_agent.md +382 -0
  176. package/vendor/ars/academic-pipeline/agents/collaboration_depth_agent.md +164 -0
  177. package/vendor/ars/academic-pipeline/agents/integrity_verification_agent.md +870 -0
  178. package/vendor/ars/academic-pipeline/agents/pipeline_orchestrator_agent.md +1379 -0
  179. package/vendor/ars/academic-pipeline/agents/state_tracker_agent.md +622 -0
  180. package/vendor/ars/academic-pipeline/examples/full_pipeline_example.md +482 -0
  181. package/vendor/ars/academic-pipeline/examples/integrity_failure_recovery.md +389 -0
  182. package/vendor/ars/academic-pipeline/examples/mid_entry_example.md +414 -0
  183. package/vendor/ars/academic-pipeline/references/adapters/.gitkeep +0 -0
  184. package/vendor/ars/academic-pipeline/references/adapters/overview.md +153 -0
  185. package/vendor/ars/academic-pipeline/references/ai_research_failure_modes.md +185 -0
  186. package/vendor/ars/academic-pipeline/references/changelog.md +14 -0
  187. package/vendor/ars/academic-pipeline/references/claim_audit_calibration_protocol.md +175 -0
  188. package/vendor/ars/academic-pipeline/references/claim_verification_protocol.md +282 -0
  189. package/vendor/ars/academic-pipeline/references/external_review_protocol.md +131 -0
  190. package/vendor/ars/academic-pipeline/references/integrity_review_protocol.md +110 -0
  191. package/vendor/ars/academic-pipeline/references/literature_corpus_consumers.md +193 -0
  192. package/vendor/ars/academic-pipeline/references/mode_advisor.md +135 -0
  193. package/vendor/ars/academic-pipeline/references/passport_as_reset_boundary.md +132 -0
  194. package/vendor/ars/academic-pipeline/references/pipeline_state_machine.md +405 -0
  195. package/vendor/ars/academic-pipeline/references/plagiarism_detection_protocol.md +239 -0
  196. package/vendor/ars/academic-pipeline/references/process_summary_protocol.md +209 -0
  197. package/vendor/ars/academic-pipeline/references/progress_dashboard_template.md +38 -0
  198. package/vendor/ars/academic-pipeline/references/reinforcement_content.md +15 -0
  199. package/vendor/ars/academic-pipeline/references/reproducibility_audit.md +55 -0
  200. package/vendor/ars/academic-pipeline/references/score_trajectory_protocol.md +78 -0
  201. package/vendor/ars/academic-pipeline/references/team_collaboration_protocol.md +261 -0
  202. package/vendor/ars/academic-pipeline/references/two_stage_review_protocol.md +27 -0
  203. package/vendor/ars/academic-pipeline/templates/pipeline_status_template.md +146 -0
  204. package/vendor/ars/agents/report_compiler_agent.md +341 -0
  205. package/vendor/ars/agents/research_architect_agent.md +298 -0
  206. package/vendor/ars/agents/synthesis_agent.md +356 -0
  207. package/vendor/ars/commands/ars-3w.md +10 -0
  208. package/vendor/ars/commands/ars-abstract.md +10 -0
  209. package/vendor/ars/commands/ars-cache-invalidate.md +20 -0
  210. package/vendor/ars/commands/ars-citation-check.md +10 -0
  211. package/vendor/ars/commands/ars-disclosure.md +10 -0
  212. package/vendor/ars/commands/ars-format-convert.md +10 -0
  213. package/vendor/ars/commands/ars-full.md +9 -0
  214. package/vendor/ars/commands/ars-lit-review.md +12 -0
  215. package/vendor/ars/commands/ars-mark-read.md +18 -0
  216. package/vendor/ars/commands/ars-outline.md +10 -0
  217. package/vendor/ars/commands/ars-plan.md +10 -0
  218. package/vendor/ars/commands/ars-rebuttal-audit.md +12 -0
  219. package/vendor/ars/commands/ars-reviewer.md +9 -0
  220. package/vendor/ars/commands/ars-revision-coach.md +9 -0
  221. package/vendor/ars/commands/ars-revision.md +10 -0
  222. package/vendor/ars/commands/ars-unmark-read.md +16 -0
  223. package/vendor/ars/deep-research/SKILL.md +600 -0
  224. package/vendor/ars/deep-research/agents/bibliography_agent.md +473 -0
  225. package/vendor/ars/deep-research/agents/devils_advocate_agent.md +192 -0
  226. package/vendor/ars/deep-research/agents/editor_in_chief_agent.md +167 -0
  227. package/vendor/ars/deep-research/agents/ethics_review_agent.md +267 -0
  228. package/vendor/ars/deep-research/agents/meta_analysis_agent.md +325 -0
  229. package/vendor/ars/deep-research/agents/monitoring_agent.md +209 -0
  230. package/vendor/ars/deep-research/agents/report_compiler_agent.md +341 -0
  231. package/vendor/ars/deep-research/agents/research_architect_agent.md +298 -0
  232. package/vendor/ars/deep-research/agents/research_question_agent.md +216 -0
  233. package/vendor/ars/deep-research/agents/risk_of_bias_agent.md +231 -0
  234. package/vendor/ars/deep-research/agents/socratic_mentor_agent.md +764 -0
  235. package/vendor/ars/deep-research/agents/source_verification_agent.md +219 -0
  236. package/vendor/ars/deep-research/agents/synthesis_agent.md +356 -0
  237. package/vendor/ars/deep-research/agents/timeline_extraction_agent.md +99 -0
  238. package/vendor/ars/deep-research/examples/exploratory_research.md +157 -0
  239. package/vendor/ars/deep-research/examples/fact_check_mode.md +173 -0
  240. package/vendor/ars/deep-research/examples/handoff_to_paper.md +318 -0
  241. package/vendor/ars/deep-research/examples/idea_diversity_coverage_gap_advisory.md +64 -0
  242. package/vendor/ars/deep-research/examples/policy_analysis.md +161 -0
  243. package/vendor/ars/deep-research/examples/review_mode.md +253 -0
  244. package/vendor/ars/deep-research/examples/socratic_guided_research.md +331 -0
  245. package/vendor/ars/deep-research/examples/systematic_review.md +133 -0
  246. package/vendor/ars/deep-research/references/apa7_style_guide.md +162 -0
  247. package/vendor/ars/deep-research/references/argumentation_reasoning_framework.md +68 -0
  248. package/vendor/ars/deep-research/references/arxiv_api_protocol.md +76 -0
  249. package/vendor/ars/deep-research/references/changelog.md +22 -0
  250. package/vendor/ars/deep-research/references/chinese_literature_api_protocol.md +317 -0
  251. package/vendor/ars/deep-research/references/cross_agent_quality_definitions.md +14 -0
  252. package/vendor/ars/deep-research/references/crossref_api_protocol.md +84 -0
  253. package/vendor/ars/deep-research/references/equator_reporting_guidelines.md +482 -0
  254. package/vendor/ars/deep-research/references/ethics_checklist.md +282 -0
  255. package/vendor/ars/deep-research/references/failure_paths.md +355 -0
  256. package/vendor/ars/deep-research/references/interdisciplinary_bridges.md +292 -0
  257. package/vendor/ars/deep-research/references/irb_decision_tree.md +315 -0
  258. package/vendor/ars/deep-research/references/literature_monitoring_strategies.md +263 -0
  259. package/vendor/ars/deep-research/references/logical_fallacies.md +192 -0
  260. package/vendor/ars/deep-research/references/methodology_patterns.md +462 -0
  261. package/vendor/ars/deep-research/references/mode_selection_guide.md +331 -0
  262. package/vendor/ars/deep-research/references/openalex_api_protocol.md +82 -0
  263. package/vendor/ars/deep-research/references/preregistration_guide.md +324 -0
  264. package/vendor/ars/deep-research/references/semantic_scholar_api_protocol.md +107 -0
  265. package/vendor/ars/deep-research/references/socratic_mode_protocol.md +99 -0
  266. package/vendor/ars/deep-research/references/socratic_questioning_framework.md +232 -0
  267. package/vendor/ars/deep-research/references/source_quality_hierarchy.md +188 -0
  268. package/vendor/ars/deep-research/references/systematic_review_protocol.md +95 -0
  269. package/vendor/ars/deep-research/references/systematic_review_toolkit.md +353 -0
  270. package/vendor/ars/deep-research/templates/evidence_assessment_template.md +127 -0
  271. package/vendor/ars/deep-research/templates/literature_matrix_template.md +85 -0
  272. package/vendor/ars/deep-research/templates/preregistration_template.md +318 -0
  273. package/vendor/ars/deep-research/templates/prisma_protocol_template.md +248 -0
  274. package/vendor/ars/deep-research/templates/prisma_report_template.md +415 -0
  275. package/vendor/ars/deep-research/templates/research_brief_template.md +93 -0
  276. package/vendor/ars/package.json +24 -0
  277. package/vendor/ars/pi/README.md +161 -0
  278. package/vendor/ars/pi/package.json +26 -0
  279. package/vendor/ars/pi/wrapper.js +193 -0
  280. package/vendor/ars/pi/wrapper.test.mjs +201 -0
  281. package/vendor/ars/pyproject.toml +2 -0
  282. package/vendor/ars/requirements-pdf-content-classifier.txt +5 -0
  283. package/vendor/ars/scripts/_block_parser.py +396 -0
  284. package/vendor/ars/scripts/_ci_pytest_manifest.toml +661 -0
  285. package/vendor/ars/scripts/_claim_audit_constants.py +268 -0
  286. package/vendor/ars/scripts/_e4_evidence.py +110 -0
  287. package/vendor/ars/scripts/_eval_threshold_gate.py +73 -0
  288. package/vendor/ars/scripts/_markdown_lint_util.py +224 -0
  289. package/vendor/ars/scripts/_next_verified_at_ms.py +176 -0
  290. package/vendor/ars/scripts/_passport_yaml.py +53 -0
  291. package/vendor/ars/scripts/_skill_lint.py +254 -0
  292. package/vendor/ars/scripts/_text_similarity.py +141 -0
  293. package/vendor/ars/scripts/adapters/README.md +89 -0
  294. package/vendor/ars/scripts/adapters/_common.py +209 -0
  295. package/vendor/ars/scripts/adapters/examples/folder_scan/expected_passport.yaml +25 -0
  296. package/vendor/ars/scripts/adapters/examples/folder_scan/expected_rejection_log.yaml +18 -0
  297. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/Chen2024_AIAssessment.pdf +0 -0
  298. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/Wang_2023_formative_feedback.pdf +0 -0
  299. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture/paper1.pdf +0 -0
  300. package/vendor/ars/scripts/adapters/examples/folder_scan/input_fixture//344/270/255/346/226/207/346/252/224/345/220/215_2024.pdf +0 -0
  301. package/vendor/ars/scripts/adapters/examples/obsidian/expected_passport.yaml +40 -0
  302. package/vendor/ars/scripts/adapters/examples/obsidian/expected_rejection_log.yaml +13 -0
  303. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/.gitkeep +0 -0
  304. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/.obsidian/app.json +1 -0
  305. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/_templates/tmpl.md +7 -0
  306. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/chen2024ai.md +13 -0
  307. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/invalid.md +1 -0
  308. package/vendor/ars/scripts/adapters/examples/obsidian/input_fixture/vault/wang2023formative.md +10 -0
  309. package/vendor/ars/scripts/adapters/examples/zotero/expected_passport.yaml +34 -0
  310. package/vendor/ars/scripts/adapters/examples/zotero/expected_rejection_log.yaml +34 -0
  311. package/vendor/ars/scripts/adapters/examples/zotero/input_fixture/.gitkeep +0 -0
  312. package/vendor/ars/scripts/adapters/examples/zotero/input_fixture/export.json +43 -0
  313. package/vendor/ars/scripts/adapters/folder_scan.py +214 -0
  314. package/vendor/ars/scripts/adapters/obsidian.py +336 -0
  315. package/vendor/ars/scripts/adapters/tests/.gitkeep +0 -0
  316. package/vendor/ars/scripts/adapters/tests/conftest.py +77 -0
  317. package/vendor/ars/scripts/adapters/tests/test_check_corpus_consumer_protocol.py +632 -0
  318. package/vendor/ars/scripts/adapters/tests/test_check_literature_corpus_schema.py +631 -0
  319. package/vendor/ars/scripts/adapters/tests/test_common.py +365 -0
  320. package/vendor/ars/scripts/adapters/tests/test_conftest.py +96 -0
  321. package/vendor/ars/scripts/adapters/tests/test_folder_scan.py +260 -0
  322. package/vendor/ars/scripts/adapters/tests/test_literature_corpus_entry_schema.py +745 -0
  323. package/vendor/ars/scripts/adapters/tests/test_obsidian.py +357 -0
  324. package/vendor/ars/scripts/adapters/tests/test_rejection_log_schema.py +271 -0
  325. package/vendor/ars/scripts/adapters/tests/test_sync_adapter_docs.py +88 -0
  326. package/vendor/ars/scripts/adapters/tests/test_zotero.py +454 -0
  327. package/vendor/ars/scripts/adapters/zotero.py +318 -0
  328. package/vendor/ars/scripts/adjudication_activity.py +1592 -0
  329. package/vendor/ars/scripts/announce-ars-loaded.sh +144 -0
  330. package/vendor/ars/scripts/ars_anchorize_draft.py +170 -0
  331. package/vendor/ars/scripts/ars_apply_revision_patch.py +912 -0
  332. package/vendor/ars/scripts/ars_cache_invalidate.py +40 -0
  333. package/vendor/ars/scripts/ars_mark_read.py +521 -0
  334. package/vendor/ars/scripts/ars_phase_scope_manifest.json +33 -0
  335. package/vendor/ars/scripts/ars_update_check.sh +215 -0
  336. package/vendor/ars/scripts/ars_write_scope_guard.py +506 -0
  337. package/vendor/ars/scripts/arxiv_client.py +222 -0
  338. package/vendor/ars/scripts/audit_snapshot.py +572 -0
  339. package/vendor/ars/scripts/bibliographic_integrity_signals.py +800 -0
  340. package/vendor/ars/scripts/bootstrap_timeline_yaml.py +146 -0
  341. package/vendor/ars/scripts/build_claim_standing_candidate_ledger.py +1238 -0
  342. package/vendor/ars/scripts/build_claim_standing_query_plan.py +643 -0
  343. package/vendor/ars/scripts/build_content_coverage_advisory.py +1205 -0
  344. package/vendor/ars/scripts/build_cross_document_consistency_advisory.py +2332 -0
  345. package/vendor/ars/scripts/build_review_pathway_rule_trace.py +830 -0
  346. package/vendor/ars/scripts/build_submission_packet_manifest.py +2310 -0
  347. package/vendor/ars/scripts/check_215_field_norm.py +173 -0
  348. package/vendor/ars/scripts/check_216_surface_form.py +250 -0
  349. package/vendor/ars/scripts/check_268_nested_commitment_ledger.py +180 -0
  350. package/vendor/ars/scripts/check_390_revision_patch_discipline.py +296 -0
  351. package/vendor/ars/scripts/check_392_citation_verification_intake.py +126 -0
  352. package/vendor/ars/scripts/check_394_submission_policy.py +178 -0
  353. package/vendor/ars/scripts/check_439_format_profile.py +307 -0
  354. package/vendor/ars/scripts/check_619_disclosure_closeout.py +237 -0
  355. package/vendor/ars/scripts/check_630_codex_subscription_transport.py +456 -0
  356. package/vendor/ars/scripts/check_669_review_pathway_rule_trace.py +662 -0
  357. package/vendor/ars/scripts/check_670_revision_roadmap_integration.py +518 -0
  358. package/vendor/ars/scripts/check_673_adjudication_activity.py +684 -0
  359. package/vendor/ars/scripts/check_684_review_criteria_binding.py +557 -0
  360. package/vendor/ars/scripts/check_agents_mirror_sync.py +115 -0
  361. package/vendor/ars/scripts/check_audit_artifact_consistency.py +2313 -0
  362. package/vendor/ars/scripts/check_benchmark_report.py +79 -0
  363. package/vendor/ars/scripts/check_bibliographic_integrity_signals.py +831 -0
  364. package/vendor/ars/scripts/check_calibration_tiers.py +235 -0
  365. package/vendor/ars/scripts/check_changelog_covers_merges.py +289 -0
  366. package/vendor/ars/scripts/check_ci_pytest_manifest.py +204 -0
  367. package/vendor/ars/scripts/check_claim_audit_consistency.py +1664 -0
  368. package/vendor/ars/scripts/check_claim_standing_candidate_ledger_integration.py +500 -0
  369. package/vendor/ars/scripts/check_claim_standing_freshness.py +253 -0
  370. package/vendor/ars/scripts/check_claim_standing_transmissions.py +449 -0
  371. package/vendor/ars/scripts/check_collaboration_depth_rubric.py +180 -0
  372. package/vendor/ars/scripts/check_command_frontmatter_name.py +116 -0
  373. package/vendor/ars/scripts/check_committee_correspondence.py +333 -0
  374. package/vendor/ars/scripts/check_compliance_report.py +108 -0
  375. package/vendor/ars/scripts/check_content_coverage_advisory_integration.py +796 -0
  376. package/vendor/ars/scripts/check_control_availability.py +172 -0
  377. package/vendor/ars/scripts/check_corpus_consumer_protocol.py +404 -0
  378. package/vendor/ars/scripts/check_cross_document_consistency_advisory_integration.py +1191 -0
  379. package/vendor/ars/scripts/check_cross_model_handoff_contract.py +234 -0
  380. package/vendor/ars/scripts/check_cross_model_verification_sync.py +261 -0
  381. package/vendor/ars/scripts/check_data_access_level.py +131 -0
  382. package/vendor/ars/scripts/check_data_flows.py +252 -0
  383. package/vendor/ars/scripts/check_decision_contract.py +464 -0
  384. package/vendor/ars/scripts/check_degradation_registry.py +326 -0
  385. package/vendor/ars/scripts/check_distribution_surface_claims.py +226 -0
  386. package/vendor/ars/scripts/check_domain_evidence_profile.py +538 -0
  387. package/vendor/ars/scripts/check_e4_promotion.py +195 -0
  388. package/vendor/ars/scripts/check_evals_gold_set.py +279 -0
  389. package/vendor/ars/scripts/check_evidence_row_integration.py +396 -0
  390. package/vendor/ars/scripts/check_experiment_provenance.py +117 -0
  391. package/vendor/ars/scripts/check_field_norm_severity.py +144 -0
  392. package/vendor/ars/scripts/check_firm_rules_sync.py +375 -0
  393. package/vendor/ars/scripts/check_heldout_measurement_report.py +1179 -0
  394. package/vendor/ars/scripts/check_human_subjects_output_contract.py +139 -0
  395. package/vendor/ars/scripts/check_human_subjects_reference_migration.py +844 -0
  396. package/vendor/ars/scripts/check_indirect_prompt_injection_no_call.py +328 -0
  397. package/vendor/ars/scripts/check_instruction_data_boundary.py +236 -0
  398. package/vendor/ars/scripts/check_judge_prompt_version.py +125 -0
  399. package/vendor/ars/scripts/check_literature_corpus_schema.py +402 -0
  400. package/vendor/ars/scripts/check_model_tiering.py +223 -0
  401. package/vendor/ars/scripts/check_panel_synthesis.py +1331 -0
  402. package/vendor/ars/scripts/check_passport_reset_contract.py +214 -0
  403. package/vendor/ars/scripts/check_pattern_eval_manifest.py +422 -0
  404. package/vendor/ars/scripts/check_persuasion_invariance_fixtures.py +637 -0
  405. package/vendor/ars/scripts/check_phase_conformance.py +2180 -0
  406. package/vendor/ars/scripts/check_pipeline_boundary_semantics.py +600 -0
  407. package/vendor/ars/scripts/check_pipeline_integrity.py +340 -0
  408. package/vendor/ars/scripts/check_policy_anchor_protocol.py +159 -0
  409. package/vendor/ars/scripts/check_policy_anchor_table.py +286 -0
  410. package/vendor/ars/scripts/check_preprint_venues_consistency.py +123 -0
  411. package/vendor/ars/scripts/check_prisma_trAIce_freshness.py +84 -0
  412. package/vendor/ars/scripts/check_promotion_bakeoff_preregistration.py +1303 -0
  413. package/vendor/ars/scripts/check_ranking_lift.py +323 -0
  414. package/vendor/ars/scripts/check_re_review_synthesis.py +2719 -0
  415. package/vendor/ars/scripts/check_receipt_enum_sync.py +203 -0
  416. package/vendor/ars/scripts/check_repro_lock.py +85 -0
  417. package/vendor/ars/scripts/check_review_pathway_output.py +276 -0
  418. package/vendor/ars/scripts/check_reviewer_data_fences.py +224 -0
  419. package/vendor/ars/scripts/check_reviewer_finding_contract.py +783 -0
  420. package/vendor/ars/scripts/check_reviewer_role_label.py +447 -0
  421. package/vendor/ars/scripts/check_reviewer_scoring_honesty.py +292 -0
  422. package/vendor/ars/scripts/check_reviewer_sprint_prompt_sync.py +409 -0
  423. package/vendor/ars/scripts/check_revision_claim_drift_suite_v2.py +1622 -0
  424. package/vendor/ars/scripts/check_revision_token_conservation.py +263 -0
  425. package/vendor/ars/scripts/check_risk_register.py +280 -0
  426. package/vendor/ars/scripts/check_role_scoped_contract.py +702 -0
  427. package/vendor/ars/scripts/check_rq_framing_patterns.py +184 -0
  428. package/vendor/ars/scripts/check_rubric_weight_consistency.py +32 -0
  429. package/vendor/ars/scripts/check_seeded_defect_fixtures.py +425 -0
  430. package/vendor/ars/scripts/check_setup_cross_model_parity.py +139 -0
  431. package/vendor/ars/scripts/check_spec_consistency.py +1249 -0
  432. package/vendor/ars/scripts/check_sprint_contract.py +371 -0
  433. package/vendor/ars/scripts/check_stage_capability_matrix.py +784 -0
  434. package/vendor/ars/scripts/check_submission_packet_manifest_integration.py +358 -0
  435. package/vendor/ars/scripts/check_surface_form_parity.py +445 -0
  436. package/vendor/ars/scripts/check_task_type.py +22 -0
  437. package/vendor/ars/scripts/check_tools_allowlist.py +538 -0
  438. package/vendor/ars/scripts/check_tortured_phrase_screening_integration.py +1974 -0
  439. package/vendor/ars/scripts/check_v3_10_134_write_scope.py +286 -0
  440. package/vendor/ars/scripts/check_v3_10_policy.py +656 -0
  441. package/vendor/ars/scripts/check_v3_6_6_ab_manifest.py +364 -0
  442. package/vendor/ars/scripts/check_v3_6_7_pattern_protection.py +1366 -0
  443. package/vendor/ars/scripts/check_v3_6_8_audit_scope_block.py +464 -0
  444. package/vendor/ars/scripts/check_v3_6_8_cite_provenance_pipeline.py +231 -0
  445. package/vendor/ars/scripts/check_v3_6_8_frontmatter_trust_schema.py +248 -0
  446. package/vendor/ars/scripts/check_v3_6_8_mark_read_commands.py +79 -0
  447. package/vendor/ars/scripts/check_v3_6_8_pattern_protection.py +941 -0
  448. package/vendor/ars/scripts/check_v3_7_3_three_layer_citation.py +318 -0
  449. package/vendor/ars/scripts/check_v3_8_annotation_literal_sync.py +228 -0
  450. package/vendor/ars/scripts/check_v3_9_0_triangulation.py +366 -0
  451. package/vendor/ars/scripts/check_v3_9_2_phase_boundary.py +270 -0
  452. package/vendor/ars/scripts/check_v3_9_4_temporal_verification.py +139 -0
  453. package/vendor/ars/scripts/check_venue_disclosure_policies.py +63 -0
  454. package/vendor/ars/scripts/check_version_consistency.py +826 -0
  455. package/vendor/ars/scripts/check_workflow_classification.py +223 -0
  456. package/vendor/ars/scripts/chinese_literature_client.py +1938 -0
  457. package/vendor/ars/scripts/citation_verification_summary.py +85 -0
  458. package/vendor/ars/scripts/claim_audit_calibration.py +517 -0
  459. package/vendor/ars/scripts/claim_audit_finalizer.py +456 -0
  460. package/vendor/ars/scripts/claim_audit_pipeline.py +1594 -0
  461. package/vendor/ars/scripts/claim_registry_coverage.py +493 -0
  462. package/vendor/ars/scripts/claim_standing_discovery.py +784 -0
  463. package/vendor/ars/scripts/claim_standing_stance_runner.py +758 -0
  464. package/vendor/ars/scripts/claim_standing_stance_scorer.py +239 -0
  465. package/vendor/ars/scripts/claim_strength_drift_disposition.py +666 -0
  466. package/vendor/ars/scripts/contamination_signals.py +689 -0
  467. package/vendor/ars/scripts/corpus_consumer_manifest.json +19 -0
  468. package/vendor/ars/scripts/cross_model_codex_transport.py +1374 -0
  469. package/vendor/ars/scripts/cross_model_codex_verify.sh +6 -0
  470. package/vendor/ars/scripts/cross_model_handoff.py +359 -0
  471. package/vendor/ars/scripts/cross_model_smoke_test.sh +183 -0
  472. package/vendor/ars/scripts/cross_model_smoke_test_codex.sh +35 -0
  473. package/vendor/ars/scripts/cross_model_verification/gemini_is_grounded.jq +45 -0
  474. package/vendor/ars/scripts/cross_model_verification/gemini_sources.jq +45 -0
  475. package/vendor/ars/scripts/cross_model_verification/normalize_compat_verdict.py +57 -0
  476. package/vendor/ars/scripts/cross_model_verification/openai_has_completed_web_search.jq +15 -0
  477. package/vendor/ars/scripts/cross_model_verification/openai_sources.jq +17 -0
  478. package/vendor/ars/scripts/cross_model_verification/openai_text.jq +11 -0
  479. package/vendor/ars/scripts/crossref_client.py +225 -0
  480. package/vendor/ars/scripts/dispatch_e4_panel.py +2731 -0
  481. package/vendor/ars/scripts/evidence_rows.py +2043 -0
  482. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3.json +32 -0
  483. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_input.json +27 -0
  484. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_prime.json +32 -0
  485. package/vendor/ars/scripts/fixtures/adjudication_activity/author_stage3_prime_input.json +27 -0
  486. package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_override.json +50 -0
  487. package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_override_action.json +14 -0
  488. package/vendor/ars/scripts/fixtures/adjudication_activity/compliance_pass.json +44 -0
  489. package/vendor/ars/scripts/fixtures/adjudication_activity/explicit_user_request_log.json +26 -0
  490. package/vendor/ars/scripts/fixtures/adjudication_activity/mandatory_checkpoint_log.json +116 -0
  491. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/README.md +56 -0
  492. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +24 -0
  493. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
  494. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
  495. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a1_pass_with_p1/2026-04-30T15-22-04Z-d8f3.verdict.yaml +19 -0
  496. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/negative/a7_orphan_completion/2026-04-30T15-22-04Z-d8f3.jsonl +3 -0
  497. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +26 -0
  498. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
  499. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
  500. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/persisted_minor/2026-04-30T15-22-04Z-d8f3.verdict.yaml +19 -0
  501. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.audit_artifact_entry.json +24 -0
  502. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.jsonl +4 -0
  503. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.meta.json +43 -0
  504. package/vendor/ars/scripts/fixtures/audit_artifact_consistency/positive/proposal_pass/2026-04-30T15-22-04Z-d8f3.verdict.yaml +12 -0
  505. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/retraction.json +46 -0
  506. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/retraction_check_attestation.json +44 -0
  507. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase.json +44 -0
  508. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase_v1_2_abstract_missing.json +107 -0
  509. package/vendor/ars/scripts/fixtures/bibliographic_integrity_signals/tortured_phrase_v1_2_detected.json +124 -0
  510. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/expected_outcomes.json +77 -0
  511. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/manifest.yaml +37 -0
  512. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/001-valid-doi-test.json +20 -0
  513. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/002-valid-arxiv-test.json +19 -0
  514. package/vendor/ars/scripts/fixtures/check_evals_gold_set/clean/tuples/003-fabricated-test.json +20 -0
  515. package/vendor/ars/scripts/fixtures/claim_audit_calibration/gold_set.json +344 -0
  516. package/vendor/ars/scripts/fixtures/claim_standing_candidate_ledger/query_plan.json +108 -0
  517. package/vendor/ars/scripts/fixtures/claim_standing_candidate_ledger/retrieval_input.json +179 -0
  518. package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/concern_tracker.json +129 -0
  519. package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/response_skeleton.md +19 -0
  520. package/vendor/ars/scripts/fixtures/committee_correspondence/16fd83f6aec7/source_letter.txt +9 -0
  521. package/vendor/ars/scripts/fixtures/content_coverage_advisory/base_draft.json +48 -0
  522. package/vendor/ars/scripts/fixtures/content_coverage_advisory/base_inventory.json +38 -0
  523. package/vendor/ars/scripts/fixtures/content_coverage_advisory/packet/consent.txt +1 -0
  524. package/vendor/ars/scripts/fixtures/content_coverage_advisory/session_sources.json +3 -0
  525. package/vendor/ars/scripts/fixtures/cross_document_consistency/README.md +6 -0
  526. package/vendor/ars/scripts/fixtures/cross_document_consistency/accepted_draft.md +35 -0
  527. package/vendor/ars/scripts/fixtures/cross_document_consistency/cases.json +75 -0
  528. package/vendor/ars/scripts/fixtures/cross_document_consistency/preregistration.md +5 -0
  529. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/forbidden_event.jsonl +4 -0
  530. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/grounded_verified.jsonl +3 -0
  531. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/malformed.jsonl +3 -0
  532. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/missing_search.jsonl +2 -0
  533. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/multiple_finals.jsonl +4 -0
  534. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/not_found.jsonl +3 -0
  535. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/unbound_source.jsonl +3 -0
  536. package/vendor/ars/scripts/fixtures/cross_model_codex_transport/wrong_search_shape.jsonl +3 -0
  537. package/vendor/ars/scripts/fixtures/evidence_rows/phase_e_inputs.json +114 -0
  538. package/vendor/ars/scripts/fixtures/evidence_rows/session_sources.json +5 -0
  539. package/vendor/ars/scripts/fixtures/human_subjects_authority/cross-border-us-tw-gdpr.json +71 -0
  540. package/vendor/ars/scripts/fixtures/human_subjects_authority/gdpr-member-state-unresolved.json +62 -0
  541. package/vendor/ars/scripts/fixtures/human_subjects_authority/missing-data-axis.json +45 -0
  542. package/vendor/ars/scripts/fixtures/human_subjects_authority/no-profile.json +33 -0
  543. package/vendor/ars/scripts/fixtures/human_subjects_authority/tw-gdpr-two-axis.json +74 -0
  544. package/vendor/ars/scripts/fixtures/human_subjects_authority/us-gdpr-two-axis.json +74 -0
  545. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/lint-near-misses.json +72 -0
  546. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/no-profile-request.json +18 -0
  547. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/tw-candidates-request.json +63 -0
  548. package/vendor/ars/scripts/fixtures/review_pathway_rule_trace/us-candidates-request.json +77 -0
  549. package/vendor/ars/scripts/fixtures/review_target_context/exact-declaration.json +24 -0
  550. package/vendor/ars/scripts/fixtures/review_target_context/field-general-declaration.json +24 -0
  551. package/vendor/ars/scripts/fixtures/review_target_context/msr-2027-technical-full-declaration.json +24 -0
  552. package/vendor/ars/scripts/fixtures/review_target_context/synthetic-registry.json +92 -0
  553. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/README.md +21 -0
  554. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_attested_only.json +55 -0
  555. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_machine_supported.json +49 -0
  556. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_not_isolated.json +56 -0
  557. package/vendor/ars/scripts/fixtures/revision_claim_drift_v2/subject_context_unknown.json +49 -0
  558. package/vendor/ars/scripts/fixtures/submission_package/clean/paper.md +16 -0
  559. package/vendor/ars/scripts/fixtures/submission_package/clean/references.bib +13 -0
  560. package/vendor/ars/scripts/fixtures/submission_package/fallback_authoryear/paper.md +11 -0
  561. package/vendor/ars/scripts/fixtures/submission_package/fallback_authoryear/references.bib +13 -0
  562. package/vendor/ars/scripts/fixtures/submission_package/fallback_latex/paper.tex +9 -0
  563. package/vendor/ars/scripts/fixtures/submission_package/fallback_latex/references.bib +13 -0
  564. package/vendor/ars/scripts/fixtures/submission_package/marker_no_join/paper.md +7 -0
  565. package/vendor/ars/scripts/fixtures/submission_package/orphan_intext/paper.md +8 -0
  566. package/vendor/ars/scripts/fixtures/submission_package/orphan_intext/references.bib +6 -0
  567. package/vendor/ars/scripts/fixtures/submission_package/passports/corpus_only.yaml +9 -0
  568. package/vendor/ars/scripts/fixtures/submission_package/passports/summary_join.yaml +16 -0
  569. package/vendor/ars/scripts/fixtures/submission_package/profiles/full.yaml +15 -0
  570. package/vendor/ars/scripts/fixtures/submission_package/profiles/tight.yaml +15 -0
  571. package/vendor/ars/scripts/fixtures/submission_package/summary_join/paper.md +7 -0
  572. package/vendor/ars/scripts/fixtures/submission_package/uncited_reference/paper.md +9 -0
  573. package/vendor/ars/scripts/fixtures/submission_package/uncited_reference/references.bib +13 -0
  574. package/vendor/ars/scripts/fixtures/submission_package/venue_clean/paper.md +26 -0
  575. package/vendor/ars/scripts/fixtures/submission_package/venue_clean/references.bib +13 -0
  576. package/vendor/ars/scripts/fixtures/submission_package/venue_violations/paper.md +20 -0
  577. package/vendor/ars/scripts/fixtures/submission_package/venue_violations/references.bib +13 -0
  578. package/vendor/ars/scripts/fixtures/submission_packet_manifest/base_inventory.json +101 -0
  579. package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/consent-materials.txt +3 -0
  580. package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/training-certificate.txt +2 -0
  581. package/vendor/ars/scripts/fixtures/submission_packet_manifest/packet/tw-consent-materials.txt +3 -0
  582. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/corpus_input.yaml +30 -0
  583. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/own_draft.md +36 -0
  584. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/own_draft.tex +21 -0
  585. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/seed_expectations.json +218 -0
  586. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/snapshot.json +164 -0
  587. package/vendor/ars/scripts/fixtures/tortured_phrase_screening/snapshot_manifest.json +29 -0
  588. package/vendor/ars/scripts/fixtures/transport_bodies/README.md +55 -0
  589. package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/empty_feed.xml +10 -0
  590. package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/error_5xx.html +7 -0
  591. package/vendor/ars/scripts/fixtures/transport_bodies/arxiv/id_hit.xml +26 -0
  592. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/README.md +69 -0
  593. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/cnki_landing_page.html +13 -0
  594. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/error_5xx.html +2 -0
  595. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_ambiguous.json +16 -0
  596. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_hit.json +16 -0
  597. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coordinate_zero.json +14 -0
  598. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coverage_hit.json +15 -0
  599. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esearch_coverage_zero.json +13 -0
  600. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_hit.json +43 -0
  601. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_issn_mismatch.json +31 -0
  602. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_no_doi.json +31 -0
  603. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_unknown_ra.json +34 -0
  604. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/esummary_year_mismatch.json +31 -0
  605. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_absent.json +4 -0
  606. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_exists.json +16 -0
  607. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/handle_internal_error.json +4 -0
  608. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/istic_csl_hit.json +25 -0
  609. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/istic_csl_other_title.json +18 -0
  610. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_cnki.json +6 -0
  611. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_crossref.json +6 -0
  612. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_istic.json +6 -0
  613. package/vendor/ars/scripts/fixtures/transport_bodies/chinese_literature/ra_unknown_prefix.json +6 -0
  614. package/vendor/ars/scripts/fixtures/transport_bodies/crossref/doi_hit.json +24 -0
  615. package/vendor/ars/scripts/fixtures/transport_bodies/crossref/error_5xx.html +7 -0
  616. package/vendor/ars/scripts/fixtures/transport_bodies/crossref/title_search_miss.json +15 -0
  617. package/vendor/ars/scripts/fixtures/transport_bodies/openalex/doi_hit.json +29 -0
  618. package/vendor/ars/scripts/fixtures/transport_bodies/openalex/error_5xx.json +4 -0
  619. package/vendor/ars/scripts/fixtures/transport_bodies/openalex/title_search_miss.json +11 -0
  620. package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/doi_hit.json +14 -0
  621. package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/error_5xx.json +3 -0
  622. package/vendor/ars/scripts/fixtures/transport_bodies/semantic_scholar/title_search_miss.json +5 -0
  623. package/vendor/ars/scripts/human_read_attestation_resolver.py +467 -0
  624. package/vendor/ars/scripts/ideation_diversity_assignment_gate.py +430 -0
  625. package/vendor/ars/scripts/inquiry_branch_ledger.py +2540 -0
  626. package/vendor/ars/scripts/legacy/ars_apply_revision_patch_v1_0.py +715 -0
  627. package/vendor/ars/scripts/legacy/check_re_review_synthesis_v1_0.py +2215 -0
  628. package/vendor/ars/scripts/migrate_literature_corpus_to_v3_10.py +223 -0
  629. package/vendor/ars/scripts/migrate_literature_corpus_to_v3_7_3.py +277 -0
  630. package/vendor/ars/scripts/migrate_literature_corpus_to_v3_9_0.py +304 -0
  631. package/vendor/ars/scripts/model_tiering_manifest.json +46 -0
  632. package/vendor/ars/scripts/openalex_client.py +232 -0
  633. package/vendor/ars/scripts/parse_audit_verdict.py +802 -0
  634. package/vendor/ars/scripts/pdf_content_classifier_worker.py +176 -0
  635. package/vendor/ars/scripts/pdf_read_preflight.py +1455 -0
  636. package/vendor/ars/scripts/policy_anchor_disclosure_referee.py +354 -0
  637. package/vendor/ars/scripts/recompute_receipts.py +1414 -0
  638. package/vendor/ars/scripts/render_claim_standing_view.py +374 -0
  639. package/vendor/ars/scripts/render_eval_comment.py +130 -0
  640. package/vendor/ars/scripts/render_harness_retirement_issue.py +167 -0
  641. package/vendor/ars/scripts/repro_lock_validation.py +90 -0
  642. package/vendor/ars/scripts/research_workflow_profile.py +1079 -0
  643. package/vendor/ars/scripts/resolve_human_subjects_authority.py +1158 -0
  644. package/vendor/ars/scripts/resolve_review_target_context.py +683 -0
  645. package/vendor/ars/scripts/resume_e4_record.py +509 -0
  646. package/vendor/ars/scripts/retraction_status.py +484 -0
  647. package/vendor/ars/scripts/review_criteria_binding.py +889 -0
  648. package/vendor/ars/scripts/review_panel_provenance.py +744 -0
  649. package/vendor/ars/scripts/revision_roadmap.py +1967 -0
  650. package/vendor/ars/scripts/run_ci_pytest_manifest.py +116 -0
  651. package/vendor/ars/scripts/run_codex_audit.sh +1191 -0
  652. package/vendor/ars/scripts/run_evals.py +513 -0
  653. package/vendor/ars/scripts/run_ideation_diversity_no_call.py +3505 -0
  654. package/vendor/ars/scripts/run_indirect_prompt_injection_no_call.py +3330 -0
  655. package/vendor/ars/scripts/run_indirect_prompt_injection_probe.py +399 -0
  656. package/vendor/ars/scripts/run_review_criteria_constructive_value.py +1895 -0
  657. package/vendor/ars/scripts/run_role_topology_utility_dry_run.py +606 -0
  658. package/vendor/ars/scripts/score_review_criteria_constructive_value.py +617 -0
  659. package/vendor/ars/scripts/semantic_scholar_client.py +291 -0
  660. package/vendor/ars/scripts/slr_lineage.py +59 -0
  661. package/vendor/ars/scripts/sync_adapter_docs.py +118 -0
  662. package/vendor/ars/scripts/temporal_integrity_audit.py +840 -0
  663. package/vendor/ars/scripts/test_431_exact_or_bust.py +253 -0
  664. package/vendor/ars/scripts/test__eval_threshold_gate.py +125 -0
  665. package/vendor/ars/scripts/test__markdown_lint_util.py +129 -0
  666. package/vendor/ars/scripts/test__next_verified_at_ms.py +256 -0
  667. package/vendor/ars/scripts/test_adjacent_framing_probe_lint.py +171 -0
  668. package/vendor/ars/scripts/test_adjudication_activity.py +1515 -0
  669. package/vendor/ars/scripts/test_ars_anchorize_draft.py +178 -0
  670. package/vendor/ars/scripts/test_ars_apply_revision_patch.py +1335 -0
  671. package/vendor/ars/scripts/test_ars_cache_invalidate.py +51 -0
  672. package/vendor/ars/scripts/test_ars_mark_read.py +910 -0
  673. package/vendor/ars/scripts/test_ars_update_check.py +816 -0
  674. package/vendor/ars/scripts/test_ars_write_scope_guard.py +790 -0
  675. package/vendor/ars/scripts/test_arxiv_client.py +374 -0
  676. package/vendor/ars/scripts/test_audit_schemas.py +560 -0
  677. package/vendor/ars/scripts/test_audit_snapshot_render_section_0.py +105 -0
  678. package/vendor/ars/scripts/test_block_parser.py +259 -0
  679. package/vendor/ars/scripts/test_bootstrap_timeline_yaml.py +148 -0
  680. package/vendor/ars/scripts/test_build_claim_standing_candidate_ledger.py +1203 -0
  681. package/vendor/ars/scripts/test_build_claim_standing_query_plan.py +791 -0
  682. package/vendor/ars/scripts/test_build_submission_packet_manifest.py +2644 -0
  683. package/vendor/ars/scripts/test_check_215_field_norm.py +238 -0
  684. package/vendor/ars/scripts/test_check_216_surface_form.py +341 -0
  685. package/vendor/ars/scripts/test_check_268_nested_commitment_ledger.py +194 -0
  686. package/vendor/ars/scripts/test_check_390_revision_patch_discipline.py +279 -0
  687. package/vendor/ars/scripts/test_check_392_citation_verification_intake.py +144 -0
  688. package/vendor/ars/scripts/test_check_394_submission_policy.py +200 -0
  689. package/vendor/ars/scripts/test_check_439_format_profile.py +252 -0
  690. package/vendor/ars/scripts/test_check_619_disclosure_closeout.py +158 -0
  691. package/vendor/ars/scripts/test_check_630_codex_subscription_transport.py +245 -0
  692. package/vendor/ars/scripts/test_check_669_review_pathway_rule_trace.py +304 -0
  693. package/vendor/ars/scripts/test_check_670_revision_roadmap_integration.py +260 -0
  694. package/vendor/ars/scripts/test_check_673_adjudication_activity.py +367 -0
  695. package/vendor/ars/scripts/test_check_684_review_criteria_binding.py +274 -0
  696. package/vendor/ars/scripts/test_check_agents_mirror_sync.py +137 -0
  697. package/vendor/ars/scripts/test_check_audit_artifact_consistency.py +2133 -0
  698. package/vendor/ars/scripts/test_check_benchmark_report.py +117 -0
  699. package/vendor/ars/scripts/test_check_bibliographic_integrity_signals.py +609 -0
  700. package/vendor/ars/scripts/test_check_calibration_tiers.py +376 -0
  701. package/vendor/ars/scripts/test_check_changelog_covers_merges.py +509 -0
  702. package/vendor/ars/scripts/test_check_ci_pytest_manifest.py +425 -0
  703. package/vendor/ars/scripts/test_check_claim_standing_candidate_ledger_integration.py +256 -0
  704. package/vendor/ars/scripts/test_check_claim_standing_freshness.py +277 -0
  705. package/vendor/ars/scripts/test_check_collaboration_depth_rubric.py +239 -0
  706. package/vendor/ars/scripts/test_check_command_frontmatter_name.py +178 -0
  707. package/vendor/ars/scripts/test_check_committee_correspondence.py +298 -0
  708. package/vendor/ars/scripts/test_check_compliance_report.py +381 -0
  709. package/vendor/ars/scripts/test_check_content_coverage_advisory_integration.py +308 -0
  710. package/vendor/ars/scripts/test_check_control_availability.py +361 -0
  711. package/vendor/ars/scripts/test_check_cross_document_consistency_advisory_integration.py +698 -0
  712. package/vendor/ars/scripts/test_check_cross_model_handoff_contract.py +301 -0
  713. package/vendor/ars/scripts/test_check_cross_model_verification_sync.py +203 -0
  714. package/vendor/ars/scripts/test_check_data_access_level.py +227 -0
  715. package/vendor/ars/scripts/test_check_data_flows.py +382 -0
  716. package/vendor/ars/scripts/test_check_decision_contract.py +387 -0
  717. package/vendor/ars/scripts/test_check_degradation_registry.py +270 -0
  718. package/vendor/ars/scripts/test_check_distribution_surface_claims.py +225 -0
  719. package/vendor/ars/scripts/test_check_domain_evidence_profile.py +439 -0
  720. package/vendor/ars/scripts/test_check_e4_promotion.py +163 -0
  721. package/vendor/ars/scripts/test_check_evals_gold_set.py +312 -0
  722. package/vendor/ars/scripts/test_check_evidence_row_integration.py +187 -0
  723. package/vendor/ars/scripts/test_check_field_norm_severity.py +173 -0
  724. package/vendor/ars/scripts/test_check_firm_rules_sync.py +342 -0
  725. package/vendor/ars/scripts/test_check_heldout_measurement_report.py +1508 -0
  726. package/vendor/ars/scripts/test_check_human_subjects_output_contract.py +129 -0
  727. package/vendor/ars/scripts/test_check_human_subjects_reference_migration.py +756 -0
  728. package/vendor/ars/scripts/test_check_instruction_data_boundary.py +204 -0
  729. package/vendor/ars/scripts/test_check_judge_prompt_version.py +90 -0
  730. package/vendor/ars/scripts/test_check_model_tiering.py +236 -0
  731. package/vendor/ars/scripts/test_check_panel_synthesis.py +1658 -0
  732. package/vendor/ars/scripts/test_check_passport_reset_contract.py +249 -0
  733. package/vendor/ars/scripts/test_check_pattern_eval_manifest.py +381 -0
  734. package/vendor/ars/scripts/test_check_persuasion_invariance_fixtures.py +591 -0
  735. package/vendor/ars/scripts/test_check_phase_conformance.py +4366 -0
  736. package/vendor/ars/scripts/test_check_pipeline_boundary_semantics.py +931 -0
  737. package/vendor/ars/scripts/test_check_pipeline_integrity.py +243 -0
  738. package/vendor/ars/scripts/test_check_policy_anchor_protocol.py +236 -0
  739. package/vendor/ars/scripts/test_check_policy_anchor_table.py +295 -0
  740. package/vendor/ars/scripts/test_check_prisma_trAIce_freshness.py +68 -0
  741. package/vendor/ars/scripts/test_check_promotion_bakeoff_preregistration.py +799 -0
  742. package/vendor/ars/scripts/test_check_ranking_lift.py +377 -0
  743. package/vendor/ars/scripts/test_check_re_review_synthesis.py +3398 -0
  744. package/vendor/ars/scripts/test_check_receipt_enum_sync.py +176 -0
  745. package/vendor/ars/scripts/test_check_repro_lock.py +107 -0
  746. package/vendor/ars/scripts/test_check_reviewer_data_fences.py +334 -0
  747. package/vendor/ars/scripts/test_check_reviewer_finding_contract.py +1107 -0
  748. package/vendor/ars/scripts/test_check_reviewer_role_label.py +491 -0
  749. package/vendor/ars/scripts/test_check_reviewer_scoring_honesty.py +222 -0
  750. package/vendor/ars/scripts/test_check_reviewer_sprint_prompt_sync.py +373 -0
  751. package/vendor/ars/scripts/test_check_revision_claim_drift_suite_v2.py +1925 -0
  752. package/vendor/ars/scripts/test_check_revision_token_conservation.py +398 -0
  753. package/vendor/ars/scripts/test_check_risk_register.py +336 -0
  754. package/vendor/ars/scripts/test_check_role_scoped_contract.py +1104 -0
  755. package/vendor/ars/scripts/test_check_rq_framing_patterns.py +110 -0
  756. package/vendor/ars/scripts/test_check_rubric_weight_consistency.py +32 -0
  757. package/vendor/ars/scripts/test_check_seeded_defect_fixtures.py +392 -0
  758. package/vendor/ars/scripts/test_check_setup_cross_model_parity.py +121 -0
  759. package/vendor/ars/scripts/test_check_spec_consistency.py +1081 -0
  760. package/vendor/ars/scripts/test_check_sprint_contract.py +458 -0
  761. package/vendor/ars/scripts/test_check_stage_capability_matrix.py +698 -0
  762. package/vendor/ars/scripts/test_check_submission_packet_manifest_integration.py +177 -0
  763. package/vendor/ars/scripts/test_check_surface_form_parity.py +417 -0
  764. package/vendor/ars/scripts/test_check_task_type.py +116 -0
  765. package/vendor/ars/scripts/test_check_tools_allowlist.py +835 -0
  766. package/vendor/ars/scripts/test_check_tortured_phrase_screening_integration.py +1306 -0
  767. package/vendor/ars/scripts/test_check_v3_10_134_write_scope.py +251 -0
  768. package/vendor/ars/scripts/test_check_v3_10_policy.py +547 -0
  769. package/vendor/ars/scripts/test_check_v3_6_7_pattern_protection.py +960 -0
  770. package/vendor/ars/scripts/test_check_v3_6_8_audit_scope_block.py +1000 -0
  771. package/vendor/ars/scripts/test_check_v3_6_8_cite_provenance_pipeline.py +454 -0
  772. package/vendor/ars/scripts/test_check_v3_6_8_frontmatter_trust_schema.py +581 -0
  773. package/vendor/ars/scripts/test_check_v3_6_8_mark_read_commands.py +120 -0
  774. package/vendor/ars/scripts/test_check_v3_6_8_pattern_protection.py +1138 -0
  775. package/vendor/ars/scripts/test_check_v3_7_3_three_layer_citation.py +566 -0
  776. package/vendor/ars/scripts/test_check_v3_8_annotation_literal_sync.py +262 -0
  777. package/vendor/ars/scripts/test_check_v3_9_0_triangulation.py +321 -0
  778. package/vendor/ars/scripts/test_check_v3_9_2_phase_boundary.py +190 -0
  779. package/vendor/ars/scripts/test_check_v3_9_4_temporal_verification.py +471 -0
  780. package/vendor/ars/scripts/test_check_version_consistency.py +1462 -0
  781. package/vendor/ars/scripts/test_check_workflow_classification.py +225 -0
  782. package/vendor/ars/scripts/test_chinese_literature_client.py +1889 -0
  783. package/vendor/ars/scripts/test_citation_existence_policy.py +480 -0
  784. package/vendor/ars/scripts/test_citation_verification_summary.py +342 -0
  785. package/vendor/ars/scripts/test_claim_audit_calibration.py +882 -0
  786. package/vendor/ars/scripts/test_claim_audit_finalizer.py +979 -0
  787. package/vendor/ars/scripts/test_claim_audit_pipeline.py +2398 -0
  788. package/vendor/ars/scripts/test_claim_audit_schema.py +1778 -0
  789. package/vendor/ars/scripts/test_claim_intent_manifest.py +666 -0
  790. package/vendor/ars/scripts/test_claim_registry_coverage.py +246 -0
  791. package/vendor/ars/scripts/test_claim_standing_discovery.py +579 -0
  792. package/vendor/ars/scripts/test_claim_standing_pipeline_wiring.py +290 -0
  793. package/vendor/ars/scripts/test_claim_standing_stance_assets.py +399 -0
  794. package/vendor/ars/scripts/test_claim_standing_stance_contracts.py +383 -0
  795. package/vendor/ars/scripts/test_claim_standing_stance_runner.py +349 -0
  796. package/vendor/ars/scripts/test_claim_standing_transmissions.py +605 -0
  797. package/vendor/ars/scripts/test_claim_strength_drift_disposition.py +517 -0
  798. package/vendor/ars/scripts/test_claim_verification_coverage_contract.py +104 -0
  799. package/vendor/ars/scripts/test_contamination_signals.py +1086 -0
  800. package/vendor/ars/scripts/test_content_coverage_advisory.py +1767 -0
  801. package/vendor/ars/scripts/test_cross_document_consistency_advisory.py +1270 -0
  802. package/vendor/ars/scripts/test_cross_model_codex_transport.py +1152 -0
  803. package/vendor/ars/scripts/test_cross_model_handoff.py +571 -0
  804. package/vendor/ars/scripts/test_cross_model_verification_guards.py +792 -0
  805. package/vendor/ars/scripts/test_crossref_client.py +393 -0
  806. package/vendor/ars/scripts/test_dispatch_e4_panel.py +4235 -0
  807. package/vendor/ars/scripts/test_e2e_claim_audit.py +540 -0
  808. package/vendor/ars/scripts/test_eval_harness_workflow.py +140 -0
  809. package/vendor/ars/scripts/test_evals_citation_extraction.py +150 -0
  810. package/vendor/ars/scripts/test_evals_lift_report_schema.py +108 -0
  811. package/vendor/ars/scripts/test_evidence_rows.py +2593 -0
  812. package/vendor/ars/scripts/test_experiment_provenance.py +915 -0
  813. package/vendor/ars/scripts/test_human_read_attestation_resolver.py +477 -0
  814. package/vendor/ars/scripts/test_ideation_diversity_assignment_gate.py +615 -0
  815. package/vendor/ars/scripts/test_indirect_prompt_injection_behavior_probe.py +250 -0
  816. package/vendor/ars/scripts/test_inquiry_branch_ledger.py +2296 -0
  817. package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_10.py +248 -0
  818. package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_7_3.py +545 -0
  819. package/vendor/ars/scripts/test_migrate_literature_corpus_to_v3_9_0.py +497 -0
  820. package/vendor/ars/scripts/test_normalize_compat_verdict.py +149 -0
  821. package/vendor/ars/scripts/test_openalex_client.py +490 -0
  822. package/vendor/ars/scripts/test_passport_yaml.py +104 -0
  823. package/vendor/ars/scripts/test_pattern_eval_runtime.py +1295 -0
  824. package/vendor/ars/scripts/test_pdf_read_preflight.py +1943 -0
  825. package/vendor/ars/scripts/test_policy_anchor_disclosure.py +666 -0
  826. package/vendor/ars/scripts/test_reading_probe_lint.py +218 -0
  827. package/vendor/ars/scripts/test_recompute_receipts.py +778 -0
  828. package/vendor/ars/scripts/test_render_claim_standing_view.py +192 -0
  829. package/vendor/ars/scripts/test_render_eval_comment.py +162 -0
  830. package/vendor/ars/scripts/test_render_harness_retirement_issue.py +110 -0
  831. package/vendor/ars/scripts/test_repro_lock_validation_drift.py +100 -0
  832. package/vendor/ars/scripts/test_research_workflow_profile.py +734 -0
  833. package/vendor/ars/scripts/test_resolve_human_subjects_authority.py +1219 -0
  834. package/vendor/ars/scripts/test_resolve_review_target_context.py +703 -0
  835. package/vendor/ars/scripts/test_resume_e4_record.py +315 -0
  836. package/vendor/ars/scripts/test_retraction_status.py +456 -0
  837. package/vendor/ars/scripts/test_review_criteria_binding.py +629 -0
  838. package/vendor/ars/scripts/test_review_panel_provenance.py +565 -0
  839. package/vendor/ars/scripts/test_review_pathway_rule_trace.py +821 -0
  840. package/vendor/ars/scripts/test_revision_roadmap.py +1255 -0
  841. package/vendor/ars/scripts/test_run_ci_pytest_manifest.py +186 -0
  842. package/vendor/ars/scripts/test_run_codex_audit_e2e.py +369 -0
  843. package/vendor/ars/scripts/test_run_evals.py +430 -0
  844. package/vendor/ars/scripts/test_run_guard_launcher.py +500 -0
  845. package/vendor/ars/scripts/test_run_ideation_diversity_no_call.py +1833 -0
  846. package/vendor/ars/scripts/test_run_indirect_prompt_injection_no_call.py +1889 -0
  847. package/vendor/ars/scripts/test_run_review_criteria_constructive_value.py +586 -0
  848. package/vendor/ars/scripts/test_run_role_topology_utility_dry_run.py +428 -0
  849. package/vendor/ars/scripts/test_score_review_criteria_constructive_value.py +340 -0
  850. package/vendor/ars/scripts/test_semantic_scholar_client.py +554 -0
  851. package/vendor/ars/scripts/test_slr_lineage_emission.py +230 -0
  852. package/vendor/ars/scripts/test_socratic_rq_non_generation_contract.py +173 -0
  853. package/vendor/ars/scripts/test_temporal_integrity_audit.py +438 -0
  854. package/vendor/ars/scripts/test_text_similarity.py +95 -0
  855. package/vendor/ars/scripts/test_title_fuzzy_false_positive.py +111 -0
  856. package/vendor/ars/scripts/test_tortured_phrase_screening.py +2959 -0
  857. package/vendor/ars/scripts/test_transport_fixture_citation_gate.py +338 -0
  858. package/vendor/ars/scripts/test_uncited_assertion.py +558 -0
  859. package/vendor/ars/scripts/test_v3_6_7_phase_6_6.py +1279 -0
  860. package/vendor/ars/scripts/test_validate_compliance_fixtures.py +36 -0
  861. package/vendor/ars/scripts/test_validate_ideation_diversity_assets.py +230 -0
  862. package/vendor/ars/scripts/test_venue_disclosure_contract.py +755 -0
  863. package/vendor/ars/scripts/test_verification_cache.py +280 -0
  864. package/vendor/ars/scripts/test_verification_gate.py +461 -0
  865. package/vendor/ars/scripts/test_verify_passport_cli.py +123 -0
  866. package/vendor/ars/scripts/test_verify_submission_package.py +1407 -0
  867. package/vendor/ars/scripts/test_version_records_schema.py +211 -0
  868. package/vendor/ars/scripts/tortured_phrase_screening.py +3502 -0
  869. package/vendor/ars/scripts/uncited_assertion_detector.py +254 -0
  870. package/vendor/ars/scripts/v3_6_7_inversion_manifest.json +9 -0
  871. package/vendor/ars/scripts/v3_6_8_inversion_manifest.json +10 -0
  872. package/vendor/ars/scripts/validate_claim_standing_stance_assets.py +249 -0
  873. package/vendor/ars/scripts/validate_compliance_fixtures.py +56 -0
  874. package/vendor/ars/scripts/validate_ideation_diversity_assets.py +303 -0
  875. package/vendor/ars/scripts/venue_disclosure_contract_harness.py +837 -0
  876. package/vendor/ars/scripts/verification_cache.py +276 -0
  877. package/vendor/ars/scripts/verification_gate/__init__.py +345 -0
  878. package/vendor/ars/scripts/verify_passport.py +133 -0
  879. package/vendor/ars/scripts/verify_submission_package.py +1657 -0
  880. package/vendor/ars/shared/agents/compliance_agent.md +136 -0
  881. package/vendor/ars/shared/artifact_reproducibility_pattern.md +173 -0
  882. package/vendor/ars/shared/benchmark_report.schema.json +81 -0
  883. package/vendor/ars/shared/benchmark_report_pattern.md +180 -0
  884. package/vendor/ars/shared/bibliographic_integrity_signals.md +142 -0
  885. package/vendor/ars/shared/collaboration_depth_rubric.md +154 -0
  886. package/vendor/ars/shared/compliance_checkpoint_protocol.md +162 -0
  887. package/vendor/ars/shared/compliance_report.schema.json +187 -0
  888. package/vendor/ars/shared/contracts/README.md +938 -0
  889. package/vendor/ars/shared/contracts/activity/adjudication_activity_input.schema.json +555 -0
  890. package/vendor/ars/shared/contracts/activity/adjudication_activity_store.schema.json +560 -0
  891. package/vendor/ars/shared/contracts/audit/audit_jsonl.schema.json +128 -0
  892. package/vendor/ars/shared/contracts/audit/audit_sidecar.schema.json +169 -0
  893. package/vendor/ars/shared/contracts/audit/audit_verdict.schema.json +133 -0
  894. package/vendor/ars/shared/contracts/audit/cross_document_consistency_advisory.schema.json +480 -0
  895. package/vendor/ars/shared/contracts/audit/cross_document_consistency_advisory_draft.schema.json +564 -0
  896. package/vendor/ars/shared/contracts/audit/cross_document_source_manifest.schema.json +202 -0
  897. package/vendor/ars/shared/contracts/audit/tortured_phrase_advisory.schema.json +1362 -0
  898. package/vendor/ars/shared/contracts/audit/tortured_phrase_snapshot.schema.json +208 -0
  899. package/vendor/ars/shared/contracts/audit/tortured_phrase_snapshot_manifest.schema.json +335 -0
  900. package/vendor/ars/shared/contracts/capability/stage_capability_matrix.json +434 -0
  901. package/vendor/ars/shared/contracts/claim_standing/candidate_ledger.schema.json +705 -0
  902. package/vendor/ars/shared/contracts/claim_standing/query_plan.schema.json +389 -0
  903. package/vendor/ars/shared/contracts/claim_standing/query_plan_v1_1.schema.json +782 -0
  904. package/vendor/ars/shared/contracts/claim_standing/retrieval_input.schema.json +440 -0
  905. package/vendor/ars/shared/contracts/claim_standing/stance_record.schema.json +460 -0
  906. package/vendor/ars/shared/contracts/claim_standing/transmission_ledger.schema.json +282 -0
  907. package/vendor/ars/shared/contracts/cross_model/codex_citation_receipt.schema.json +157 -0
  908. package/vendor/ars/shared/contracts/cross_model/codex_citation_request.schema.json +23 -0
  909. package/vendor/ars/shared/contracts/cross_model/promotion_bakeoff_sealed_commitment.schema.json +55 -0
  910. package/vendor/ars/shared/contracts/cross_model/promotion_bakeoff_sealed_reveal.schema.json +50 -0
  911. package/vendor/ars/shared/contracts/degradation_registry.json +428 -0
  912. package/vendor/ars/shared/contracts/evaluator/full.json +126 -0
  913. package/vendor/ars/shared/contracts/evidence/claim_registry.schema.json +50 -0
  914. package/vendor/ars/shared/contracts/evidence/claim_registry_coverage_report.schema.json +79 -0
  915. package/vendor/ars/shared/contracts/evidence/evidence_row.schema.json +504 -0
  916. package/vendor/ars/shared/contracts/evidence/evidence_row_v1_1.schema.json +364 -0
  917. package/vendor/ars/shared/contracts/evidence/evidence_row_v1_2.schema.json +714 -0
  918. package/vendor/ars/shared/contracts/evidence/evidence_row_v1_3.schema.json +524 -0
  919. package/vendor/ars/shared/contracts/human_subjects/authority_profile_registry.schema.json +497 -0
  920. package/vendor/ars/shared/contracts/human_subjects/committee_correspondence.schema.json +253 -0
  921. package/vendor/ars/shared/contracts/human_subjects/content_coverage_advisory.schema.json +620 -0
  922. package/vendor/ars/shared/contracts/human_subjects/irb_context_record.schema.json +329 -0
  923. package/vendor/ars/shared/contracts/human_subjects/resolved_authority_context.schema.json +347 -0
  924. package/vendor/ars/shared/contracts/human_subjects/review_pathway_rule_trace.schema.json +265 -0
  925. package/vendor/ars/shared/contracts/human_subjects/review_pathway_trace_request.schema.json +127 -0
  926. package/vendor/ars/shared/contracts/human_subjects/submission_packet_inventory.schema.json +240 -0
  927. package/vendor/ars/shared/contracts/human_subjects/submission_packet_manifest.schema.json +999 -0
  928. package/vendor/ars/shared/contracts/passport/audit_artifact_entry.schema.json +266 -0
  929. package/vendor/ars/shared/contracts/passport/bibliographic_integrity_signal.schema.json +1676 -0
  930. package/vendor/ars/shared/contracts/passport/citation_provenance.schema.json +107 -0
  931. package/vendor/ars/shared/contracts/passport/citation_verification_summary.schema.json +164 -0
  932. package/vendor/ars/shared/contracts/passport/claim_audit_result.schema.json +124 -0
  933. package/vendor/ars/shared/contracts/passport/claim_drift.schema.json +58 -0
  934. package/vendor/ars/shared/contracts/passport/claim_intent_manifest.schema.json +107 -0
  935. package/vendor/ars/shared/contracts/passport/constraint_violation.schema.json +65 -0
  936. package/vendor/ars/shared/contracts/passport/experiment_alignment_result.schema.json +69 -0
  937. package/vendor/ars/shared/contracts/passport/experiment_provenance_entry.schema.json +169 -0
  938. package/vendor/ars/shared/contracts/passport/human_read_log.schema.json +86 -0
  939. package/vendor/ars/shared/contracts/passport/inquiry_ledger_ref.schema.json +22 -0
  940. package/vendor/ars/shared/contracts/passport/literature_corpus_entry.schema.json +650 -0
  941. package/vendor/ars/shared/contracts/passport/preregistration_artifact.schema.json +132 -0
  942. package/vendor/ars/shared/contracts/passport/rejection_log.schema.json +89 -0
  943. package/vendor/ars/shared/contracts/passport/reset_ledger_entry.schema.json +158 -0
  944. package/vendor/ars/shared/contracts/passport/temporal_audit_results.schema.json +208 -0
  945. package/vendor/ars/shared/contracts/passport/terminal_policies.schema.json +50 -0
  946. package/vendor/ars/shared/contracts/passport/timeline.schema.json +102 -0
  947. package/vendor/ars/shared/contracts/passport/uncited_assertion.schema.json +56 -0
  948. package/vendor/ars/shared/contracts/passport/uncited_audit_failure.schema.json +72 -0
  949. package/vendor/ars/shared/contracts/passport/user_attested_read_resolution.schema.json +87 -0
  950. package/vendor/ars/shared/contracts/passport/version_records.schema.json +138 -0
  951. package/vendor/ars/shared/contracts/patch/block_manifest.schema.json +45 -0
  952. package/vendor/ars/shared/contracts/patch/legacy/v1_0/revision_patch.schema.json +110 -0
  953. package/vendor/ars/shared/contracts/patch/revision_patch.schema.json +250 -0
  954. package/vendor/ars/shared/contracts/pdf/pdf_content_classifier_diagnostic.schema.json +39 -0
  955. package/vendor/ars/shared/contracts/pdf/pdf_content_classifier_worker.schema.json +88 -0
  956. package/vendor/ars/shared/contracts/pdf/pdf_read_preflight.schema.json +227 -0
  957. package/vendor/ars/shared/contracts/re_review/input_manifest.schema.json +161 -0
  958. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/input_manifest.schema.json +134 -0
  959. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/precommitment.schema.json +193 -0
  960. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/traceability.schema.json +841 -0
  961. package/vendor/ars/shared/contracts/re_review/legacy/v1_0/verdict_record.schema.json +244 -0
  962. package/vendor/ars/shared/contracts/re_review/precommitment.schema.json +193 -0
  963. package/vendor/ars/shared/contracts/re_review/traceability.schema.json +916 -0
  964. package/vendor/ars/shared/contracts/re_review/verdict_record.schema.json +245 -0
  965. package/vendor/ars/shared/contracts/research_workflow/inquiry_branch_ledger.schema.json +449 -0
  966. package/vendor/ars/shared/contracts/research_workflow/research_workflow_profile.schema.json +222 -0
  967. package/vendor/ars/shared/contracts/research_workflow/research_workflow_profile_selection_receipt.schema.json +111 -0
  968. package/vendor/ars/shared/contracts/review_target/constructive_review_findings.schema.json +172 -0
  969. package/vendor/ars/shared/contracts/review_target/criteria_registry.schema.json +97 -0
  970. package/vendor/ars/shared/contracts/review_target/review_criteria_binding_manifest.schema.json +235 -0
  971. package/vendor/ars/shared/contracts/review_target/review_criteria_source_receipt.schema.json +158 -0
  972. package/vendor/ars/shared/contracts/review_target/review_target_context.schema.json +106 -0
  973. package/vendor/ars/shared/contracts/review_target/review_target_declaration.schema.json +118 -0
  974. package/vendor/ars/shared/contracts/reviewer/full.json +114 -0
  975. package/vendor/ars/shared/contracts/reviewer/methodology_focus.json +75 -0
  976. package/vendor/ars/shared/contracts/reviewer/review_panel_provenance.schema.json +263 -0
  977. package/vendor/ars/shared/contracts/reviewer/review_panel_provenance_carrier.schema.json +122 -0
  978. package/vendor/ars/shared/contracts/reviewer/review_panel_provenance_input.schema.json +120 -0
  979. package/vendor/ars/shared/contracts/revision/author_adjudication.schema.json +251 -0
  980. package/vendor/ars/shared/contracts/revision/author_adjudication_input.schema.json +25 -0
  981. package/vendor/ars/shared/contracts/revision/claim_strength_drift_disposition.schema.json +97 -0
  982. package/vendor/ars/shared/contracts/revision/claim_strength_drift_disposition_input.schema.json +80 -0
  983. package/vendor/ars/shared/contracts/revision/claim_strength_drift_findings.schema.json +139 -0
  984. package/vendor/ars/shared/contracts/revision/claim_surface_manifest.schema.json +99 -0
  985. package/vendor/ars/shared/contracts/revision/integrity_correction_authorization.schema.json +140 -0
  986. package/vendor/ars/shared/contracts/revision/integrity_correction_authorization_input.schema.json +28 -0
  987. package/vendor/ars/shared/contracts/revision/integrity_correction_list.schema.json +48 -0
  988. package/vendor/ars/shared/contracts/revision/integrity_pass_receipt.schema.json +16 -0
  989. package/vendor/ars/shared/contracts/revision/revision_evidence_bundle.schema.json +134 -0
  990. package/vendor/ars/shared/contracts/revision/revision_roadmap.schema.json +334 -0
  991. package/vendor/ars/shared/contracts/submission/format_profile.example.yaml +33 -0
  992. package/vendor/ars/shared/contracts/submission/format_profile.schema.json +102 -0
  993. package/vendor/ars/shared/contracts/submission/submission_verification_report.schema.json +233 -0
  994. package/vendor/ars/shared/contracts/submission/venue_profile.schema.json +114 -0
  995. package/vendor/ars/shared/contracts/writer/full.json +87 -0
  996. package/vendor/ars/shared/cross_model_verification.md +714 -0
  997. package/vendor/ars/shared/evals_lift_report.schema.json +141 -0
  998. package/vendor/ars/shared/ground_truth_isolation_pattern.md +275 -0
  999. package/vendor/ars/shared/handoff_schemas.md +1209 -0
  1000. package/vendor/ars/shared/human_subjects_authority_registry.json +1278 -0
  1001. package/vendor/ars/shared/mode_spectrum.md +57 -0
  1002. package/vendor/ars/shared/model_tiering.md +83 -0
  1003. package/vendor/ars/shared/policy_data/nature_policy.md +56 -0
  1004. package/vendor/ars/shared/prisma_trAIce_protocol.md +157 -0
  1005. package/vendor/ars/shared/raise_framework.md +129 -0
  1006. package/vendor/ars/shared/references/authority_content_coverage_advisory_protocol.md +275 -0
  1007. package/vendor/ars/shared/references/claim_standing_candidate_ledger_protocol.md +66 -0
  1008. package/vendor/ars/shared/references/claim_strength_ladder.md +93 -0
  1009. package/vendor/ars/shared/references/cross_document_consistency_advisory_protocol.md +263 -0
  1010. package/vendor/ars/shared/references/evidence_row_protocol.md +260 -0
  1011. package/vendor/ars/shared/references/firm_rules.md +90 -0
  1012. package/vendor/ars/shared/references/human_subjects_authority_protocol.md +274 -0
  1013. package/vendor/ars/shared/references/intent_clarification_protocol.md +168 -0
  1014. package/vendor/ars/shared/references/irb_terminology_glossary.md +229 -0
  1015. package/vendor/ars/shared/references/protected_hedging_phrases.md +118 -0
  1016. package/vendor/ars/shared/references/psychometric_terminology_glossary.md +109 -0
  1017. package/vendor/ars/shared/references/review_criteria_consumer_protocol.md +238 -0
  1018. package/vendor/ars/shared/references/review_pathway_rule_trace_protocol.md +166 -0
  1019. package/vendor/ars/shared/references/submission_packet_manifest_protocol.md +292 -0
  1020. package/vendor/ars/shared/references/word_count_conventions.md +124 -0
  1021. package/vendor/ars/shared/research_workflow_profiles/field_general.json +1 -0
  1022. package/vendor/ars/shared/review_criteria_registry.json +207 -0
  1023. package/vendor/ars/shared/review_criteria_sources/msr-2027-technical-papers.2026-08-24.json +46 -0
  1024. package/vendor/ars/shared/review_criteria_sources/sigsoft-empirical-standards.2026-08-24.json +31 -0
  1025. package/vendor/ars/shared/sprint_contract.schema.json +482 -0
  1026. package/vendor/ars/shared/style_calibration_protocol.md +151 -0
  1027. package/vendor/ars/shared/templates/codex_audit_multifile_template.md +263 -0
  1028. package/vendor/ars/tools/release-discipline/.toolkit-version +1 -0
  1029. package/vendor/ars/tools/release-discipline/README.md +4 -0
  1030. package/vendor/ars/tools/release-discipline/scripts/_release_doc_alignment_schema.py +1011 -0
  1031. package/vendor/ars/tools/release-discipline/scripts/check_command_invariants.py +497 -0
  1032. package/vendor/ars/tools/release-discipline/scripts/check_release_doc_alignment.py +263 -0
  1033. package/vendor/ars/tools/release-discipline/scripts/sync-toolkit.sh +147 -0
  1034. package/vendor/windows/NOTICE.md +12 -0
  1035. package/vendor/windows/arm64/fd.exe +0 -0
  1036. package/vendor/windows/arm64/licenses/fd/LICENSE-APACHE +201 -0
  1037. package/vendor/windows/arm64/licenses/fd/LICENSE-MIT +21 -0
  1038. package/vendor/windows/arm64/licenses/ripgrep/COPYING +3 -0
  1039. package/vendor/windows/arm64/licenses/ripgrep/LICENSE-MIT +21 -0
  1040. package/vendor/windows/arm64/licenses/ripgrep/UNLICENSE +24 -0
  1041. package/vendor/windows/arm64/rg.exe +0 -0
  1042. package/vendor/windows/x64/fd.exe +0 -0
  1043. package/vendor/windows/x64/licenses/fd/LICENSE-APACHE +201 -0
  1044. package/vendor/windows/x64/licenses/fd/LICENSE-MIT +21 -0
  1045. package/vendor/windows/x64/licenses/ripgrep/COPYING +3 -0
  1046. package/vendor/windows/x64/licenses/ripgrep/LICENSE-MIT +21 -0
  1047. package/vendor/windows/x64/licenses/ripgrep/UNLICENSE +24 -0
  1048. package/vendor/windows/x64/rg.exe +0 -0
@@ -0,0 +1,2133 @@
1
+ """Phase 6.3 acceptance tests for ARS v3.6.7 §3.7 invariant lint.
2
+
3
+ Every row in spec §3.7 families A-F gets at least one positive (valid
4
+ fixture passes) and one negative (deliberately-broken fixture fails)
5
+ test. The aggregator (run_checks) is also smoke-tested on a fully-
6
+ valid synthetic proposal + persisted bundle.
7
+
8
+ Per the user's iron law: positive + negative tests for every rule.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import copy
13
+ import json
14
+ import re
15
+ import subprocess
16
+ import sys
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+ import pytest
21
+
22
+ from scripts.check_audit_artifact_consistency import (
23
+ LintContext,
24
+ LintError,
25
+ check_a1,
26
+ check_a2,
27
+ check_a3,
28
+ check_a4,
29
+ check_a5,
30
+ check_a6,
31
+ check_a7,
32
+ check_b1,
33
+ check_b2,
34
+ check_b3,
35
+ check_b4,
36
+ check_b5,
37
+ check_b6,
38
+ check_b7,
39
+ check_b8,
40
+ check_b9,
41
+ check_b10,
42
+ check_c1,
43
+ check_c2,
44
+ check_c3,
45
+ check_c4,
46
+ check_d1,
47
+ check_d2,
48
+ check_d3,
49
+ check_d4,
50
+ check_e1_e2_e6,
51
+ check_e3_e4,
52
+ check_e5,
53
+ check_e7,
54
+ check_f1,
55
+ check_f2,
56
+ check_f3,
57
+ compute_bundle_manifest,
58
+ main,
59
+ run_checks,
60
+ run_example_harness,
61
+ )
62
+
63
+ REPO = Path(__file__).resolve().parent.parent
64
+
65
+
66
+ # ---------------------------------------------------------------------------
67
+ # Synthetic fixtures (kept in-memory; written to tmp_path when files needed)
68
+ # ---------------------------------------------------------------------------
69
+
70
+ VALID_RUN_ID = "2026-04-30T15-22-04Z-d8f3"
71
+ VALID_THREAD_ID = "019de371-4c13-7521-8af7-fccf6bd23279"
72
+
73
+
74
+ def make_valid_verdict_file_minor() -> dict[str, Any]:
75
+ return {
76
+ "run_id": VALID_RUN_ID,
77
+ "verdict_status": "MINOR",
78
+ "round": 2,
79
+ "target_rounds": 3,
80
+ "finding_counts": {"p1": 0, "p2": 0, "p3": 1},
81
+ "findings": [
82
+ {"id": "F-007", "severity": "P3", "dimension": "3.7",
83
+ "file": "chapter_4/synthesis.md", "line": 482,
84
+ "description": "deictic temporal phrase",
85
+ "suggested_fix": "replace with explicit date"},
86
+ ],
87
+ "generated_at": "2026-04-30T15:22:58.471Z",
88
+ "generated_by": "scripts/run_codex_audit.sh",
89
+ "generator_version": "1.0.0",
90
+ }
91
+
92
+
93
+ def make_valid_verdict_file_pass() -> dict[str, Any]:
94
+ return {
95
+ "run_id": VALID_RUN_ID,
96
+ "verdict_status": "PASS",
97
+ "round": 1,
98
+ "target_rounds": 3,
99
+ "finding_counts": {"p1": 0, "p2": 0, "p3": 0},
100
+ "findings": [],
101
+ "generated_at": "2026-04-30T15:22:58.471Z",
102
+ "generated_by": "scripts/run_codex_audit.sh",
103
+ "generator_version": "1.0.0",
104
+ }
105
+
106
+
107
+ def make_valid_verdict_file_audit_failed() -> dict[str, Any]:
108
+ return {
109
+ "run_id": VALID_RUN_ID,
110
+ "verdict_status": "AUDIT_FAILED",
111
+ "round": 1,
112
+ "target_rounds": 3,
113
+ "finding_counts": {"p1": 0, "p2": 0, "p3": 0},
114
+ "findings": [],
115
+ "failure_reason": "codex exit 70: network timeout",
116
+ "generated_at": "2026-04-30T15:22:58.471Z",
117
+ "generated_by": "scripts/run_codex_audit.sh",
118
+ "generator_version": "1.0.0",
119
+ }
120
+
121
+
122
+ def make_valid_persisted_entry_minor() -> dict[str, Any]:
123
+ return {
124
+ "stage": 2,
125
+ "agent": "synthesis_agent",
126
+ "deliverable_path": "chapter_4/synthesis.md",
127
+ "deliverable_sha": "a" * 64,
128
+ "run_id": VALID_RUN_ID,
129
+ "bundle_id": "phase2-chapter4-2026-04-30",
130
+ "bundle_manifest_sha": "9" * 64,
131
+ "artifact_paths": {
132
+ "jsonl": f"{VALID_RUN_ID}.jsonl",
133
+ "sidecar": f"{VALID_RUN_ID}.meta.json",
134
+ "verdict": f"{VALID_RUN_ID}.verdict.yaml",
135
+ },
136
+ "verdict": {
137
+ "status": "MINOR",
138
+ "round": 2,
139
+ "target_rounds": 3,
140
+ "finding_counts": {"p1": 0, "p2": 0, "p3": 1},
141
+ "verified_at": "2026-04-30T15:23:11.847Z",
142
+ "verified_by": "pipeline_orchestrator_agent",
143
+ },
144
+ }
145
+
146
+
147
+ def make_valid_proposal_entry_pass() -> dict[str, Any]:
148
+ return {
149
+ "stage": 2,
150
+ "agent": "synthesis_agent",
151
+ "deliverable_path": "chapter_4/synthesis.md",
152
+ "deliverable_sha": "a" * 64,
153
+ "run_id": VALID_RUN_ID,
154
+ "bundle_id": "phase2-chapter4-2026-04-30",
155
+ "bundle_manifest_sha": "9" * 64,
156
+ "artifact_paths": {
157
+ "jsonl": f"{VALID_RUN_ID}.jsonl",
158
+ "sidecar": f"{VALID_RUN_ID}.meta.json",
159
+ "verdict": f"{VALID_RUN_ID}.verdict.yaml",
160
+ },
161
+ "verdict": {
162
+ "status": "PASS",
163
+ "round": 1,
164
+ "target_rounds": 3,
165
+ "finding_counts": {"p1": 0, "p2": 0, "p3": 0},
166
+ },
167
+ }
168
+
169
+
170
+ def make_valid_sidecar(template_sha: str = "1" * 64) -> dict[str, Any]:
171
+ primary = [{"path": "chapter_4/synthesis.md", "sha": "a" * 64}]
172
+ supporting = [{"path": "chapter_4/bibliography.json", "sha": "b" * 64}]
173
+ _, manifest_sha = compute_bundle_manifest(
174
+ primary, supporting, "shared/templates/codex_audit_multifile_template.md", template_sha
175
+ )
176
+ return {
177
+ "run_id": VALID_RUN_ID,
178
+ "codex_cli_version": "0.128.0",
179
+ "runner": {
180
+ "hostname": "runner.example.local",
181
+ "cwd": "/path/to/academic-research-skills",
182
+ "git_sha": "b4fbffd",
183
+ "git_dirty": False,
184
+ },
185
+ "timing": {
186
+ "started_at": "2026-04-30T15:22:04.123Z",
187
+ "ended_at": "2026-04-30T15:22:58.471Z",
188
+ "duration_seconds": 54.348,
189
+ },
190
+ "process": {
191
+ "exit_code": 0,
192
+ "stdout_path": f"audit_artifacts/{VALID_RUN_ID}.stdout",
193
+ "stderr_path": f"audit_artifacts/{VALID_RUN_ID}.stderr",
194
+ },
195
+ "stream": {"jsonl_thread_id": VALID_THREAD_ID},
196
+ "prompt": {
197
+ "audit_template_path": "shared/templates/codex_audit_multifile_template.md",
198
+ "audit_template_sha": template_sha,
199
+ "bundle": {
200
+ "bundle_id": "phase2-chapter4-2026-04-30",
201
+ "bundle_manifest_sha": manifest_sha,
202
+ "primary_deliverables": primary,
203
+ "supporting_context": supporting,
204
+ },
205
+ },
206
+ }
207
+
208
+
209
+ def make_valid_jsonl_events_no_tool() -> list[dict[str, Any]]:
210
+ return [
211
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
212
+ {"type": "turn.started"},
213
+ {"type": "item.completed",
214
+ "item": {"id": "item_0", "type": "agent_message",
215
+ "text": "## Section 6 — Verdict\n\nRound 1: 0 findings of any severity. Convergence reached.\n"}},
216
+ {"type": "turn.completed",
217
+ "usage": {"input_tokens": 100, "cached_input_tokens": 0,
218
+ "output_tokens": 50, "reasoning_output_tokens": 25}},
219
+ ]
220
+
221
+
222
+ def make_valid_jsonl_events_tool_using() -> list[dict[str, Any]]:
223
+ return [
224
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
225
+ {"type": "turn.started"},
226
+ {"type": "item.started", "item": {"id": "item_1", "type": "command_execution"}},
227
+ {"type": "item.completed", "item": {"id": "item_1", "type": "command_execution"}},
228
+ {"type": "item.completed",
229
+ "item": {"id": "item_2", "type": "agent_message",
230
+ "text": "## Section 6 — Verdict\n\nRound 1: 0 findings of any severity. Convergence reached.\n"}},
231
+ {"type": "turn.completed",
232
+ "usage": {"input_tokens": 100, "cached_input_tokens": 0,
233
+ "output_tokens": 50, "reasoning_output_tokens": 25}},
234
+ ]
235
+
236
+
237
+ # ---------------------------------------------------------------------------
238
+ # Family A
239
+ # ---------------------------------------------------------------------------
240
+
241
+
242
+ class TestA1:
243
+ """A1 — verdict.status agrees with finding_counts and failure_reason."""
244
+
245
+ def test_pass_with_zero_counts_passes(self):
246
+ v = make_valid_verdict_file_pass()
247
+ assert check_a1(None, v) == []
248
+
249
+ def test_pass_with_nonzero_p1_fails(self):
250
+ v = make_valid_verdict_file_pass()
251
+ v["finding_counts"]["p1"] = 1
252
+ findings = check_a1(None, v)
253
+ assert any(f.rule_id == "A1" and "PASS" in f.message for f in findings)
254
+
255
+ def test_minor_with_p3_le_3_passes(self):
256
+ v = make_valid_verdict_file_minor()
257
+ assert check_a1(None, v) == []
258
+
259
+ def test_minor_with_p3_4_fails(self):
260
+ v = make_valid_verdict_file_minor()
261
+ v["finding_counts"]["p3"] = 4
262
+ findings = check_a1(None, v)
263
+ assert any("MINOR" in f.message for f in findings)
264
+
265
+ def test_minor_with_zero_counts_fails(self):
266
+ # Codex round 10 P2: MINOR with p1=p2=p3=0 is malformed because the
267
+ # wrapper classifies zero findings as PASS, not MINOR. A hand-edited
268
+ # verdict file claiming zero-count MINOR would otherwise pass A1
269
+ # and send the orchestrator down the MINOR escalation path with
270
+ # no findings to act on.
271
+ v = make_valid_verdict_file_minor()
272
+ v["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
273
+ findings = check_a1(None, v)
274
+ assert any(f.rule_id == "A1" and "MINOR" in f.message for f in findings), [
275
+ f.render() for f in findings
276
+ ]
277
+
278
+ def test_material_with_p1_gt_0_passes(self):
279
+ v = {**make_valid_verdict_file_pass(),
280
+ "verdict_status": "MATERIAL",
281
+ "finding_counts": {"p1": 1, "p2": 0, "p3": 0}}
282
+ assert check_a1(None, v) == []
283
+
284
+ def test_material_all_zero_fails(self):
285
+ v = {**make_valid_verdict_file_pass(), "verdict_status": "MATERIAL"}
286
+ findings = check_a1(None, v)
287
+ assert any("MATERIAL" in f.message for f in findings)
288
+
289
+ def test_audit_failed_with_failure_reason_passes(self):
290
+ v = make_valid_verdict_file_audit_failed()
291
+ assert check_a1(None, v) == []
292
+
293
+ def test_audit_failed_missing_failure_reason_fails(self):
294
+ v = make_valid_verdict_file_audit_failed()
295
+ del v["failure_reason"]
296
+ findings = check_a1(None, v)
297
+ assert any("AUDIT_FAILED" in f.message for f in findings)
298
+
299
+ def test_pass_with_failure_reason_fails(self):
300
+ v = make_valid_verdict_file_pass()
301
+ v["failure_reason"] = "should not be here"
302
+ findings = check_a1(None, v)
303
+ assert any("PASS forbids failure_reason" in f.message for f in findings)
304
+
305
+
306
+ class TestA2:
307
+ """A2 — failure_reason iff status == AUDIT_FAILED."""
308
+
309
+ def test_audit_failed_with_reason_passes(self):
310
+ v = make_valid_verdict_file_audit_failed()
311
+ assert check_a2(None, v) == []
312
+
313
+ def test_audit_failed_without_reason_fails(self):
314
+ v = make_valid_verdict_file_audit_failed()
315
+ del v["failure_reason"]
316
+ findings = check_a2(None, v)
317
+ assert any(f.rule_id == "A2" for f in findings)
318
+
319
+ def test_minor_with_reason_fails(self):
320
+ v = make_valid_verdict_file_minor()
321
+ v["failure_reason"] = "leftover"
322
+ findings = check_a2(None, v)
323
+ assert any(f.rule_id == "A2" and "forbidden" in f.message for f in findings)
324
+
325
+
326
+ class TestA3:
327
+ """A3 — round <= target_rounds."""
328
+
329
+ def test_round_le_target_passes(self):
330
+ v = make_valid_verdict_file_minor() # round=2 target=3
331
+ assert check_a3(None, v) == []
332
+
333
+ def test_round_gt_target_fails(self):
334
+ v = make_valid_verdict_file_minor()
335
+ v["round"] = 5
336
+ findings = check_a3(None, v)
337
+ assert any(f.rule_id == "A3" for f in findings)
338
+
339
+
340
+ class TestA4:
341
+ """A4 — acknowledgement only on persisted MATERIAL."""
342
+
343
+ def test_persisted_material_with_ack_passes(self):
344
+ e = make_valid_persisted_entry_minor()
345
+ e["verdict"]["status"] = "MATERIAL"
346
+ e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
347
+ e["acknowledgement"] = {
348
+ "finding_ids": ["F-001"],
349
+ "acknowledged_at": e["verdict"]["verified_at"],
350
+ "acknowledged_by": "user",
351
+ }
352
+ assert check_a4(e, "persisted") == []
353
+
354
+ def test_proposal_with_ack_fails(self):
355
+ e = make_valid_proposal_entry_pass()
356
+ e["acknowledgement"] = {"finding_ids": ["F-001"],
357
+ "acknowledged_at": "2026-04-30T15:23:11.847Z",
358
+ "acknowledged_by": "user"}
359
+ findings = check_a4(e, "proposal")
360
+ assert any(f.rule_id == "A4" for f in findings)
361
+
362
+ def test_persisted_pass_with_ack_fails(self):
363
+ e = make_valid_persisted_entry_minor()
364
+ e["verdict"]["status"] = "PASS"
365
+ e["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
366
+ e["acknowledgement"] = {"finding_ids": ["F-001"],
367
+ "acknowledged_at": e["verdict"]["verified_at"],
368
+ "acknowledged_by": "user"}
369
+ findings = check_a4(e, "persisted")
370
+ assert any(f.rule_id == "A4" and "MATERIAL" in f.message for f in findings)
371
+
372
+
373
+ class TestA5:
374
+ """A5 — finding_counts.pN == count(findings[severity==PN])."""
375
+
376
+ def test_counts_agree_passes(self):
377
+ v = make_valid_verdict_file_minor()
378
+ assert check_a5(v) == []
379
+
380
+ def test_count_disagrees_fails(self):
381
+ v = make_valid_verdict_file_minor()
382
+ v["finding_counts"]["p3"] = 5 # but findings only has 1 P3
383
+ findings = check_a5(v)
384
+ assert any(f.rule_id == "A5" and "p3" in f.message for f in findings)
385
+
386
+
387
+ class TestA6:
388
+ """A6 — AUDIT_FAILED requires findings == []."""
389
+
390
+ def test_audit_failed_empty_findings_passes(self):
391
+ v = make_valid_verdict_file_audit_failed()
392
+ assert check_a6(v) == []
393
+
394
+ def test_audit_failed_with_findings_fails(self):
395
+ v = make_valid_verdict_file_audit_failed()
396
+ v["findings"] = [{"id": "F-001", "severity": "P3", "dimension": "3.7",
397
+ "file": "x.md", "line": 1, "description": "x", "suggested_fix": "y"}]
398
+ findings = check_a6(v)
399
+ assert any(f.rule_id == "A6" for f in findings)
400
+
401
+
402
+ class TestA7:
403
+ """A7 — JSONL stream tool-event pairing."""
404
+
405
+ def test_no_tool_events_passes(self):
406
+ events = make_valid_jsonl_events_no_tool()
407
+ assert check_a7(events) == []
408
+
409
+ def test_tool_using_events_pass(self):
410
+ events = make_valid_jsonl_events_tool_using()
411
+ assert check_a7(events) == []
412
+
413
+ def test_orphan_tool_completion_fails(self):
414
+ events = [
415
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
416
+ {"type": "turn.started"},
417
+ {"type": "item.completed",
418
+ "item": {"id": "item_orphan", "type": "command_execution"}},
419
+ {"type": "turn.completed",
420
+ "usage": {"input_tokens": 1, "cached_input_tokens": 0,
421
+ "output_tokens": 1, "reasoning_output_tokens": 0}},
422
+ ]
423
+ findings = check_a7(events)
424
+ assert any(f.rule_id == "A7" and "orphan" in f.message for f in findings)
425
+
426
+ def test_unmatched_tool_start_fails(self):
427
+ events = [
428
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
429
+ {"type": "item.started", "item": {"id": "item_unmatched", "type": "command_execution"}},
430
+ {"type": "turn.completed",
431
+ "usage": {"input_tokens": 1, "cached_input_tokens": 0,
432
+ "output_tokens": 1, "reasoning_output_tokens": 0}},
433
+ ]
434
+ findings = check_a7(events)
435
+ assert any(f.rule_id == "A7" and "unmatched" in f.message for f in findings)
436
+
437
+ def test_duplicate_started_fails(self):
438
+ events = [
439
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
440
+ {"type": "item.started", "item": {"id": "item_x", "type": "command_execution"}},
441
+ {"type": "item.started", "item": {"id": "item_x", "type": "command_execution"}},
442
+ {"type": "item.completed", "item": {"id": "item_x", "type": "command_execution"}},
443
+ {"type": "turn.completed",
444
+ "usage": {"input_tokens": 1, "cached_input_tokens": 0,
445
+ "output_tokens": 1, "reasoning_output_tokens": 0}},
446
+ ]
447
+ findings = check_a7(events)
448
+ assert any(f.rule_id == "A7" and "duplicate item.started" in f.message for f in findings)
449
+
450
+ def test_completed_before_started_fails(self):
451
+ # An item.completed appearing before its item.started would be classified
452
+ # as orphan because we scan top-down. Either error covers the violation.
453
+ events = [
454
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
455
+ {"type": "item.completed", "item": {"id": "item_z", "type": "command_execution"}},
456
+ {"type": "item.started", "item": {"id": "item_z", "type": "command_execution"}},
457
+ {"type": "turn.completed",
458
+ "usage": {"input_tokens": 1, "cached_input_tokens": 0,
459
+ "output_tokens": 1, "reasoning_output_tokens": 0}},
460
+ ]
461
+ findings = check_a7(events)
462
+ # We expect at least one A7 finding (orphan or ordering)
463
+ assert any(f.rule_id == "A7" for f in findings)
464
+
465
+
466
+ # ---------------------------------------------------------------------------
467
+ # Family B
468
+ # ---------------------------------------------------------------------------
469
+
470
+
471
+ class TestB1:
472
+ """B1 — sidecar.stream.jsonl_thread_id matches JSONL.thread.started.thread_id."""
473
+
474
+ def test_matching_thread_id_passes(self):
475
+ s = make_valid_sidecar()
476
+ events = make_valid_jsonl_events_no_tool()
477
+ v = make_valid_verdict_file_minor()
478
+ assert check_b1(s, events, v) == []
479
+
480
+ def test_mismatched_thread_id_fails(self):
481
+ s = make_valid_sidecar()
482
+ s["stream"]["jsonl_thread_id"] = "11111111-1111-1111-1111-111111111111"
483
+ events = make_valid_jsonl_events_no_tool()
484
+ v = make_valid_verdict_file_minor()
485
+ findings = check_b1(s, events, v)
486
+ assert any(f.rule_id == "B1" for f in findings)
487
+
488
+ def test_audit_failed_suspends_b1(self):
489
+ s = make_valid_sidecar()
490
+ s["stream"]["jsonl_thread_id"] = "" # AUDIT_FAILED permits empty
491
+ v = make_valid_verdict_file_audit_failed()
492
+ # JSONL may be malformed/missing on AUDIT_FAILED; but B1 is suspended
493
+ assert check_b1(s, [], v) == []
494
+
495
+
496
+ class TestB2:
497
+ """B2 — entry.deliverable_sha == sidecar.primary_sha == disk SHA."""
498
+
499
+ def test_matching_shas_passes(self, tmp_path: Path):
500
+ e = make_valid_persisted_entry_minor()
501
+ s = make_valid_sidecar()
502
+ # repo_root is tmp_path with no deliverable file — disk verify skipped
503
+ assert check_b2(e, s, tmp_path) == []
504
+
505
+ def test_entry_sidecar_sha_disagree_fails(self):
506
+ e = make_valid_persisted_entry_minor()
507
+ s = make_valid_sidecar()
508
+ s["prompt"]["bundle"]["primary_deliverables"][0]["sha"] = "f" * 64
509
+ findings = check_b2(e, s, REPO)
510
+ assert any(f.rule_id == "B2" for f in findings)
511
+
512
+ def test_disk_sha_mismatch_fails(self, tmp_path: Path):
513
+ e = make_valid_persisted_entry_minor()
514
+ e["deliverable_path"] = "deliverable.md"
515
+ s = make_valid_sidecar()
516
+ s["prompt"]["bundle"]["primary_deliverables"] = [
517
+ {"path": "deliverable.md", "sha": "a" * 64}
518
+ ]
519
+ # Write a real file but with content whose SHA is NOT all-a's
520
+ (tmp_path / "deliverable.md").write_text("totally different content\n")
521
+ findings = check_b2(e, s, tmp_path)
522
+ assert any(f.rule_id == "B2" and "current_file" in f.message for f in findings)
523
+
524
+
525
+ class TestB3:
526
+ """B3 — bundle_manifest_sha agrees with recomputed manifest."""
527
+
528
+ def test_matching_manifest_passes(self):
529
+ s = make_valid_sidecar()
530
+ # repo_root REPO doesn't have these files; but declared SHAs will recompute
531
+ # consistently because the helper used the same SHAs.
532
+ assert check_b3(s, REPO / "nonexistent_subtree") == []
533
+
534
+ def test_mismatched_manifest_fails(self):
535
+ s = make_valid_sidecar()
536
+ s["prompt"]["bundle"]["bundle_manifest_sha"] = "0" * 64
537
+ findings = check_b3(s, REPO / "nonexistent_subtree")
538
+ assert any(f.rule_id == "B3" for f in findings)
539
+
540
+ def test_missing_bundle_file_fails(self, tmp_path: Path):
541
+ # Codex round 2 P2: when a bundle file is missing on disk, B3 must
542
+ # NOT fall back to the sidecar-declared SHA — that would let a
543
+ # deleted/moved file silently pass. With a real .git/ marker, B3
544
+ # Step 2 is required to flag the missing file as stale/unverifiable.
545
+ # Synthesize a tmp repo with a real .git marker but missing bundle
546
+ # files; the sidecar's declared paths are intentionally absent.
547
+ (tmp_path / ".git").mkdir() # marker only; B3 doesn't run git commands
548
+ s = make_valid_sidecar() # references deliverable.md / bibliography.json / verification.md / template
549
+ findings = check_b3(s, tmp_path)
550
+ assert any(
551
+ f.rule_id == "B3" and "missing on disk" in f.message
552
+ for f in findings
553
+ ), [f.render() for f in findings]
554
+
555
+
556
+ class TestB4:
557
+ """B4 — runner.git_sha resolves to a real commit."""
558
+
559
+ def test_real_git_sha_passes(self):
560
+ # Use the actual repo HEAD
561
+ result = subprocess.run(
562
+ ["git", "rev-parse", "HEAD"],
563
+ capture_output=True, text=True, cwd=REPO,
564
+ )
565
+ head = result.stdout.strip()
566
+ s = make_valid_sidecar()
567
+ s["runner"]["git_sha"] = head[:12]
568
+ assert check_b4(s, REPO) == []
569
+
570
+ def test_fake_git_sha_fails(self):
571
+ s = make_valid_sidecar()
572
+ s["runner"]["git_sha"] = "deadbeefdeadbeef"
573
+ findings = check_b4(s, REPO)
574
+ assert any(f.rule_id == "B4" for f in findings)
575
+
576
+ def test_outside_git_repo_skipped(self, tmp_path: Path):
577
+ # tmp_path has no .git/, so B4 is skipped (no false positive on synthetic fixtures)
578
+ s = make_valid_sidecar()
579
+ s["runner"]["git_sha"] = "deadbeef"
580
+ assert check_b4(s, tmp_path) == []
581
+
582
+
583
+ class TestB5:
584
+ """B5 — ended_at - started_at == duration_seconds (±1s)."""
585
+
586
+ def test_consistent_timing_passes(self):
587
+ s = make_valid_sidecar()
588
+ assert check_b5(s) == []
589
+
590
+ def test_inconsistent_timing_fails(self):
591
+ s = make_valid_sidecar()
592
+ s["timing"]["duration_seconds"] = 999.0
593
+ findings = check_b5(s)
594
+ assert any(f.rule_id == "B5" for f in findings)
595
+
596
+
597
+ class TestB6:
598
+ """B6 — exit_code == 0 for non-AUDIT_FAILED."""
599
+
600
+ def test_zero_exit_minor_passes(self):
601
+ s = make_valid_sidecar()
602
+ v = make_valid_verdict_file_minor()
603
+ assert check_b6(s, v) == []
604
+
605
+ def test_nonzero_exit_minor_fails(self):
606
+ s = make_valid_sidecar()
607
+ s["process"]["exit_code"] = 70
608
+ v = make_valid_verdict_file_minor()
609
+ findings = check_b6(s, v)
610
+ assert any(f.rule_id == "B6" for f in findings)
611
+
612
+ def test_nonzero_exit_audit_failed_passes(self):
613
+ s = make_valid_sidecar()
614
+ s["process"]["exit_code"] = 70
615
+ v = make_valid_verdict_file_audit_failed()
616
+ assert check_b6(s, v) == []
617
+
618
+
619
+ class TestB7:
620
+ """B7 — entry.run_id == sidecar.run_id == bare basename of every co-located file."""
621
+
622
+ def test_matching_run_ids_pass(self, tmp_path: Path):
623
+ e = make_valid_persisted_entry_minor()
624
+ s = make_valid_sidecar()
625
+ sidecar_path = tmp_path / f"{VALID_RUN_ID}.meta.json"
626
+ jsonl_path = tmp_path / f"{VALID_RUN_ID}.jsonl"
627
+ verdict_path = tmp_path / f"{VALID_RUN_ID}.verdict.yaml"
628
+ # Don't actually need to write the files for B7
629
+ assert check_b7(e, s, None, sidecar_path, jsonl_path, verdict_path, "persisted") == []
630
+
631
+ def test_entry_sidecar_run_id_mismatch_fails(self, tmp_path: Path):
632
+ e = make_valid_persisted_entry_minor()
633
+ s = make_valid_sidecar()
634
+ s["run_id"] = "2026-04-30T15-22-04Z-aaaa"
635
+ findings = check_b7(e, s, None, tmp_path / f"{VALID_RUN_ID}.meta.json",
636
+ tmp_path / f"{VALID_RUN_ID}.jsonl",
637
+ tmp_path / f"{VALID_RUN_ID}.verdict.yaml", "persisted")
638
+ assert any(f.rule_id == "B7" and "entry.run_id" in f.message for f in findings)
639
+
640
+ def test_basename_mismatch_fails(self, tmp_path: Path):
641
+ e = make_valid_persisted_entry_minor()
642
+ s = make_valid_sidecar()
643
+ # sidecar path basename doesn't match run_id
644
+ wrong_path = tmp_path / "totally-wrong-stem.meta.json"
645
+ findings = check_b7(e, s, None, wrong_path, None, None, "persisted")
646
+ assert any(f.rule_id == "B7" and "basename" in f.message for f in findings)
647
+
648
+
649
+ class TestB8:
650
+ """B8 — verified_at == acknowledged_at on ack entries."""
651
+
652
+ def test_matching_timestamps_pass(self):
653
+ e = make_valid_persisted_entry_minor()
654
+ e["verdict"]["status"] = "MATERIAL"
655
+ e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
656
+ e["acknowledgement"] = {
657
+ "finding_ids": ["F-001"],
658
+ "acknowledged_at": e["verdict"]["verified_at"],
659
+ "acknowledged_by": "user",
660
+ }
661
+ assert check_b8(e, "persisted") == []
662
+
663
+ def test_mismatched_timestamps_fail(self):
664
+ e = make_valid_persisted_entry_minor()
665
+ e["verdict"]["status"] = "MATERIAL"
666
+ e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
667
+ e["acknowledgement"] = {
668
+ "finding_ids": ["F-001"],
669
+ "acknowledged_at": "2026-04-30T16:00:00.000Z",
670
+ "acknowledged_by": "user",
671
+ }
672
+ findings = check_b8(e, "persisted")
673
+ assert any(f.rule_id == "B8" for f in findings)
674
+
675
+
676
+ class TestB9:
677
+ """B9 — entry.bundle_id == sidecar.prompt.bundle.bundle_id (when present)."""
678
+
679
+ def test_matching_bundle_id_passes(self):
680
+ e = make_valid_persisted_entry_minor()
681
+ s = make_valid_sidecar()
682
+ assert check_b9(e, s) == []
683
+
684
+ def test_no_bundle_id_skipped(self):
685
+ e = make_valid_persisted_entry_minor()
686
+ del e["bundle_id"]
687
+ s = make_valid_sidecar()
688
+ assert check_b9(e, s) == []
689
+
690
+ def test_mismatched_bundle_id_fails(self):
691
+ e = make_valid_persisted_entry_minor()
692
+ s = make_valid_sidecar()
693
+ s["prompt"]["bundle"]["bundle_id"] = "different-bundle"
694
+ findings = check_b9(e, s)
695
+ assert any(f.rule_id == "B9" for f in findings)
696
+
697
+
698
+ class TestB10:
699
+ """B10 — ack finding_ids: non-empty + all exist + full coverage."""
700
+
701
+ def test_full_coverage_passes(self):
702
+ e = make_valid_persisted_entry_minor()
703
+ e["verdict"]["status"] = "MATERIAL"
704
+ e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
705
+ e["acknowledgement"] = {
706
+ "finding_ids": ["F-001"],
707
+ "acknowledged_at": e["verdict"]["verified_at"],
708
+ "acknowledged_by": "user",
709
+ }
710
+ v = make_valid_verdict_file_minor()
711
+ v["verdict_status"] = "MATERIAL"
712
+ v["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
713
+ v["findings"] = [{"id": "F-001", "severity": "P1", "dimension": "3.7",
714
+ "file": "x.md", "line": 1, "description": "x",
715
+ "suggested_fix": "y"}]
716
+ assert check_b10(e, v, "persisted") == []
717
+
718
+ def test_unknown_finding_id_fails(self):
719
+ e = make_valid_persisted_entry_minor()
720
+ e["verdict"]["status"] = "MATERIAL"
721
+ e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
722
+ e["acknowledgement"] = {
723
+ "finding_ids": ["F-999"],
724
+ "acknowledged_at": e["verdict"]["verified_at"],
725
+ "acknowledged_by": "user",
726
+ }
727
+ v = make_valid_verdict_file_minor()
728
+ v["verdict_status"] = "MATERIAL"
729
+ v["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
730
+ v["findings"] = [{"id": "F-001", "severity": "P1", "dimension": "3.7",
731
+ "file": "x.md", "line": 1, "description": "x",
732
+ "suggested_fix": "y"}]
733
+ findings = check_b10(e, v, "persisted")
734
+ assert any(f.rule_id == "B10" and "unknown" in f.message for f in findings)
735
+
736
+ def test_partial_coverage_fails(self):
737
+ e = make_valid_persisted_entry_minor()
738
+ e["verdict"]["status"] = "MATERIAL"
739
+ e["verdict"]["finding_counts"] = {"p1": 2, "p2": 0, "p3": 0}
740
+ e["acknowledgement"] = {
741
+ "finding_ids": ["F-001"],
742
+ "acknowledged_at": e["verdict"]["verified_at"],
743
+ "acknowledged_by": "user",
744
+ }
745
+ v = make_valid_verdict_file_minor()
746
+ v["verdict_status"] = "MATERIAL"
747
+ v["finding_counts"] = {"p1": 2, "p2": 0, "p3": 0}
748
+ v["findings"] = [
749
+ {"id": "F-001", "severity": "P1", "dimension": "3.7",
750
+ "file": "x.md", "line": 1, "description": "x", "suggested_fix": "y"},
751
+ {"id": "F-002", "severity": "P1", "dimension": "3.7",
752
+ "file": "x.md", "line": 2, "description": "y", "suggested_fix": "z"},
753
+ ]
754
+ findings = check_b10(e, v, "persisted")
755
+ assert any(f.rule_id == "B10" and "coverage" in f.message for f in findings)
756
+
757
+ def test_empty_finding_ids_fails(self):
758
+ e = make_valid_persisted_entry_minor()
759
+ e["verdict"]["status"] = "MATERIAL"
760
+ e["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
761
+ e["acknowledgement"] = {
762
+ "finding_ids": [],
763
+ "acknowledged_at": e["verdict"]["verified_at"],
764
+ "acknowledged_by": "user",
765
+ }
766
+ v = make_valid_verdict_file_minor()
767
+ findings = check_b10(e, v, "persisted")
768
+ assert any(f.rule_id == "B10" and "non-empty" in f.message for f in findings)
769
+
770
+
771
+ # ---------------------------------------------------------------------------
772
+ # Family C
773
+ # ---------------------------------------------------------------------------
774
+
775
+
776
+ class TestC1:
777
+ """C1 — entry.verdict mirrors verdict file."""
778
+
779
+ def test_matching_mirror_passes(self):
780
+ e = make_valid_persisted_entry_minor()
781
+ v = make_valid_verdict_file_minor()
782
+ assert check_c1(e, v) == []
783
+
784
+ def test_status_drift_fails(self):
785
+ e = make_valid_persisted_entry_minor()
786
+ v = make_valid_verdict_file_minor()
787
+ v["verdict_status"] = "PASS"
788
+ findings = check_c1(e, v)
789
+ assert any(f.rule_id == "C1" and "status" in f.message for f in findings)
790
+
791
+ def test_finding_counts_drift_fails(self):
792
+ e = make_valid_persisted_entry_minor()
793
+ v = make_valid_verdict_file_minor()
794
+ v["finding_counts"] = {"p1": 0, "p2": 0, "p3": 5}
795
+ findings = check_c1(e, v)
796
+ assert any(f.rule_id == "C1" and "finding_counts" in f.message for f in findings)
797
+
798
+
799
+ class TestC2:
800
+ """C2 — entry.bundle_manifest_sha == sidecar.bundle_manifest_sha."""
801
+
802
+ def test_matching_sha_passes(self):
803
+ e = make_valid_persisted_entry_minor()
804
+ s = make_valid_sidecar()
805
+ e["bundle_manifest_sha"] = s["prompt"]["bundle"]["bundle_manifest_sha"]
806
+ assert check_c2(e, s) == []
807
+
808
+ def test_drift_fails(self):
809
+ e = make_valid_persisted_entry_minor()
810
+ s = make_valid_sidecar()
811
+ e["bundle_manifest_sha"] = "0" * 64
812
+ findings = check_c2(e, s)
813
+ assert any(f.rule_id == "C2" for f in findings)
814
+
815
+
816
+ class TestC3:
817
+ """C3 — ack append copies prior entry byte-for-byte except verified_at/by."""
818
+
819
+ def test_byte_for_byte_copy_passes(self):
820
+ prior = make_valid_persisted_entry_minor()
821
+ prior["verdict"]["status"] = "MATERIAL"
822
+ prior["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
823
+ new = copy.deepcopy(prior)
824
+ new["verdict"]["verified_at"] = "2026-04-30T15:24:00.000Z" # bumped
825
+ new["acknowledgement"] = {
826
+ "finding_ids": ["F-001"],
827
+ "acknowledged_at": new["verdict"]["verified_at"],
828
+ "acknowledged_by": "user",
829
+ }
830
+ assert check_c3(new, prior) == []
831
+
832
+ def test_drift_in_copied_field_fails(self):
833
+ prior = make_valid_persisted_entry_minor()
834
+ new = copy.deepcopy(prior)
835
+ new["deliverable_sha"] = "f" * 64 # diverged
836
+ new["verdict"]["verified_at"] = "2026-04-30T15:24:00.000Z"
837
+ findings = check_c3(new, prior)
838
+ assert any(f.rule_id == "C3" and "deliverable_sha" in f.message for f in findings)
839
+
840
+ def test_same_verified_at_fails(self):
841
+ prior = make_valid_persisted_entry_minor()
842
+ new = copy.deepcopy(prior) # same verified_at
843
+ findings = check_c3(new, prior)
844
+ assert any(f.rule_id == "C3" and "freshly set" in f.message for f in findings)
845
+
846
+
847
+ class TestC4:
848
+ """C4 — entry.deliverable_sha == sidecar.primary[].sha (entry-side half of B2)."""
849
+
850
+ def test_matching_passes(self):
851
+ e = make_valid_persisted_entry_minor()
852
+ s = make_valid_sidecar()
853
+ assert check_c4(e, s) == []
854
+
855
+ def test_drift_fails(self):
856
+ e = make_valid_persisted_entry_minor()
857
+ s = make_valid_sidecar()
858
+ s["prompt"]["bundle"]["primary_deliverables"][0]["sha"] = "f" * 64
859
+ findings = check_c4(e, s)
860
+ assert any(f.rule_id == "C4" for f in findings)
861
+
862
+
863
+ # ---------------------------------------------------------------------------
864
+ # Family D
865
+ # ---------------------------------------------------------------------------
866
+
867
+
868
+ class TestD1:
869
+ """D1 — duplicate verified_at within (stage,agent,sha,run_id) group."""
870
+
871
+ def test_unique_verified_at_passes(self):
872
+ e1 = make_valid_persisted_entry_minor()
873
+ e2 = make_valid_persisted_entry_minor()
874
+ e2["verdict"]["verified_at"] = "2026-04-30T15:23:12.847Z"
875
+ e2["run_id"] = "2026-04-30T15-22-04Z-d8f4" # different run
876
+ assert check_d1([e1, e2]) == []
877
+
878
+ def test_duplicate_verified_at_fails(self):
879
+ e1 = make_valid_persisted_entry_minor()
880
+ e2 = copy.deepcopy(e1)
881
+ # same key + same verified_at → duplicate
882
+ findings = check_d1([e1, e2])
883
+ assert any(f.rule_id == "D1" for f in findings)
884
+
885
+
886
+ class TestD2:
887
+ """D2 — proposals sharing started_at."""
888
+
889
+ def test_distinct_started_at_passes(self):
890
+ proposals = [
891
+ {"sidecar": {"timing": {"started_at": "2026-04-30T15:22:04.123Z"}},
892
+ "entry": {"run_id": "2026-04-30T15-22-04Z-aaaa"}},
893
+ {"sidecar": {"timing": {"started_at": "2026-04-30T15:22:05.123Z"}},
894
+ "entry": {"run_id": "2026-04-30T15-22-05Z-bbbb"}},
895
+ ]
896
+ assert check_d2(proposals) == []
897
+
898
+ def test_shared_started_at_warns(self):
899
+ proposals = [
900
+ {"sidecar": {"timing": {"started_at": "2026-04-30T15:22:04.123Z"}},
901
+ "entry": {"run_id": "2026-04-30T15-22-04Z-aaaa"}},
902
+ {"sidecar": {"timing": {"started_at": "2026-04-30T15:22:04.123Z"}},
903
+ "entry": {"run_id": "2026-04-30T15-22-04Z-bbbb"}},
904
+ ]
905
+ findings = check_d2(proposals)
906
+ assert any(f.rule_id == "D2" for f in findings)
907
+
908
+
909
+ class TestD3:
910
+ """D3 — verified_at strictly monotonic."""
911
+
912
+ def test_monotonic_ledger_passes(self):
913
+ e1 = make_valid_persisted_entry_minor()
914
+ e2 = copy.deepcopy(e1)
915
+ e2["verdict"]["verified_at"] = "2026-04-30T15:23:12.847Z"
916
+ e2["run_id"] = "2026-04-30T15-22-04Z-d8f4"
917
+ assert check_d3([e1, e2]) == []
918
+
919
+ def test_non_monotonic_fails(self):
920
+ e1 = make_valid_persisted_entry_minor()
921
+ e2 = copy.deepcopy(e1)
922
+ e2["verdict"]["verified_at"] = "2026-04-30T15:00:00.000Z" # earlier than e1
923
+ e2["run_id"] = "2026-04-30T15-22-04Z-d8f4"
924
+ findings = check_d3([e1, e2])
925
+ assert any(f.rule_id == "D3" for f in findings)
926
+
927
+
928
+ class TestD4:
929
+ """D4 — surface the supersession requirement when proposal round is
930
+ higher than persisted round (spec §3.7 family D + §5.6 A1.5)."""
931
+
932
+ def test_higher_round_fires_supersession(self):
933
+ # The lint should SURFACE the supersession (proposal preempts
934
+ # persisted Path A) so the caller sees it before the orchestrator
935
+ # processes the artifacts.
936
+ findings = check_d4(persisted_round=2, proposal_round=3)
937
+ assert any(f.rule_id == "D4" for f in findings)
938
+ assert any("supersedes" in f.message for f in findings)
939
+
940
+ def test_equal_round_no_supersession(self):
941
+ # Same round — no supersession required; D4 is silent.
942
+ assert check_d4(persisted_round=3, proposal_round=3) == []
943
+
944
+ def test_lower_round_no_supersession(self):
945
+ # Lower-round proposal — D4 silent (orchestrator should reject the
946
+ # proposal at B1a as a stale leftover, but that's not D4's surface).
947
+ assert check_d4(persisted_round=3, proposal_round=1) == []
948
+
949
+
950
+ # ---------------------------------------------------------------------------
951
+ # Family E
952
+ # ---------------------------------------------------------------------------
953
+
954
+
955
+ class TestE1E2E6:
956
+ """E1/E2/E6 — passport-shape post-hoc detection."""
957
+
958
+ def test_orchestrator_emitted_passes(self):
959
+ e = make_valid_persisted_entry_minor()
960
+ assert check_e1_e2_e6([e]) == []
961
+
962
+ def test_missing_verified_by_fails(self):
963
+ e = make_valid_persisted_entry_minor()
964
+ del e["verdict"]["verified_by"]
965
+ findings = check_e1_e2_e6([e])
966
+ assert any(f.rule_id == "E1/E2/E6" for f in findings)
967
+
968
+ def test_wrong_verified_by_fails(self):
969
+ e = make_valid_persisted_entry_minor()
970
+ e["verdict"]["verified_by"] = "totally_different_agent"
971
+ findings = check_e1_e2_e6([e])
972
+ assert any(f.rule_id == "E1/E2/E6" and "verified_by" in f.message for f in findings)
973
+
974
+
975
+ class TestE3E4:
976
+ """E3/E4 — proposals carrying verified_at/verified_by are rejected."""
977
+
978
+ def test_clean_proposal_passes(self):
979
+ e = make_valid_proposal_entry_pass()
980
+ assert check_e3_e4(e, "proposal") == []
981
+
982
+ def test_proposal_with_verified_at_fails(self):
983
+ e = make_valid_proposal_entry_pass()
984
+ e["verdict"]["verified_at"] = "2026-04-30T15:23:11.847Z"
985
+ findings = check_e3_e4(e, "proposal")
986
+ assert any(f.rule_id == "E3/E4" and "verified_at" in f.message for f in findings)
987
+
988
+ def test_proposal_with_verified_by_fails(self):
989
+ e = make_valid_proposal_entry_pass()
990
+ e["verdict"]["verified_by"] = "pipeline_orchestrator_agent"
991
+ findings = check_e3_e4(e, "proposal")
992
+ assert any(f.rule_id == "E3/E4" and "verified_by" in f.message for f in findings)
993
+
994
+
995
+ class TestE5:
996
+ """E5 — persisted mode rejects AUDIT_FAILED."""
997
+
998
+ def test_minor_persisted_passes(self):
999
+ e = make_valid_persisted_entry_minor()
1000
+ assert check_e5(e, "persisted") == []
1001
+
1002
+ def test_audit_failed_persisted_fails(self):
1003
+ e = make_valid_persisted_entry_minor()
1004
+ e["verdict"]["status"] = "AUDIT_FAILED"
1005
+ findings = check_e5(e, "persisted")
1006
+ assert any(f.rule_id == "E5" for f in findings)
1007
+
1008
+
1009
+ class TestE7:
1010
+ """E7 — ack entry's status remains MATERIAL."""
1011
+
1012
+ def test_material_with_ack_passes(self):
1013
+ e = make_valid_persisted_entry_minor()
1014
+ e["verdict"]["status"] = "MATERIAL"
1015
+ e["acknowledgement"] = {
1016
+ "finding_ids": ["F-001"],
1017
+ "acknowledged_at": e["verdict"]["verified_at"],
1018
+ "acknowledged_by": "user",
1019
+ }
1020
+ assert check_e7(e) == []
1021
+
1022
+ def test_synthetic_status_with_ack_fails(self):
1023
+ e = make_valid_persisted_entry_minor()
1024
+ e["verdict"]["status"] = "MATERIAL_ACKNOWLEDGED"
1025
+ e["acknowledgement"] = {
1026
+ "finding_ids": ["F-001"],
1027
+ "acknowledged_at": e["verdict"]["verified_at"],
1028
+ "acknowledged_by": "user",
1029
+ }
1030
+ findings = check_e7(e)
1031
+ assert any(f.rule_id == "E7" for f in findings)
1032
+
1033
+
1034
+ # ---------------------------------------------------------------------------
1035
+ # Family F
1036
+ # ---------------------------------------------------------------------------
1037
+
1038
+
1039
+ class TestF1:
1040
+ """F1 — run_id format."""
1041
+
1042
+ def test_canonical_run_id_passes(self):
1043
+ assert check_f1(VALID_RUN_ID) == []
1044
+
1045
+ def test_wrong_format_fails(self):
1046
+ findings = check_f1("not-a-run-id")
1047
+ assert any(f.rule_id == "F1" for f in findings)
1048
+
1049
+ def test_missing_run_id_fails(self):
1050
+ findings = check_f1(None)
1051
+ assert any(f.rule_id == "F1" for f in findings)
1052
+
1053
+
1054
+ class TestF2:
1055
+ """F2 — bare-run_id basename + extensions."""
1056
+
1057
+ def test_canonical_filenames_pass(self, tmp_path: Path):
1058
+ jsonl = tmp_path / f"{VALID_RUN_ID}.jsonl"
1059
+ sidecar = tmp_path / f"{VALID_RUN_ID}.meta.json"
1060
+ verdict = tmp_path / f"{VALID_RUN_ID}.verdict.yaml"
1061
+ entry = tmp_path / f"{VALID_RUN_ID}.audit_artifact_entry.json"
1062
+ assert check_f2(jsonl, sidecar, verdict, entry, "proposal") == []
1063
+
1064
+ def test_stage_prefix_fails(self, tmp_path: Path):
1065
+ sidecar = tmp_path / f"stage2-synthesis-{VALID_RUN_ID}.meta.json"
1066
+ findings = check_f2(None, sidecar, None, None, "persisted")
1067
+ assert any(f.rule_id == "F2" for f in findings)
1068
+
1069
+ def test_wrong_extension_fails(self, tmp_path: Path):
1070
+ sidecar = tmp_path / f"{VALID_RUN_ID}.json" # missing .meta
1071
+ findings = check_f2(None, sidecar, None, None, "persisted")
1072
+ assert any(f.rule_id == "F2" for f in findings)
1073
+
1074
+
1075
+ class TestF3:
1076
+ """F3 — sidecar.run_id == basename stem."""
1077
+
1078
+ def test_matching_passes(self, tmp_path: Path):
1079
+ s = make_valid_sidecar()
1080
+ sidecar_path = tmp_path / f"{VALID_RUN_ID}.meta.json"
1081
+ assert check_f3(s, sidecar_path) == []
1082
+
1083
+ def test_drift_fails(self, tmp_path: Path):
1084
+ s = make_valid_sidecar()
1085
+ s["run_id"] = "2026-04-30T15-22-04Z-aaaa"
1086
+ sidecar_path = tmp_path / f"{VALID_RUN_ID}.meta.json"
1087
+ findings = check_f3(s, sidecar_path)
1088
+ assert any(f.rule_id == "F3" for f in findings)
1089
+
1090
+
1091
+ # ---------------------------------------------------------------------------
1092
+ # Schema self-validation (verification gate item #1)
1093
+ # ---------------------------------------------------------------------------
1094
+
1095
+
1096
+ class TestSchemaSelfValidation:
1097
+ """Verification gate: each Phase 6.2 schema validates as Draft 2020-12."""
1098
+
1099
+ @pytest.mark.parametrize("schema_name", [
1100
+ "audit_artifact_entry.schema.json",
1101
+ "audit_jsonl.schema.json",
1102
+ "audit_sidecar.schema.json",
1103
+ "audit_verdict.schema.json",
1104
+ ])
1105
+ def test_schema_meta_valid(self, schema_name):
1106
+ from tests.test_helpers import load_json_schema
1107
+ if "audit_jsonl" in schema_name or "audit_sidecar" in schema_name or "audit_verdict" in schema_name:
1108
+ path = REPO / "shared/contracts/audit" / schema_name
1109
+ else:
1110
+ path = REPO / "shared/contracts/passport" / schema_name
1111
+ schema = load_json_schema(path)
1112
+ assert "$schema" in schema
1113
+
1114
+
1115
+ # ---------------------------------------------------------------------------
1116
+ # Aggregator + CLI smoke tests
1117
+ # ---------------------------------------------------------------------------
1118
+
1119
+
1120
+ class TestAggregator:
1121
+ """run_checks should emit zero findings on a fully-valid synthetic bundle."""
1122
+
1123
+ def test_clean_persisted_bundle_zero_findings(self, tmp_path: Path):
1124
+ sidecar = make_valid_sidecar()
1125
+ entry = make_valid_persisted_entry_minor()
1126
+ # align entry's bundle_manifest_sha with the sidecar's computed value
1127
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1128
+ # Codex round 8 P2 closure: B7 now resolves entry.artifact_paths
1129
+ # against repo_root and verifies disk existence + samefile match
1130
+ # against CLI-loaded paths. Aggregator test writes placeholder
1131
+ # files at the recorded basename so the resolution succeeds.
1132
+ for ext in (".jsonl", ".meta.json", ".verdict.yaml",
1133
+ ".audit_artifact_entry.json"):
1134
+ (tmp_path / f"{VALID_RUN_ID}{ext}").write_text("placeholder")
1135
+ ctx = LintContext(
1136
+ mode="persisted",
1137
+ entry=entry,
1138
+ entry_path=tmp_path / f"{VALID_RUN_ID}.audit_artifact_entry.json",
1139
+ sidecar=sidecar,
1140
+ sidecar_path=tmp_path / f"{VALID_RUN_ID}.meta.json",
1141
+ verdict=make_valid_verdict_file_minor(),
1142
+ verdict_path=tmp_path / f"{VALID_RUN_ID}.verdict.yaml",
1143
+ jsonl_events=make_valid_jsonl_events_no_tool(),
1144
+ jsonl_path=tmp_path / f"{VALID_RUN_ID}.jsonl",
1145
+ repo_root=tmp_path, # no .git, no deliverable file: B4 skipped, B2 disk skipped
1146
+ )
1147
+ findings = run_checks(ctx)
1148
+ assert findings == [], "\n".join(f.render() for f in findings)
1149
+
1150
+ def test_clean_proposal_bundle_zero_findings(self, tmp_path: Path):
1151
+ sidecar = make_valid_sidecar()
1152
+ entry = make_valid_proposal_entry_pass()
1153
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1154
+ for ext in (".jsonl", ".meta.json", ".verdict.yaml",
1155
+ ".audit_artifact_entry.json"):
1156
+ (tmp_path / f"{VALID_RUN_ID}{ext}").write_text("placeholder")
1157
+ ctx = LintContext(
1158
+ mode="proposal",
1159
+ entry=entry,
1160
+ entry_path=tmp_path / f"{VALID_RUN_ID}.audit_artifact_entry.json",
1161
+ sidecar=sidecar,
1162
+ sidecar_path=tmp_path / f"{VALID_RUN_ID}.meta.json",
1163
+ verdict=make_valid_verdict_file_pass(),
1164
+ verdict_path=tmp_path / f"{VALID_RUN_ID}.verdict.yaml",
1165
+ jsonl_events=make_valid_jsonl_events_no_tool(),
1166
+ jsonl_path=tmp_path / f"{VALID_RUN_ID}.jsonl",
1167
+ repo_root=tmp_path,
1168
+ )
1169
+ findings = run_checks(ctx)
1170
+ assert findings == [], "\n".join(f.render() for f in findings)
1171
+
1172
+ def test_jsonl_stream_mode_clean(self, tmp_path: Path):
1173
+ ctx = LintContext(
1174
+ mode="jsonl-stream",
1175
+ jsonl_events=make_valid_jsonl_events_tool_using(),
1176
+ jsonl_path=tmp_path / f"{VALID_RUN_ID}.jsonl",
1177
+ repo_root=tmp_path,
1178
+ )
1179
+ assert run_checks(ctx) == []
1180
+
1181
+ def test_jsonl_stream_mode_orphan_fails(self, tmp_path: Path):
1182
+ events = [
1183
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
1184
+ {"type": "item.completed",
1185
+ "item": {"id": "orphan", "type": "command_execution"}},
1186
+ ]
1187
+ ctx = LintContext(
1188
+ mode="jsonl-stream",
1189
+ jsonl_events=events,
1190
+ jsonl_path=tmp_path / f"{VALID_RUN_ID}.jsonl",
1191
+ repo_root=tmp_path,
1192
+ )
1193
+ findings = run_checks(ctx)
1194
+ assert any(f.rule_id == "A7" for f in findings)
1195
+
1196
+
1197
+ class TestCLI:
1198
+ """End-to-end CLI smoke tests using subprocess-style invocation through main()."""
1199
+
1200
+ def test_jsonl_stream_clean_returns_0(self, tmp_path: Path, capsys):
1201
+ jsonl = tmp_path / f"{VALID_RUN_ID}.jsonl"
1202
+ events = make_valid_jsonl_events_tool_using()
1203
+ jsonl.write_text("\n".join(json.dumps(e) for e in events) + "\n")
1204
+ rc = main(["--mode", "jsonl-stream", "--jsonl", str(jsonl)])
1205
+ assert rc == 0
1206
+
1207
+ def test_jsonl_stream_orphan_returns_1(self, tmp_path: Path):
1208
+ jsonl = tmp_path / f"{VALID_RUN_ID}.jsonl"
1209
+ events = [
1210
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
1211
+ {"type": "item.completed",
1212
+ "item": {"id": "orphan", "type": "command_execution"}},
1213
+ ]
1214
+ jsonl.write_text("\n".join(json.dumps(e) for e in events) + "\n")
1215
+ rc = main(["--mode", "jsonl-stream", "--jsonl", str(jsonl)])
1216
+ assert rc == 1
1217
+
1218
+ def test_persisted_clean_via_files(self, tmp_path: Path):
1219
+ # Write all four artifact files
1220
+ run_id = VALID_RUN_ID
1221
+ sidecar = make_valid_sidecar()
1222
+ entry = make_valid_persisted_entry_minor()
1223
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1224
+ verdict = make_valid_verdict_file_minor()
1225
+ events = make_valid_jsonl_events_no_tool()
1226
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1227
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1228
+ # verdict as YAML
1229
+ import yaml as _yaml
1230
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1231
+ (tmp_path / f"{run_id}.jsonl").write_text(
1232
+ "\n".join(json.dumps(e) for e in events) + "\n")
1233
+ rc = main([
1234
+ "--mode", "persisted",
1235
+ "--output-dir", str(tmp_path),
1236
+ "--run-id", run_id,
1237
+ "--repo-root", str(tmp_path),
1238
+ ])
1239
+ # zero findings -> rc == 0
1240
+ assert rc == 0
1241
+
1242
+ def test_persisted_missing_sidecar_returns_1(self, tmp_path: Path, capsys):
1243
+ # Codex round 1 P1: missing companion artifacts must be rejected, not
1244
+ # silently ignored. Write entry + verdict + jsonl but NOT sidecar.
1245
+ run_id = VALID_RUN_ID
1246
+ entry = make_valid_persisted_entry_minor()
1247
+ verdict = make_valid_verdict_file_minor()
1248
+ events = make_valid_jsonl_events_no_tool()
1249
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1250
+ import yaml as _yaml
1251
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1252
+ (tmp_path / f"{run_id}.jsonl").write_text(
1253
+ "\n".join(json.dumps(e) for e in events) + "\n")
1254
+ rc = main([
1255
+ "--mode", "persisted",
1256
+ "--output-dir", str(tmp_path),
1257
+ "--run-id", run_id,
1258
+ "--repo-root", str(tmp_path),
1259
+ ])
1260
+ captured = capsys.readouterr()
1261
+ assert rc == 1, captured.out
1262
+ assert "B7" in captured.out
1263
+ assert "sidecar" in captured.out
1264
+
1265
+ def test_persisted_mode_rejects_proposal_shape(self, tmp_path: Path, capsys):
1266
+ # Codex round 2 P1: --mode persisted must enforce persisted arm. A
1267
+ # proposal-shaped entry (no verified_at/verified_by) was silently
1268
+ # passing oneOf validation as the proposal arm.
1269
+ run_id = VALID_RUN_ID
1270
+ sidecar = make_valid_sidecar()
1271
+ # Build a proposal-shaped entry (no verified_at, no verified_by)
1272
+ entry = make_valid_persisted_entry_minor()
1273
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1274
+ entry["verdict"].pop("verified_at", None)
1275
+ entry["verdict"].pop("verified_by", None)
1276
+ verdict = make_valid_verdict_file_minor()
1277
+ events = make_valid_jsonl_events_no_tool()
1278
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1279
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1280
+ import yaml as _yaml
1281
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1282
+ (tmp_path / f"{run_id}.jsonl").write_text(
1283
+ "\n".join(json.dumps(e) for e in events) + "\n")
1284
+ rc = main([
1285
+ "--mode", "persisted",
1286
+ "--output-dir", str(tmp_path),
1287
+ "--run-id", run_id,
1288
+ "--repo-root", str(tmp_path),
1289
+ ])
1290
+ captured = capsys.readouterr()
1291
+ assert rc == 1, captured.out
1292
+ assert "E3" in captured.out
1293
+ assert "verified_at" in captured.out
1294
+
1295
+ def test_persisted_mode_rejects_audit_failed(self, tmp_path: Path, capsys):
1296
+ # AUDIT_FAILED is proposal-only per §3.2 lifecycle-conditional table.
1297
+ run_id = VALID_RUN_ID
1298
+ sidecar = make_valid_sidecar()
1299
+ entry = make_valid_persisted_entry_minor()
1300
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1301
+ entry["verdict"]["status"] = "AUDIT_FAILED"
1302
+ entry["verdict"]["failure_reason"] = "synthetic test"
1303
+ entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
1304
+ verdict = make_valid_verdict_file_minor()
1305
+ events = make_valid_jsonl_events_no_tool()
1306
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1307
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1308
+ import yaml as _yaml
1309
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1310
+ (tmp_path / f"{run_id}.jsonl").write_text(
1311
+ "\n".join(json.dumps(e) for e in events) + "\n")
1312
+ rc = main([
1313
+ "--mode", "persisted",
1314
+ "--output-dir", str(tmp_path),
1315
+ "--run-id", run_id,
1316
+ "--repo-root", str(tmp_path),
1317
+ ])
1318
+ captured = capsys.readouterr()
1319
+ assert rc == 1, captured.out
1320
+ assert "E5" in captured.out
1321
+ assert "AUDIT_FAILED" in captured.out
1322
+
1323
+ def test_persisted_truncated_jsonl_rejected(self, tmp_path: Path, capsys):
1324
+ # Codex round 3 P1: JSONL with only thread.started + turn.started
1325
+ # has no A7 pairing violation but is incomplete evidence. Phase 6.1's
1326
+ # parse_audit_verdict.validate_stream_shape must run before A7 in
1327
+ # full mode (non-AUDIT_FAILED bundles).
1328
+ run_id = VALID_RUN_ID
1329
+ sidecar = make_valid_sidecar()
1330
+ entry = make_valid_persisted_entry_minor()
1331
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1332
+ verdict = make_valid_verdict_file_minor() # MINOR — non-AUDIT_FAILED
1333
+ # Truncated stream: opens correctly, never closes
1334
+ truncated_events = [
1335
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
1336
+ {"type": "turn.started"},
1337
+ ]
1338
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1339
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1340
+ import yaml as _yaml
1341
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1342
+ (tmp_path / f"{run_id}.jsonl").write_text(
1343
+ "\n".join(json.dumps(e) for e in truncated_events) + "\n")
1344
+ rc = main([
1345
+ "--mode", "persisted",
1346
+ "--output-dir", str(tmp_path),
1347
+ "--run-id", run_id,
1348
+ "--repo-root", str(tmp_path),
1349
+ ])
1350
+ captured = capsys.readouterr()
1351
+ assert rc == 1, captured.out
1352
+ assert "L2-3/L2-4" in captured.out or "stream-shape" in captured.out, captured.out
1353
+
1354
+ def test_passport_scan_rejects_audit_failed(self, tmp_path: Path, capsys):
1355
+ # Codex round 3 P2: hand-edited passport entry with AUDIT_FAILED status
1356
+ # plus valid verified_at + verified_by must be flagged as forbidden.
1357
+ run_id = VALID_RUN_ID
1358
+ sidecar = make_valid_sidecar()
1359
+ entry = make_valid_persisted_entry_minor()
1360
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1361
+ verdict = make_valid_verdict_file_minor()
1362
+ events = make_valid_jsonl_events_no_tool()
1363
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1364
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1365
+ import yaml as _yaml
1366
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1367
+ (tmp_path / f"{run_id}.jsonl").write_text(
1368
+ "\n".join(json.dumps(e) for e in events) + "\n")
1369
+ # Passport with hand-edited AUDIT_FAILED entry that passes E1/E2/E6
1370
+ # presence checks (verified_at + verified_by both set, verified_by
1371
+ # is the canonical orchestrator value).
1372
+ passport = {
1373
+ "audit_artifact": [
1374
+ {
1375
+ "stage": 2,
1376
+ "agent": "synthesis_agent",
1377
+ "deliverable_path": "chapter_4/synthesis.md",
1378
+ "deliverable_sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
1379
+ "run_id": "2026-04-30T15-22-04Z-aaaa",
1380
+ "bundle_manifest_sha": "9a8b7c6d5e4f3b2a1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9876",
1381
+ "artifact_paths": {
1382
+ "jsonl": "audit_artifacts/2026-04-30T15-22-04Z-aaaa.jsonl",
1383
+ "sidecar": "audit_artifacts/2026-04-30T15-22-04Z-aaaa.meta.json",
1384
+ "verdict": "audit_artifacts/2026-04-30T15-22-04Z-aaaa.verdict.yaml",
1385
+ },
1386
+ "verdict": {
1387
+ "status": "AUDIT_FAILED",
1388
+ "round": 1,
1389
+ "target_rounds": 3,
1390
+ "finding_counts": {"p1": 0, "p2": 0, "p3": 0},
1391
+ "failure_reason": "hand-edited forgery",
1392
+ "verified_at": "2026-04-30T15:23:11.847Z",
1393
+ "verified_by": "pipeline_orchestrator_agent",
1394
+ },
1395
+ }
1396
+ ]
1397
+ }
1398
+ passport_path = tmp_path / "passport.yaml"
1399
+ passport_path.write_text(_yaml.safe_dump(passport))
1400
+ rc = main([
1401
+ "--mode", "persisted",
1402
+ "--output-dir", str(tmp_path),
1403
+ "--run-id", run_id,
1404
+ "--passport-path", str(passport_path),
1405
+ "--repo-root", str(tmp_path),
1406
+ ])
1407
+ captured = capsys.readouterr()
1408
+ assert rc == 1, captured.out
1409
+ assert "E5" in captured.out
1410
+ assert "AUDIT_FAILED" in captured.out
1411
+
1412
+ def test_persisted_forged_artifact_paths_in_entry(self, tmp_path: Path, capsys):
1413
+ # Codex round 4 P2: hand-edited entry.artifact_paths pointing at a
1414
+ # different run_id must be flagged as forgery seam, even when the
1415
+ # CLI is invoked with canonical --output-dir + --run-id and the
1416
+ # actual on-disk files have the canonical basename.
1417
+ run_id = VALID_RUN_ID
1418
+ sidecar = make_valid_sidecar()
1419
+ entry = make_valid_persisted_entry_minor()
1420
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1421
+ # Forged: artifact_paths point at a DIFFERENT run_id's files
1422
+ entry["artifact_paths"] = {
1423
+ "jsonl": "audit_artifacts/2026-04-30T15-22-04Z-ffff.jsonl",
1424
+ "sidecar": "audit_artifacts/2026-04-30T15-22-04Z-ffff.meta.json",
1425
+ "verdict": "audit_artifacts/2026-04-30T15-22-04Z-ffff.verdict.yaml",
1426
+ }
1427
+ verdict = make_valid_verdict_file_minor()
1428
+ events = make_valid_jsonl_events_no_tool()
1429
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1430
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1431
+ import yaml as _yaml
1432
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1433
+ (tmp_path / f"{run_id}.jsonl").write_text(
1434
+ "\n".join(json.dumps(e) for e in events) + "\n")
1435
+ rc = main([
1436
+ "--mode", "persisted",
1437
+ "--output-dir", str(tmp_path),
1438
+ "--run-id", run_id,
1439
+ "--repo-root", str(tmp_path),
1440
+ ])
1441
+ captured = capsys.readouterr()
1442
+ assert rc == 1, captured.out
1443
+ assert "B7" in captured.out
1444
+ assert "artifact-paths forgery seam" in captured.out
1445
+
1446
+ def test_argparse_invalid_mode_returns_64(self):
1447
+ # Codex round 4 P3: argparse-level failures must exit 64, not 2.
1448
+ result = subprocess.run(
1449
+ [sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
1450
+ "--mode", "definitely_not_a_mode"],
1451
+ capture_output=True, text=True,
1452
+ )
1453
+ assert result.returncode == 64, (
1454
+ f"argparse usage error returned {result.returncode}, expected 64\n"
1455
+ f"stderr: {result.stderr}"
1456
+ )
1457
+
1458
+ def test_argparse_unknown_flag_returns_64(self):
1459
+ result = subprocess.run(
1460
+ [sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
1461
+ "--bogus-flag"],
1462
+ capture_output=True, text=True,
1463
+ )
1464
+ assert result.returncode == 64, (
1465
+ f"unknown flag returned {result.returncode}, expected 64\n"
1466
+ f"stderr: {result.stderr}"
1467
+ )
1468
+
1469
+ def test_persisted_ack_without_prior_rejected(self, tmp_path: Path, capsys):
1470
+ # Codex round 5 P1: a persisted ack entry whose copied fields don't
1471
+ # match any prior MATERIAL entry must be rejected, not silently
1472
+ # skipped. Previously `prior is None` skipped check_c3 entirely.
1473
+ run_id = VALID_RUN_ID
1474
+ sidecar = make_valid_sidecar()
1475
+ # Build a MATERIAL ack entry with NO matching prior in the passport.
1476
+ entry = make_valid_persisted_entry_minor()
1477
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1478
+ entry["verdict"]["status"] = "MATERIAL"
1479
+ entry["verdict"]["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
1480
+ entry["acknowledgement"] = {
1481
+ "finding_ids": ["F-001"],
1482
+ "acknowledged_at": entry["verdict"]["verified_at"],
1483
+ "acknowledged_by": "user",
1484
+ }
1485
+ verdict = make_valid_verdict_file_minor()
1486
+ verdict["verdict_status"] = "MATERIAL"
1487
+ verdict["finding_counts"] = {"p1": 1, "p2": 0, "p3": 0}
1488
+ verdict["findings"] = [{
1489
+ "id": "F-001", "severity": "P1", "dimension": "3.1",
1490
+ "file": "x.md", "line": 1,
1491
+ "description": "x", "suggested_fix": "y",
1492
+ }]
1493
+ events = make_valid_jsonl_events_no_tool()
1494
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1495
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1496
+ import yaml as _yaml
1497
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1498
+ (tmp_path / f"{run_id}.jsonl").write_text(
1499
+ "\n".join(json.dumps(e) for e in events) + "\n")
1500
+ # Empty passport — no prior MATERIAL entry to copy from
1501
+ passport_path = tmp_path / "passport.yaml"
1502
+ passport_path.write_text(_yaml.safe_dump({"audit_artifact": []}))
1503
+ rc = main([
1504
+ "--mode", "persisted",
1505
+ "--output-dir", str(tmp_path),
1506
+ "--run-id", run_id,
1507
+ "--passport-path", str(passport_path),
1508
+ "--repo-root", str(tmp_path),
1509
+ ])
1510
+ captured = capsys.readouterr()
1511
+ assert rc == 1, captured.out
1512
+ assert "C3" in captured.out
1513
+ assert "no prior MATERIAL entry" in captured.out
1514
+
1515
+ def test_non_dict_entry_returns_2(self, tmp_path: Path, capsys):
1516
+ # Codex round 5 traceback closure: a non-object entry payload (e.g., `[]`)
1517
+ # must surface as a clean error, not an AttributeError traceback.
1518
+ entry_path = tmp_path / "entry.json"
1519
+ entry_path.write_text("[]")
1520
+ rc = main([
1521
+ "--mode", "proposal",
1522
+ "--entry", str(entry_path),
1523
+ "--sidecar", str(tmp_path / "no.meta.json"),
1524
+ "--verdict", str(tmp_path / "no.verdict.yaml"),
1525
+ "--jsonl", str(tmp_path / "no.jsonl"),
1526
+ "--repo-root", str(tmp_path),
1527
+ ])
1528
+ captured = capsys.readouterr()
1529
+ assert rc == 2
1530
+ assert "is not a JSON object" in captured.err
1531
+
1532
+ def test_stream_shape_runs_via_importlib(self, tmp_path: Path, capsys):
1533
+ # Codex round 5 P2: stream-shape gate must work via importlib, not
1534
+ # depend on scripts/ being on sys.path. Import this module under a
1535
+ # fresh `scripts` package qualifier and run main from there.
1536
+ run_id = VALID_RUN_ID
1537
+ sidecar = make_valid_sidecar()
1538
+ entry = make_valid_persisted_entry_minor()
1539
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1540
+ verdict = make_valid_verdict_file_minor() # MINOR — non-AUDIT_FAILED
1541
+ truncated_events = [
1542
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
1543
+ {"type": "turn.started"},
1544
+ ]
1545
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1546
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1547
+ import yaml as _yaml
1548
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(truncated_events[0])) # placeholder
1549
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1550
+ (tmp_path / f"{run_id}.jsonl").write_text(
1551
+ "\n".join(json.dumps(e) for e in truncated_events) + "\n")
1552
+ # Run via subprocess so we exercise the actual import path the gate
1553
+ # uses (importlib loads parse_audit_verdict by file path).
1554
+ result = subprocess.run(
1555
+ [sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
1556
+ "--mode", "persisted",
1557
+ "--output-dir", str(tmp_path),
1558
+ "--run-id", run_id,
1559
+ "--repo-root", str(tmp_path)],
1560
+ capture_output=True, text=True,
1561
+ )
1562
+ assert result.returncode == 1, result.stdout + result.stderr
1563
+ assert "L2-3/L2-4" in result.stdout or "stream-shape" in result.stdout, result.stdout
1564
+
1565
+ def test_audit_failed_skips_a7_pairing(self, tmp_path: Path, capsys):
1566
+ # Codex round 6 P2: AUDIT_FAILED bundles legitimately have truncated
1567
+ # streams. A7 pairing must be suspended for them, mirroring the
1568
+ # round-3 stream-shape suspension and §3.4 Layer 3 suspension.
1569
+ run_id = VALID_RUN_ID
1570
+ sidecar = make_valid_sidecar()
1571
+ # AUDIT_FAILED jsonl_thread_id is allowed to be empty per §3.4
1572
+ sidecar["stream"]["jsonl_thread_id"] = ""
1573
+ entry = make_valid_persisted_entry_minor()
1574
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1575
+ # Entry needs to be a proposal (AUDIT_FAILED is proposal-only)
1576
+ # — switch CLI mode to proposal for this test.
1577
+ entry["verdict"].pop("verified_at", None)
1578
+ entry["verdict"].pop("verified_by", None)
1579
+ entry["verdict"]["status"] = "AUDIT_FAILED"
1580
+ entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
1581
+ entry["verdict"]["failure_reason"] = "synthetic — codex killed"
1582
+ verdict = make_valid_verdict_file_minor()
1583
+ verdict["verdict_status"] = "AUDIT_FAILED"
1584
+ verdict["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
1585
+ verdict["findings"] = []
1586
+ verdict["failure_reason"] = "synthetic — codex killed"
1587
+ # Truncated stream with unmatched item.started — would fail A7 if
1588
+ # pairing weren't suspended for AUDIT_FAILED
1589
+ truncated_events = [
1590
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
1591
+ {"type": "turn.started"},
1592
+ {"type": "item.started",
1593
+ "item": {"id": "killed_tool", "type": "command_execution"}},
1594
+ ]
1595
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1596
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1597
+ import yaml as _yaml
1598
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1599
+ (tmp_path / f"{run_id}.jsonl").write_text(
1600
+ "\n".join(json.dumps(e) for e in truncated_events) + "\n")
1601
+ rc = main([
1602
+ "--mode", "proposal",
1603
+ "--output-dir", str(tmp_path),
1604
+ "--run-id", run_id,
1605
+ "--repo-root", str(tmp_path),
1606
+ ])
1607
+ captured = capsys.readouterr()
1608
+ # No A7 finding should appear — AUDIT_FAILED suspends pairing
1609
+ assert "A7" not in captured.out, (
1610
+ f"AUDIT_FAILED bundle should not produce A7 findings — got: {captured.out}"
1611
+ )
1612
+
1613
+ def test_non_object_sidecar_no_traceback(self, tmp_path: Path, capsys):
1614
+ # Codex round 6 P2: a non-object sidecar must surface as SCHEMA
1615
+ # finding without crashing cross-field checks.
1616
+ run_id = VALID_RUN_ID
1617
+ entry = make_valid_persisted_entry_minor()
1618
+ verdict = make_valid_verdict_file_minor()
1619
+ events = make_valid_jsonl_events_no_tool()
1620
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1621
+ (tmp_path / f"{run_id}.meta.json").write_text("[]") # non-object
1622
+ import yaml as _yaml
1623
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1624
+ (tmp_path / f"{run_id}.jsonl").write_text(
1625
+ "\n".join(json.dumps(e) for e in events) + "\n")
1626
+ rc = main([
1627
+ "--mode", "persisted",
1628
+ "--output-dir", str(tmp_path),
1629
+ "--run-id", run_id,
1630
+ "--repo-root", str(tmp_path),
1631
+ ])
1632
+ captured = capsys.readouterr()
1633
+ assert rc == 1, captured.out
1634
+ assert "SCHEMA" in captured.out, captured.out
1635
+ # No traceback in stderr
1636
+ assert "AttributeError" not in captured.err
1637
+ assert "Traceback" not in captured.err
1638
+
1639
+ def test_non_object_verdict_no_traceback(self, tmp_path: Path, capsys):
1640
+ run_id = VALID_RUN_ID
1641
+ sidecar = make_valid_sidecar()
1642
+ entry = make_valid_persisted_entry_minor()
1643
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1644
+ events = make_valid_jsonl_events_no_tool()
1645
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1646
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1647
+ (tmp_path / f"{run_id}.verdict.yaml").write_text("[]") # non-object
1648
+ (tmp_path / f"{run_id}.jsonl").write_text(
1649
+ "\n".join(json.dumps(e) for e in events) + "\n")
1650
+ rc = main([
1651
+ "--mode", "persisted",
1652
+ "--output-dir", str(tmp_path),
1653
+ "--run-id", run_id,
1654
+ "--repo-root", str(tmp_path),
1655
+ ])
1656
+ captured = capsys.readouterr()
1657
+ assert rc == 1, captured.out
1658
+ assert "SCHEMA" in captured.out, captured.out
1659
+ assert "AttributeError" not in captured.err
1660
+ assert "Traceback" not in captured.err
1661
+
1662
+ def test_jsonl_lacks_section6_verdict_text_rejected(self, tmp_path: Path, capsys):
1663
+ # Codex round 8 P1: stream-shape passes but verdict text is not a
1664
+ # parseable Section 6 → must be rejected as L2-4 finding.
1665
+ run_id = VALID_RUN_ID
1666
+ sidecar = make_valid_sidecar()
1667
+ entry = make_valid_persisted_entry_minor()
1668
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1669
+ verdict = make_valid_verdict_file_minor()
1670
+ # Stream-shape valid but agent_message has no Section 6 summary
1671
+ events = [
1672
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
1673
+ {"type": "turn.started"},
1674
+ {"type": "item.completed",
1675
+ "item": {"id": "item_0", "type": "agent_message",
1676
+ "text": "I considered the bundle and decided everything looks fine."}},
1677
+ {"type": "turn.completed",
1678
+ "usage": {"input_tokens": 100, "cached_input_tokens": 0,
1679
+ "output_tokens": 50, "reasoning_output_tokens": 25}},
1680
+ ]
1681
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1682
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1683
+ import yaml as _yaml
1684
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1685
+ (tmp_path / f"{run_id}.jsonl").write_text(
1686
+ "\n".join(json.dumps(e) for e in events) + "\n")
1687
+ rc = main([
1688
+ "--mode", "persisted",
1689
+ "--output-dir", str(tmp_path),
1690
+ "--run-id", run_id,
1691
+ "--repo-root", str(tmp_path),
1692
+ ])
1693
+ captured = capsys.readouterr()
1694
+ assert rc == 1, captured.out
1695
+ assert "L2-4" in captured.out, captured.out
1696
+ assert "Section 6" in captured.out, captured.out
1697
+
1698
+ def test_artifact_path_resolves_to_missing_file_rejected(self, tmp_path: Path, capsys):
1699
+ # Codex round 8 P2: entry.artifact_paths recording a path that
1700
+ # resolves under repo_root to a missing file must be flagged.
1701
+ run_id = VALID_RUN_ID
1702
+ sidecar = make_valid_sidecar()
1703
+ entry = make_valid_persisted_entry_minor()
1704
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1705
+ # Recorded paths point at a sibling subdir that doesn't exist
1706
+ entry["artifact_paths"] = {
1707
+ "jsonl": f"missing_subdir/{run_id}.jsonl",
1708
+ "sidecar": f"missing_subdir/{run_id}.meta.json",
1709
+ "verdict": f"missing_subdir/{run_id}.verdict.yaml",
1710
+ }
1711
+ verdict = make_valid_verdict_file_minor()
1712
+ events = make_valid_jsonl_events_no_tool()
1713
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1714
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1715
+ import yaml as _yaml
1716
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1717
+ (tmp_path / f"{run_id}.jsonl").write_text(
1718
+ "\n".join(json.dumps(e) for e in events) + "\n")
1719
+ rc = main([
1720
+ "--mode", "persisted",
1721
+ "--output-dir", str(tmp_path),
1722
+ "--run-id", run_id,
1723
+ "--repo-root", str(tmp_path),
1724
+ ])
1725
+ captured = capsys.readouterr()
1726
+ assert rc == 1, captured.out
1727
+ assert "B7" in captured.out
1728
+ assert "does not exist on disk" in captured.out
1729
+
1730
+ def test_audit_failed_skips_layer_3_b2_b3_b4_b5_b7(self, tmp_path: Path, capsys):
1731
+ # Codex round 9 P2: Layer 3 cross-file rules (B2/B3/B4/B5/B7) are
1732
+ # suspended for AUDIT_FAILED bundles per §3.4 + §5.6 Path B5. A
1733
+ # failed audit caused by bundle mutation legitimately has live
1734
+ # deliverable SHA != recorded SHA; treating that as B2 violation
1735
+ # would reject the failure-signaling artifact.
1736
+ run_id = VALID_RUN_ID
1737
+ sidecar = make_valid_sidecar()
1738
+ sidecar["stream"]["jsonl_thread_id"] = "" # AUDIT_FAILED suspension
1739
+ # Forensic exit_code per §3.4 rule 6 — non-zero allowed for AUDIT_FAILED
1740
+ sidecar["process"]["exit_code"] = 70
1741
+ # Drift the bundle SHA in sidecar so live recompute would mismatch (if
1742
+ # B3 ran). With AUDIT_FAILED suspension, B3 must NOT fire.
1743
+ sidecar["prompt"]["bundle"]["primary_deliverables"][0]["sha"] = "f" * 64
1744
+ # Drift git_sha to a bogus hex that fails B4 git cat-file (if it ran).
1745
+ sidecar["runner"]["git_sha"] = "deadbee"
1746
+ # Drift duration vs end-start by >>1s so B5 would fire (if it ran).
1747
+ sidecar["timing"]["duration_seconds"] = 9999.0
1748
+ entry = make_valid_persisted_entry_minor()
1749
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1750
+ entry["verdict"].pop("verified_at", None)
1751
+ entry["verdict"].pop("verified_by", None)
1752
+ entry["verdict"]["status"] = "AUDIT_FAILED"
1753
+ entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
1754
+ entry["verdict"]["failure_reason"] = "synthetic — codex killed"
1755
+ # Drift entry.deliverable_sha so B2 entry-vs-sidecar would fire (if
1756
+ # it ran) — entry side not suspended internally, only Layer 3 is.
1757
+ entry["deliverable_sha"] = "c" * 64
1758
+ verdict = make_valid_verdict_file_minor()
1759
+ verdict["verdict_status"] = "AUDIT_FAILED"
1760
+ verdict["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
1761
+ verdict["findings"] = []
1762
+ verdict["failure_reason"] = "synthetic — codex killed"
1763
+ # Truncated stream — AUDIT_FAILED expected
1764
+ events = [
1765
+ {"type": "thread.started", "thread_id": VALID_THREAD_ID},
1766
+ {"type": "turn.started"},
1767
+ # codex was killed before final agent_message + turn.completed
1768
+ ]
1769
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1770
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1771
+ import yaml as _yaml
1772
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1773
+ (tmp_path / f"{run_id}.jsonl").write_text(
1774
+ "\n".join(json.dumps(e) for e in events) + "\n")
1775
+ rc = main([
1776
+ "--mode", "proposal",
1777
+ "--output-dir", str(tmp_path),
1778
+ "--run-id", run_id,
1779
+ "--repo-root", str(tmp_path),
1780
+ ])
1781
+ captured = capsys.readouterr()
1782
+ # B2/B3/B4/B5/B7 must not fire — AUDIT_FAILED suspends Layer 3
1783
+ for rule in ("B2", "B3", "B4", "B5", "B7"):
1784
+ assert f"[{rule}]" not in captured.out, (
1785
+ f"{rule} fired on AUDIT_FAILED bundle but Layer 3 should be "
1786
+ f"suspended per §3.4: {captured.out}"
1787
+ )
1788
+
1789
+ def test_audit_failed_with_partial_jsonl_no_internal_error(self, tmp_path: Path, capsys):
1790
+ # Codex round 11 P2: AUDIT_FAILED bundle with partial/non-JSON last
1791
+ # line (codex SIGKILL'd mid-write) must NOT exit 2 internal error.
1792
+ # Round 9 suspended schema validation but _load_jsonl() still raised
1793
+ # before reaching that suspension.
1794
+ run_id = VALID_RUN_ID
1795
+ sidecar = make_valid_sidecar()
1796
+ sidecar["stream"]["jsonl_thread_id"] = ""
1797
+ sidecar["process"]["exit_code"] = 137 # 128 + SIGKILL
1798
+ entry = make_valid_persisted_entry_minor()
1799
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1800
+ entry["verdict"].pop("verified_at", None)
1801
+ entry["verdict"].pop("verified_by", None)
1802
+ entry["verdict"]["status"] = "AUDIT_FAILED"
1803
+ entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
1804
+ entry["verdict"]["failure_reason"] = "synthetic — codex SIGKILL"
1805
+ verdict = make_valid_verdict_file_minor()
1806
+ verdict["verdict_status"] = "AUDIT_FAILED"
1807
+ verdict["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
1808
+ verdict["findings"] = []
1809
+ verdict["failure_reason"] = "synthetic — codex SIGKILL"
1810
+ # Partial JSON on the last line — would raise ValueError in
1811
+ # _load_jsonl and previously caused exit 2.
1812
+ partial_jsonl = (
1813
+ json.dumps({"type": "thread.started", "thread_id": VALID_THREAD_ID}) + "\n"
1814
+ + json.dumps({"type": "turn.started"}) + "\n"
1815
+ + '{"type":"item.start' # truncated mid-write, no closing quote/brace
1816
+ )
1817
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1818
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1819
+ import yaml as _yaml
1820
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1821
+ (tmp_path / f"{run_id}.jsonl").write_text(partial_jsonl)
1822
+ rc = main([
1823
+ "--mode", "proposal",
1824
+ "--output-dir", str(tmp_path),
1825
+ "--run-id", run_id,
1826
+ "--repo-root", str(tmp_path),
1827
+ ])
1828
+ captured = capsys.readouterr()
1829
+ # Must NOT be rc=2 (internal error). Forensic-only bundle is allowed.
1830
+ assert rc != 2, (
1831
+ f"AUDIT_FAILED with partial JSONL returned rc=2 internal error; "
1832
+ f"should be handled as forensic-only bundle\nstdout: {captured.out}\n"
1833
+ f"stderr: {captured.err}"
1834
+ )
1835
+
1836
+ def test_schema_error_short_circuits_no_traceback(self, tmp_path: Path, capsys):
1837
+ # Codex round 12 P2: schema-valid-as-dict but with a nested field of
1838
+ # the wrong shape (timing: [1] passes top-level isinstance(dict) but
1839
+ # timing.get() crashes in check_b5). Short-circuit on SCHEMA error
1840
+ # so cross-field rules don't crash on input violating their type
1841
+ # presuppositions.
1842
+ run_id = VALID_RUN_ID
1843
+ sidecar = make_valid_sidecar()
1844
+ # Inject malformed nested field that schema rejects but isinstance
1845
+ # at the top level still passes:
1846
+ sidecar["timing"] = [1, 2, 3] # list instead of dict
1847
+ entry = make_valid_persisted_entry_minor()
1848
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1849
+ verdict = make_valid_verdict_file_minor()
1850
+ events = make_valid_jsonl_events_no_tool()
1851
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1852
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1853
+ import yaml as _yaml
1854
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1855
+ (tmp_path / f"{run_id}.jsonl").write_text(
1856
+ "\n".join(json.dumps(e) for e in events) + "\n")
1857
+ rc = main([
1858
+ "--mode", "persisted",
1859
+ "--output-dir", str(tmp_path),
1860
+ "--run-id", run_id,
1861
+ "--repo-root", str(tmp_path),
1862
+ ])
1863
+ captured = capsys.readouterr()
1864
+ assert rc == 1, captured.out
1865
+ assert "SCHEMA" in captured.out, captured.out
1866
+ # Critical: no traceback must reach stderr
1867
+ assert "Traceback" not in captured.err, captured.err
1868
+ assert "AttributeError" not in captured.err, captured.err
1869
+
1870
+ def test_persisted_with_higher_round_proposal_fires_d4(self, tmp_path: Path, capsys):
1871
+ # Codex round 13 P2: when --mode persisted runs and --output-dir
1872
+ # contains a higher-round unmerged proposal for the same (stage,
1873
+ # agent, deliverable_sha) tuple, D4 must fire (the persisted
1874
+ # entry's Path A is supposed to be preempted).
1875
+ run_id = VALID_RUN_ID
1876
+ # Persisted entry at round 1
1877
+ sidecar = make_valid_sidecar()
1878
+ entry = make_valid_persisted_entry_minor()
1879
+ entry["verdict"]["round"] = 1
1880
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1881
+ verdict = make_valid_verdict_file_minor()
1882
+ verdict["round"] = 1 # match entry round
1883
+ events = make_valid_jsonl_events_no_tool()
1884
+ # Move persisted into a consumed/ subdir so the scan doesn't see
1885
+ # it as an unmerged proposal (matches §4.9 step 9 convention).
1886
+ consumed = tmp_path / "consumed"
1887
+ consumed.mkdir()
1888
+ (consumed / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1889
+ # Companion files in output_dir (where the lint expects them)
1890
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1891
+ import yaml as _yaml
1892
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1893
+ (tmp_path / f"{run_id}.jsonl").write_text(
1894
+ "\n".join(json.dumps(e) for e in events) + "\n")
1895
+
1896
+ # Higher-round proposal in output_dir (round 2 > persisted round 1)
1897
+ proposal_run_id = "2026-04-30T15-30-00Z-eeee"
1898
+ proposal_entry = make_valid_persisted_entry_minor()
1899
+ proposal_entry["verdict"]["round"] = 2 # higher than persisted round=1
1900
+ proposal_entry["verdict"]["status"] = "PASS"
1901
+ proposal_entry["verdict"]["finding_counts"] = {"p1": 0, "p2": 0, "p3": 0}
1902
+ proposal_entry["verdict"].pop("verified_at", None) # proposal arm
1903
+ proposal_entry["verdict"].pop("verified_by", None)
1904
+ proposal_entry["run_id"] = proposal_run_id
1905
+ proposal_entry["artifact_paths"] = {
1906
+ "jsonl": f"{proposal_run_id}.jsonl",
1907
+ "sidecar": f"{proposal_run_id}.meta.json",
1908
+ "verdict": f"{proposal_run_id}.verdict.yaml",
1909
+ }
1910
+ (tmp_path / f"{proposal_run_id}.audit_artifact_entry.json").write_text(
1911
+ json.dumps(proposal_entry))
1912
+
1913
+ rc = main([
1914
+ "--mode", "persisted",
1915
+ "--entry", str(consumed / f"{run_id}.audit_artifact_entry.json"),
1916
+ "--output-dir", str(tmp_path),
1917
+ "--run-id", run_id,
1918
+ "--repo-root", str(tmp_path),
1919
+ ])
1920
+ captured = capsys.readouterr()
1921
+ assert rc == 1, captured.out
1922
+ assert "D4" in captured.out, captured.out
1923
+ assert "round=2" in captured.out and "round=1" in captured.out, captured.out
1924
+
1925
+ def test_script_is_executable(self):
1926
+ # Codex round 14 P1: spec §5.2 L2-5 invokes the lint as
1927
+ # `scripts/check_audit_artifact_consistency.py --mode jsonl-stream …`
1928
+ # without a `python` prefix. The file must carry the executable
1929
+ # bit (100755) like Phase 6.1's audit_snapshot.py / parse_audit_
1930
+ # verdict.py / run_codex_audit.sh — direct exec failing with
1931
+ # permission denied (exit 126) would block the orchestrator gate.
1932
+ import os
1933
+ script_path = REPO / "scripts/check_audit_artifact_consistency.py"
1934
+ assert os.access(script_path, os.X_OK), (
1935
+ f"{script_path} must be executable for orchestrator §5.2 L2-5 "
1936
+ f"direct invocation. Run `chmod +x` and `git update-index --chmod=+x`."
1937
+ )
1938
+
1939
+ def test_script_runs_via_direct_exec(self):
1940
+ # End-to-end smoke: invoke the script directly (no `python` prefix)
1941
+ # and confirm it produces the documented harness exit code. Belt
1942
+ # and braces against future `chmod 644` regressions.
1943
+ script_path = REPO / "scripts/check_audit_artifact_consistency.py"
1944
+ result = subprocess.run(
1945
+ [str(script_path), "--example-validation-harness"],
1946
+ capture_output=True, text=True,
1947
+ )
1948
+ assert result.returncode == 0, (
1949
+ f"direct exec returned {result.returncode} (expected 0)\n"
1950
+ f"stdout: {result.stdout}\nstderr: {result.stderr}"
1951
+ )
1952
+
1953
+ def test_swap_verdict_file_with_different_run_id_rejected(self, tmp_path: Path, capsys):
1954
+ # Codex round 15 P1: swap-one-verdict-file forgery — a verdict.yaml
1955
+ # with all valid schema/cross-field but internal run_id pointing at
1956
+ # a different run, renamed into the canonical <run_id>.verdict.yaml
1957
+ # slot. C1 mirrors only the nested verdict block; B7 (pre-round-15)
1958
+ # checked sidecar.run_id but not verdict.run_id.
1959
+ run_id = VALID_RUN_ID
1960
+ sidecar = make_valid_sidecar()
1961
+ entry = make_valid_persisted_entry_minor()
1962
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1963
+ verdict = make_valid_verdict_file_minor()
1964
+ verdict["run_id"] = "2026-04-30T15-30-00Z-ffff" # different run!
1965
+ events = make_valid_jsonl_events_no_tool()
1966
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1967
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1968
+ import yaml as _yaml
1969
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
1970
+ (tmp_path / f"{run_id}.jsonl").write_text(
1971
+ "\n".join(json.dumps(e) for e in events) + "\n")
1972
+ rc = main([
1973
+ "--mode", "persisted",
1974
+ "--output-dir", str(tmp_path),
1975
+ "--run-id", run_id,
1976
+ "--repo-root", str(tmp_path),
1977
+ ])
1978
+ captured = capsys.readouterr()
1979
+ assert rc == 1, captured.out
1980
+ assert "B7" in captured.out
1981
+ assert "verdict.run_id" in captured.out
1982
+ assert "swap-one-verdict-file forgery" in captured.out
1983
+
1984
+ def test_only_entry_flag_resolves_companions_from_artifact_paths(self, tmp_path: Path, capsys):
1985
+ # Codex round 16 P2: caller passes only --entry + --repo-root; the
1986
+ # lint must resolve sidecar/verdict/jsonl from
1987
+ # entry.artifact_paths instead of demanding redundant flags.
1988
+ run_id = VALID_RUN_ID
1989
+ sidecar = make_valid_sidecar()
1990
+ entry = make_valid_persisted_entry_minor()
1991
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
1992
+ verdict = make_valid_verdict_file_minor()
1993
+ events = make_valid_jsonl_events_no_tool()
1994
+ # Companion files in tmp_path; entry.artifact_paths records them
1995
+ # as repo-relative basenames (matching the round-7+8 fixture
1996
+ # convention).
1997
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
1998
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
1999
+ import yaml as _yaml
2000
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
2001
+ (tmp_path / f"{run_id}.jsonl").write_text(
2002
+ "\n".join(json.dumps(e) for e in events) + "\n")
2003
+ # ONLY --entry + --repo-root, no --output-dir, no --sidecar/verdict/jsonl
2004
+ rc = main([
2005
+ "--mode", "persisted",
2006
+ "--entry", str(tmp_path / f"{run_id}.audit_artifact_entry.json"),
2007
+ "--repo-root", str(tmp_path),
2008
+ ])
2009
+ captured = capsys.readouterr()
2010
+ assert rc == 0, captured.out
2011
+
2012
+ def test_persisted_schema_invalid_sidecar_returns_1(self, tmp_path: Path, capsys):
2013
+ # Codex round 1 P2: schema-invalid sidecar must be rejected even when
2014
+ # cross-field rules don't cover the malformed field.
2015
+ run_id = VALID_RUN_ID
2016
+ sidecar = make_valid_sidecar()
2017
+ # Inject a malformed bare extra field (additionalProperties: false on
2018
+ # sidecar root → schema rejects unknown top-level key).
2019
+ sidecar["definitely_not_in_schema"] = "drift"
2020
+ entry = make_valid_persisted_entry_minor()
2021
+ entry["bundle_manifest_sha"] = sidecar["prompt"]["bundle"]["bundle_manifest_sha"]
2022
+ verdict = make_valid_verdict_file_minor()
2023
+ events = make_valid_jsonl_events_no_tool()
2024
+ (tmp_path / f"{run_id}.audit_artifact_entry.json").write_text(json.dumps(entry))
2025
+ (tmp_path / f"{run_id}.meta.json").write_text(json.dumps(sidecar))
2026
+ import yaml as _yaml
2027
+ (tmp_path / f"{run_id}.verdict.yaml").write_text(_yaml.safe_dump(verdict))
2028
+ (tmp_path / f"{run_id}.jsonl").write_text(
2029
+ "\n".join(json.dumps(e) for e in events) + "\n")
2030
+ rc = main([
2031
+ "--mode", "persisted",
2032
+ "--output-dir", str(tmp_path),
2033
+ "--run-id", run_id,
2034
+ "--repo-root", str(tmp_path),
2035
+ ])
2036
+ captured = capsys.readouterr()
2037
+ assert rc == 1, captured.out
2038
+ assert "SCHEMA" in captured.out
2039
+ assert "definitely_not_in_schema" in captured.out
2040
+
2041
+
2042
+ # ---------------------------------------------------------------------------
2043
+ # Example validation harness (F4) — smoke test it runs without crash
2044
+ # ---------------------------------------------------------------------------
2045
+
2046
+
2047
+ class TestExampleHarness:
2048
+ """F4 — example-validation-harness runs end-to-end on the real spec dir."""
2049
+
2050
+ def test_harness_runs(self):
2051
+ # Should not raise; may produce findings (known §3.4 hyphen drift)
2052
+ findings = run_example_harness(REPO)
2053
+ # We don't assert zero findings — the spec is known to have drift the
2054
+ # harness should surface. We only assert the harness ran and any
2055
+ # findings it produces are well-formed LintErrors.
2056
+ assert all(isinstance(f, LintError) for f in findings)
2057
+ # And every finding's rule_id is "F4" or "SCHEMA"-prefixed
2058
+ for f in findings:
2059
+ assert f.rule_id in ("F4", "SCHEMA"), f.render()
2060
+
2061
+
2062
+ # ---------------------------------------------------------------------------
2063
+ # Fixture smoke tests — codex round 7 P2 closure
2064
+ # ---------------------------------------------------------------------------
2065
+
2066
+
2067
+ FIXTURE_ROOT = REPO / "scripts/fixtures/audit_artifact_consistency"
2068
+
2069
+
2070
+ class TestFixtureSmoke:
2071
+ """Verify scripts/fixtures/audit_artifact_consistency/ README invocation
2072
+ examples actually produce the documented exit codes.
2073
+
2074
+ Codex round 7 P2: positive fixtures only pass when --repo-root points
2075
+ at the fixture directory itself (so B3's live-disk gate hits the
2076
+ synthetic-fixture safe-skip via missing .git/ marker). Without this
2077
+ test, README drift could leave positive fixtures silently broken
2078
+ against the documented invocation.
2079
+ """
2080
+
2081
+ def test_positive_persisted_minor(self):
2082
+ bundle = FIXTURE_ROOT / "positive/persisted_minor"
2083
+ result = subprocess.run(
2084
+ [sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
2085
+ "--mode", "persisted",
2086
+ "--output-dir", str(bundle),
2087
+ "--run-id", "2026-04-30T15-22-04Z-d8f3",
2088
+ "--repo-root", str(bundle)],
2089
+ capture_output=True, text=True,
2090
+ )
2091
+ assert result.returncode == 0, (
2092
+ f"positive fixture persisted_minor returned {result.returncode}\n"
2093
+ f"stdout: {result.stdout}\nstderr: {result.stderr}"
2094
+ )
2095
+
2096
+ def test_positive_proposal_pass(self):
2097
+ bundle = FIXTURE_ROOT / "positive/proposal_pass"
2098
+ result = subprocess.run(
2099
+ [sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
2100
+ "--mode", "proposal",
2101
+ "--output-dir", str(bundle),
2102
+ "--run-id", "2026-04-30T15-22-04Z-d8f3",
2103
+ "--repo-root", str(bundle)],
2104
+ capture_output=True, text=True,
2105
+ )
2106
+ assert result.returncode == 0, (
2107
+ f"positive fixture proposal_pass returned {result.returncode}\n"
2108
+ f"stdout: {result.stdout}\nstderr: {result.stderr}"
2109
+ )
2110
+
2111
+ def test_negative_a1_pass_with_p1(self):
2112
+ bundle = FIXTURE_ROOT / "negative/a1_pass_with_p1"
2113
+ result = subprocess.run(
2114
+ [sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
2115
+ "--mode", "proposal",
2116
+ "--output-dir", str(bundle),
2117
+ "--run-id", "2026-04-30T15-22-04Z-d8f3",
2118
+ "--repo-root", str(bundle)],
2119
+ capture_output=True, text=True,
2120
+ )
2121
+ assert result.returncode == 1, result.stdout
2122
+ assert "A1" in result.stdout, result.stdout
2123
+
2124
+ def test_negative_a7_orphan_completion(self):
2125
+ jsonl = FIXTURE_ROOT / "negative/a7_orphan_completion/2026-04-30T15-22-04Z-d8f3.jsonl"
2126
+ result = subprocess.run(
2127
+ [sys.executable, str(REPO / "scripts/check_audit_artifact_consistency.py"),
2128
+ "--mode", "jsonl-stream",
2129
+ "--jsonl", str(jsonl)],
2130
+ capture_output=True, text=True,
2131
+ )
2132
+ assert result.returncode == 1, result.stdout
2133
+ assert "A7" in result.stdout, result.stdout