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,3505 @@
1
+ #!/usr/bin/env python3
2
+ """Offline-only execution envelope for #659.
3
+
4
+ The command set creates and validates files. It has no provider transport,
5
+ dispatch, probe, actor, subject, judge, or adjudicator execution path.
6
+ Externally recorded transcripts may be ingested only after a separately held
7
+ fresh authorization; this envelope never grants that authorization.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import base64
13
+ import copy
14
+ from datetime import datetime
15
+ import hashlib
16
+ import json
17
+ import os
18
+ from pathlib import Path
19
+ import re
20
+ import secrets
21
+ import stat
22
+ import sys
23
+ import unicodedata
24
+ from typing import Any, NoReturn
25
+
26
+ from jsonschema import Draft202012Validator, FormatChecker
27
+
28
+ import validate_ideation_diversity_assets as phase1
29
+
30
+
31
+ REPO_ROOT = Path(__file__).resolve().parents[1]
32
+ SUITE = "within_session_ideation_diversity"
33
+ SUITE_ROOT = REPO_ROOT / "evals" / "heldout" / SUITE
34
+ RUN_PLAN_SCHEMA = SUITE_ROOT / "run_plan.schema.json"
35
+ AUTHORIZATION_SCHEMA = SUITE_ROOT / "authorization_record.schema.json"
36
+ TRANSCRIPT_SCHEMA = SUITE_ROOT / "transcript.schema.json"
37
+ INGESTION_SCHEMA = SUITE_ROOT / "ingestion_manifest.schema.json"
38
+ STOP_INTENT_SCHEMA = SUITE_ROOT / "stop_intent.schema.json"
39
+ BLIND_PACKET_SCHEMA = SUITE_ROOT / "blind_packet.schema.json"
40
+ BLIND_INVENTORY_SCHEMA = SUITE_ROOT / "blind_inventory.schema.json"
41
+ BLIND_MANIFEST_SCHEMA = SUITE_ROOT / "blind_manifest.schema.json"
42
+ PRIVATE_ARM_MAP_SCHEMA = SUITE_ROOT / "private_arm_map.schema.json"
43
+ BLIND_INTENT_SCHEMA = SUITE_ROOT / "blind_intent.schema.json"
44
+ PRODUCTION_PROMPT = REPO_ROOT / "deep-research" / "agents" / "socratic_mentor_agent.md"
45
+ CODEBOOK = SUITE_ROOT / "codebook.md"
46
+ MAX_INPUT_BYTES = 4 * 1024 * 1024
47
+ MAX_STOP_INTENT_BYTES = 8 * 1024 * 1024
48
+ STOP_INTENT_REF = "stop-intent.json"
49
+ STOP_INTENT_FALLBACK_REF = "stop-intent.fallback.json"
50
+ BLIND_INTENT_REF = "blind-intent.json"
51
+ BLIND_STAGING_REF = "blind-staging"
52
+ BLIND_ALIAS_ROOT = "blind-atomic-aliases"
53
+ ATTEMPT_JOURNAL_ROOT = "terminal-journal"
54
+ COMMIT_RE = re.compile(r"^[0-9a-f]{40}$")
55
+ RUN_ID_RE = re.compile(r"^[a-z0-9][a-z0-9._-]{2,79}$")
56
+ EXTERNAL_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{7,255}$")
57
+ STOP_RULES = [
58
+ "prompt_or_role_card_hash_mismatch",
59
+ "arm_leakage",
60
+ "ineligible_session",
61
+ "authorization_record_missing_or_mismatch",
62
+ "unplanned_tool_action",
63
+ "unplanned_network_action",
64
+ "evidence_write_failure",
65
+ "partial_subject_output",
66
+ "actor_protocol_deviation",
67
+ "out_of_order_ingestion",
68
+ "transcript_contract_failure",
69
+ ]
70
+ BLINDED_TO = [
71
+ "mechanism",
72
+ "arm",
73
+ "pair_mapping",
74
+ "replicate",
75
+ "expected_direction",
76
+ "other_transcripts",
77
+ "aggregate",
78
+ "prior_labels",
79
+ ]
80
+ ARM_LEAK_TOKENS = (
81
+ "adjacent_probe",
82
+ "exploratory_guardrails",
83
+ "ars_socratic_adjacent_probe",
84
+ "paired transcript",
85
+ "expected metric direction",
86
+ )
87
+ MESSAGE_EVENT_KINDS = {"actor_message", "subject_message"}
88
+ TERMINAL_EVENT_KINDS = {
89
+ "session_started",
90
+ "session_completed",
91
+ "session_blocked",
92
+ "session_partial",
93
+ "tool_action",
94
+ "network_action",
95
+ "evidence_write_failure",
96
+ }
97
+ RAW_EVENT_KINDS = MESSAGE_EVENT_KINDS | TERMINAL_EVENT_KINDS
98
+ HUMAN_EVIDENCE_PATTERNS = (
99
+ re.compile(
100
+ r"\b(?:prior|human|judge|annotator|reviewer)\s+"
101
+ r"(?:label|rating|decision|evidence)s?\b",
102
+ re.IGNORECASE,
103
+ ),
104
+ re.compile(r"\badjudicat(?:e|ed|es|ing|ion|or|ors)\b", re.IGNORECASE),
105
+ re.compile(
106
+ r"\b(?:A[12]|M[1-5])\s*(?:=|:)?\s*"
107
+ r"(?:yes|no|not_applicable)\b",
108
+ re.IGNORECASE,
109
+ ),
110
+ re.compile(r"(?:真人|人類)(?:專家|評審|標註|裁決|證據)"),
111
+ re.compile(r"(?:先前|既有|前輪)(?:標註|評分|裁決)"),
112
+ re.compile(r"\b(?:gold[- ]standard\s+)?(?:coder|annotator|rater)\b", re.IGNORECASE),
113
+ re.compile(r"\btie[- ]breaker\b", re.IGNORECASE),
114
+ re.compile(r"(?:黃金標準|標註者|編碼者|評分者|裁決者)"),
115
+ )
116
+ HUMAN_EVIDENCE_COMPACT_PHRASES = (
117
+ "adjudicate",
118
+ "adjudicated",
119
+ "adjudicates",
120
+ "adjudicating",
121
+ "adjudication",
122
+ "adjudicator",
123
+ "adjudicators",
124
+ "tie breaker",
125
+ ) + tuple(
126
+ f"{source} {evidence}"
127
+ for source in ("prior", "human", "judge", "annotator", "reviewer")
128
+ for evidence in (
129
+ "label", "labels", "rating", "ratings", "decision", "decisions",
130
+ "evidence", "evidences",
131
+ )
132
+ ) + tuple(
133
+ f"{prefix} {role}"
134
+ for prefix in ("", "gold standard")
135
+ for role in ("coder", "annotator", "rater")
136
+ ) + tuple(
137
+ f"{label} {value}"
138
+ for label in ("a1", "a2", "m1", "m2", "m3", "m4", "m5")
139
+ for value in ("yes", "no", "not applicable")
140
+ ) + tuple(
141
+ f"{source}{evidence}"
142
+ for source in ("真人", "人類")
143
+ for evidence in ("專家", "評審", "標註", "裁決", "證據")
144
+ ) + tuple(
145
+ f"{source}{evidence}"
146
+ for source in ("先前", "既有", "前輪")
147
+ for evidence in ("標註", "評分", "裁決")
148
+ ) + ("黃金標準", "標註者", "編碼者", "評分者", "裁決者")
149
+ MAPPING_LEAK_PATTERNS = (
150
+ re.compile(r"\breplicate(?:\s+id)?\s*(?:=|:|#)?\s*[12]\b", re.IGNORECASE),
151
+ re.compile(r"\b(?:cell|pair|arm|scenario|experiment)\s+id\s*(?:=|:)", re.IGNORECASE),
152
+ re.compile(r"\bother transcripts?\b", re.IGNORECASE),
153
+ re.compile(r"\b(?:adjacent[- ]probe|exploratory[- ]guardrails?)\b", re.IGNORECASE),
154
+ re.compile(
155
+ r"\b(?:treatment|control|experimental)\s+"
156
+ r"(?:arm|condition|group)\b",
157
+ re.IGNORECASE,
158
+ ),
159
+ re.compile(
160
+ r"\b(?:mechanism|feature|probe|guardrails?)\b.{0,24}"
161
+ r"\b(?:enabled|disabled|ablated|on|off)\b",
162
+ re.IGNORECASE,
163
+ ),
164
+ re.compile(
165
+ r"\b(?:preceding|previous|prior|other|paired)\s+"
166
+ r"(?:session|transcript|response)\b",
167
+ re.IGNORECASE,
168
+ ),
169
+ re.compile(r"(?:處置|控制|實驗)(?:組|條件|臂)"),
170
+ re.compile(r"(?:前一|先前|其他|配對)(?:場次|對話|逐字稿|回應)"),
171
+ )
172
+ MAPPING_LEAK_COMPACT_PHRASES = (
173
+ "adjacent probe",
174
+ "exploratory guardrail",
175
+ "exploratory guardrails",
176
+ ) + tuple(
177
+ f"replicate {middle}{value}"
178
+ for middle in ("", "id ")
179
+ for value in ("1", "2")
180
+ ) + tuple(
181
+ f"{entity} id"
182
+ for entity in ("cell", "pair", "arm", "scenario", "experiment")
183
+ ) + tuple(
184
+ f"{arm} {kind}"
185
+ for arm in ("treatment", "control", "experimental")
186
+ for kind in ("arm", "condition", "group")
187
+ ) + tuple(
188
+ f"{relation} {artifact}"
189
+ for relation in ("preceding", "previous", "prior", "other", "paired")
190
+ for artifact in ("session", "transcript", "response")
191
+ ) + ("other transcripts",) + tuple(
192
+ f"{arm}{kind}"
193
+ for arm in ("處置", "控制", "實驗")
194
+ for kind in ("組", "條件", "臂")
195
+ ) + tuple(
196
+ f"{relation}{artifact}"
197
+ for relation in ("前一", "先前", "其他", "配對")
198
+ for artifact in ("場次", "對話", "逐字稿", "回應")
199
+ )
200
+ EXPERIMENTS = (
201
+ (
202
+ "adjacent_probe",
203
+ ("adjacent_probe_off", "adjacent_probe_on"),
204
+ ),
205
+ (
206
+ "exploratory_guardrails",
207
+ ("exploratory_guardrails_on", "exploratory_guardrails_ablated"),
208
+ ),
209
+ )
210
+ ASSET_PATHS = (
211
+ "docs/design/2026-08-13-659-within-session-ideation-diversity-design.md",
212
+ "evals/heldout/within_session_ideation_diversity/README.md",
213
+ "evals/heldout/within_session_ideation_diversity/heldout_set.json",
214
+ "evals/heldout/within_session_ideation_diversity/heldout_set.schema.json",
215
+ "evals/heldout/within_session_ideation_diversity/codebook.md",
216
+ "evals/heldout/within_session_ideation_diversity/nonproduction_variant.json",
217
+ "evals/heldout/within_session_ideation_diversity/run_plan.schema.json",
218
+ "evals/heldout/within_session_ideation_diversity/authorization_record.schema.json",
219
+ "evals/heldout/within_session_ideation_diversity/transcript.schema.json",
220
+ "evals/heldout/within_session_ideation_diversity/ingestion_manifest.schema.json",
221
+ "evals/heldout/within_session_ideation_diversity/stop_intent.schema.json",
222
+ "evals/heldout/within_session_ideation_diversity/blind_packet.schema.json",
223
+ "evals/heldout/within_session_ideation_diversity/blind_inventory.schema.json",
224
+ "evals/heldout/within_session_ideation_diversity/blind_manifest.schema.json",
225
+ "evals/heldout/within_session_ideation_diversity/private_arm_map.schema.json",
226
+ "evals/heldout/within_session_ideation_diversity/blind_intent.schema.json",
227
+ "deep-research/agents/socratic_mentor_agent.md",
228
+ "scripts/run_ideation_diversity_no_call.py",
229
+ )
230
+
231
+
232
+ class EnvelopeError(RuntimeError):
233
+ """Closed no-call envelope failure."""
234
+
235
+
236
+ class StopViolation(EnvelopeError):
237
+ """A transcript event that permanently stops the run."""
238
+
239
+ def __init__(self, code: str, detail: str):
240
+ super().__init__(detail)
241
+ self.code = code
242
+ self.detail = detail
243
+
244
+
245
+ def _fail(message: str) -> NoReturn:
246
+ raise EnvelopeError(message)
247
+
248
+
249
+ def _reject_pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
250
+ value: dict[str, Any] = {}
251
+ for key, item in pairs:
252
+ if key in value:
253
+ raise ValueError(f"duplicate JSON key {key!r}")
254
+ value[key] = item
255
+ return value
256
+
257
+
258
+ def _strict_loads(raw: bytes) -> dict[str, Any]:
259
+ try:
260
+ value = json.loads(
261
+ raw.decode("utf-8"),
262
+ object_pairs_hook=_reject_pairs,
263
+ parse_constant=lambda token: (_ for _ in ()).throw(
264
+ ValueError(f"non-finite JSON value {token!r}")
265
+ ),
266
+ )
267
+ except (UnicodeError, ValueError, json.JSONDecodeError) as exc:
268
+ raise EnvelopeError(f"invalid strict JSON: {exc}") from exc
269
+ if not isinstance(value, dict):
270
+ _fail("JSON root must be an object")
271
+ return value
272
+
273
+
274
+ def _canonical(value: Any) -> bytes:
275
+ try:
276
+ return json.dumps(
277
+ value,
278
+ ensure_ascii=False,
279
+ allow_nan=False,
280
+ sort_keys=True,
281
+ separators=(",", ":"),
282
+ ).encode("utf-8")
283
+ except (TypeError, ValueError, UnicodeError) as exc:
284
+ raise EnvelopeError(f"value is not canonical JSON: {exc}") from exc
285
+
286
+
287
+ def _json_bytes(value: Any) -> bytes:
288
+ return _canonical(value) + b"\n"
289
+
290
+
291
+ def _sha(raw: bytes) -> str:
292
+ return hashlib.sha256(raw).hexdigest()
293
+
294
+
295
+ def _has_semantic_text(
296
+ value: Any, *, allowed_categories: frozenset[str] = frozenset({"L", "N", "S"})
297
+ ) -> bool:
298
+ """Require an NFKC-stable letter, number, or explicitly allowed symbol."""
299
+ if not isinstance(value, str):
300
+ return False
301
+ normalized = unicodedata.normalize("NFKC", value)
302
+ return any(
303
+ unicodedata.category(character)[0] in allowed_categories
304
+ for character in normalized
305
+ )
306
+
307
+
308
+ def _require_semantic_text(value: Any, label: str) -> None:
309
+ if not _has_semantic_text(value):
310
+ _fail(f"{label} must contain semantic text")
311
+
312
+
313
+ def _require_identity_text(value: Any, label: str) -> None:
314
+ if not _has_semantic_text(value, allowed_categories=frozenset({"L", "N"})):
315
+ _fail(f"{label} must contain semantic text with a letter or number")
316
+
317
+
318
+ def _hash_file_stable(path: Path) -> tuple[str, int]:
319
+ """Stream-hash an inventory file without imposing the transcript input cap."""
320
+ try:
321
+ if path.is_symlink() or not path.is_file():
322
+ _fail(f"inventory entry must be a regular non-symlink file: {path}")
323
+ before = path.stat()
324
+ digest = hashlib.sha256()
325
+ size = 0
326
+ with path.open("rb") as stream:
327
+ while True:
328
+ chunk = stream.read(1024 * 1024)
329
+ if not chunk:
330
+ break
331
+ digest.update(chunk)
332
+ size += len(chunk)
333
+ after = path.stat()
334
+ except OSError as exc:
335
+ raise EnvelopeError(f"cannot hash inventory file {path}: {exc}") from exc
336
+ if size != after.st_size or (
337
+ before.st_dev,
338
+ before.st_ino,
339
+ before.st_size,
340
+ before.st_mtime_ns,
341
+ ) != (
342
+ after.st_dev,
343
+ after.st_ino,
344
+ after.st_size,
345
+ after.st_mtime_ns,
346
+ ):
347
+ _fail(f"inventory file changed while being hashed: {path}")
348
+ return digest.hexdigest(), size
349
+
350
+
351
+ def _read_file(path: Path, *, limit: int = MAX_INPUT_BYTES) -> bytes:
352
+ try:
353
+ if path.is_symlink() or not path.is_file():
354
+ _fail(f"input must be a regular non-symlink file: {path}")
355
+ before = path.stat()
356
+ if before.st_size > limit:
357
+ _fail(f"input exceeds {limit} bytes: {path}")
358
+ raw = path.read_bytes()
359
+ after = path.stat()
360
+ except OSError as exc:
361
+ raise EnvelopeError(f"cannot read {path}: {exc}") from exc
362
+ if len(raw) != after.st_size or (
363
+ before.st_dev,
364
+ before.st_ino,
365
+ before.st_size,
366
+ before.st_mtime_ns,
367
+ ) != (
368
+ after.st_dev,
369
+ after.st_ino,
370
+ after.st_size,
371
+ after.st_mtime_ns,
372
+ ):
373
+ _fail(f"input changed while being read: {path}")
374
+ return raw
375
+
376
+
377
+ def _write_new(path: Path, raw: bytes, *, mode: int = 0o644) -> None:
378
+ """Publish complete bytes exclusively without ever exposing a partial target."""
379
+ temporary: Path | None = None
380
+ descriptor: int | None = None
381
+ try:
382
+ path.parent.mkdir(parents=True, exist_ok=True)
383
+ temporary = path.with_name(
384
+ f".{path.name}.atomic-write-{secrets.token_hex(12)}.tmp"
385
+ )
386
+ flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
387
+ if hasattr(os, "O_NOFOLLOW"):
388
+ flags |= os.O_NOFOLLOW
389
+ descriptor = os.open(temporary, flags, mode)
390
+ view = memoryview(raw)
391
+ written = 0
392
+ while written < len(view):
393
+ count = os.write(descriptor, view[written:])
394
+ if count <= 0:
395
+ raise OSError("atomic staging write made no progress")
396
+ written += count
397
+ os.fsync(descriptor)
398
+ os.close(descriptor)
399
+ descriptor = None
400
+ # A hard link is an atomic, exclusive publication: an existing target
401
+ # is never replaced and readers can only observe complete staged bytes.
402
+ os.link(temporary, path, follow_symlinks=False)
403
+ _fsync_directory(path.parent)
404
+ except FileExistsError as exc:
405
+ raise EnvelopeError(f"refusing to overwrite {path}") from exc
406
+ except OSError as exc:
407
+ raise EnvelopeError(f"cannot create {path}: {exc}") from exc
408
+ finally:
409
+ if descriptor is not None:
410
+ try:
411
+ os.close(descriptor)
412
+ except OSError:
413
+ pass
414
+ if temporary is not None and temporary.exists():
415
+ try:
416
+ temporary.unlink()
417
+ _fsync_directory(temporary.parent)
418
+ except OSError as exc:
419
+ # A complete target may already be published, but an orphaned
420
+ # staging name would violate the exact inventory contract.
421
+ raise EnvelopeError(
422
+ f"cannot remove unpublished atomic staging file {temporary}: {exc}"
423
+ ) from exc
424
+
425
+
426
+ def _publish_stop_intent(path: Path, raw: bytes, staging_ref: str) -> Path:
427
+ """Publish to the primary or fallback slot from one durable hardlink alias."""
428
+ staging = path.parent / staging_ref
429
+ if staging.exists():
430
+ if _read_file(staging, limit=MAX_STOP_INTENT_BYTES) != raw:
431
+ _fail(f"write-once evidence collision at {staging}")
432
+ else:
433
+ descriptor: int | None = None
434
+ try:
435
+ flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
436
+ if hasattr(os, "O_NOFOLLOW"):
437
+ flags |= os.O_NOFOLLOW
438
+ descriptor = os.open(staging, flags, 0o644)
439
+ view = memoryview(raw)
440
+ written = 0
441
+ while written < len(view):
442
+ count = os.write(descriptor, view[written:])
443
+ if count <= 0:
444
+ raise OSError("stop-intent staging write made no progress")
445
+ written += count
446
+ os.fsync(descriptor)
447
+ os.close(descriptor)
448
+ descriptor = None
449
+ _fsync_directory(staging.parent)
450
+ except OSError as exc:
451
+ raise EnvelopeError(
452
+ f"cannot stage durable stop intent {staging}: {exc}"
453
+ ) from exc
454
+ finally:
455
+ if descriptor is not None:
456
+ try:
457
+ os.close(descriptor)
458
+ except OSError:
459
+ pass
460
+ published: Path | None = None
461
+ errors: list[str] = []
462
+ for target in (path, path.parent / STOP_INTENT_FALLBACK_REF):
463
+ try:
464
+ if target.exists():
465
+ if _read_file(target, limit=MAX_STOP_INTENT_BYTES) != raw:
466
+ _fail(f"write-once evidence collision at {target}")
467
+ else:
468
+ os.link(staging, target, follow_symlinks=False)
469
+ _fsync_directory(target.parent)
470
+ if target.stat().st_ino != staging.stat().st_ino:
471
+ _fail(f"durable stop-intent slot is not the staged hardlink: {target}")
472
+ published = target
473
+ break
474
+ except OSError as exc:
475
+ # If directory fsync reported after a successful link, that linked
476
+ # slot is already the durable publication boundary.
477
+ if target.exists():
478
+ try:
479
+ if (
480
+ _read_file(target, limit=MAX_STOP_INTENT_BYTES) == raw
481
+ and target.stat().st_ino == staging.stat().st_ino
482
+ ):
483
+ published = target
484
+ break
485
+ except EnvelopeError:
486
+ pass
487
+ errors.append(f"{target.name}: {exc}")
488
+ if published is None:
489
+ raise EnvelopeError(
490
+ "cannot publish durable stop intent in either predeclared slot: "
491
+ + "; ".join(errors)
492
+ )
493
+ try:
494
+ staging.unlink()
495
+ _fsync_directory(staging.parent)
496
+ except OSError:
497
+ # The marker is already durable. The exact alias path and bytes are
498
+ # bound inside the marker and validated on every replay.
499
+ pass
500
+ return published
501
+
502
+
503
+ def _fsync_directory(path: Path) -> None:
504
+ flags = os.O_RDONLY
505
+ if hasattr(os, "O_DIRECTORY"):
506
+ flags |= os.O_DIRECTORY
507
+ descriptor = os.open(path, flags)
508
+ try:
509
+ os.fsync(descriptor)
510
+ finally:
511
+ os.close(descriptor)
512
+
513
+
514
+ def _validate_run_tree(run_dir: Path) -> None:
515
+ if run_dir.is_symlink() or not run_dir.is_dir():
516
+ _fail(f"run directory must be a real non-symlink directory: {run_dir}")
517
+ try:
518
+ links = [path for path in run_dir.rglob("*") if path.is_symlink()]
519
+ except OSError as exc:
520
+ raise EnvelopeError(f"cannot inspect run directory {run_dir}: {exc}") from exc
521
+ if links:
522
+ _fail(f"run directory must not contain symlinks: {links[0]}")
523
+
524
+
525
+ def _replace_json(
526
+ path: Path,
527
+ value: dict[str, Any],
528
+ *,
529
+ staging_ref: str | None = None,
530
+ ) -> None:
531
+ raw = _json_bytes(value)
532
+ temporary = (
533
+ path.parent / staging_ref
534
+ if staging_ref is not None
535
+ else path.with_name(f".{path.name}.replace-{_sha(raw)[:24]}.json")
536
+ )
537
+ _ensure_exact_new(temporary, raw)
538
+ try:
539
+ os.replace(temporary, path)
540
+ _fsync_directory(path.parent)
541
+ except OSError as exc:
542
+ # Keep the complete content-addressed staging bytes. A stopped-state
543
+ # marker can replay this exact replacement; normal ingestion registers
544
+ # the surviving file as pre-stop evidence.
545
+ raise EnvelopeError(
546
+ f"cannot atomically replace {path}; complete staging preserved at "
547
+ f"{temporary}: {exc}"
548
+ ) from exc
549
+
550
+
551
+ def _relative_files_and_directories(run_dir: Path) -> tuple[set[str], set[str]]:
552
+ files: set[str] = set()
553
+ directories: set[str] = set()
554
+ try:
555
+ for path in run_dir.rglob("*"):
556
+ relative = str(path.relative_to(run_dir))
557
+ if path.is_symlink():
558
+ _fail(f"run directory must not contain symlinks: {path}")
559
+ if path.is_file():
560
+ files.add(relative)
561
+ elif path.is_dir():
562
+ directories.add(relative)
563
+ else:
564
+ _fail(f"run directory contains a non-file entry: {path}")
565
+ except OSError as exc:
566
+ raise EnvelopeError(f"cannot inventory run directory {run_dir}: {exc}") from exc
567
+ return files, directories
568
+
569
+
570
+ def _inventory_binding(
571
+ run_dir: Path,
572
+ file_refs: set[str],
573
+ directory_refs: set[str],
574
+ ) -> dict[str, Any]:
575
+ rows: list[dict[str, Any]] = []
576
+ for ref in sorted(directory_refs):
577
+ rows.append({"entry_type": "directory", "ref": ref})
578
+ for ref in sorted(file_refs):
579
+ digest, size = _hash_file_stable(run_dir / ref)
580
+ rows.append(
581
+ {
582
+ "entry_type": "file",
583
+ "ref": ref,
584
+ "sha256": digest,
585
+ "size_bytes": size,
586
+ }
587
+ )
588
+ return {"entry_count": len(rows), "sha256": _sha(_canonical(rows))}
589
+
590
+
591
+ def _parent_directories(refs: set[str]) -> set[str]:
592
+ directories: set[str] = set()
593
+ for ref in refs:
594
+ parent = Path(ref).parent
595
+ while str(parent) != ".":
596
+ directories.add(str(parent))
597
+ parent = parent.parent
598
+ return directories
599
+
600
+
601
+ def _journal_ref(state: str, cell_id: str) -> str:
602
+ return f"{ATTEMPT_JOURNAL_ROOT}/{state}/{cell_id}.json"
603
+
604
+
605
+ def _journal_value(plan_sha: str, cell: dict[str, Any]) -> dict[str, Any]:
606
+ return {
607
+ "schema_version": "ideation-diversity-attempt-guard/1.0",
608
+ "suite": SUITE,
609
+ "run_plan_sha256": plan_sha,
610
+ "cell_id": cell["cell_id"],
611
+ "sequence_index": cell["sequence_index"],
612
+ "purpose": "precommitted_single_ingestion_attempt_retry_guard",
613
+ }
614
+
615
+
616
+ def _journal_raw(plan_sha: str, cell: dict[str, Any]) -> bytes:
617
+ return _json_bytes(_journal_value(plan_sha, cell))
618
+
619
+
620
+ def _journal_observed_state(run_dir: Path, cell_id: str) -> str | None:
621
+ states = [
622
+ state
623
+ for state in ("ready", "active", "completed")
624
+ if (run_dir / _journal_ref(state, cell_id)).exists()
625
+ ]
626
+ if len(states) > 1:
627
+ _fail(f"terminal journal has multiple states for {cell_id}")
628
+ return states[0] if states else None
629
+
630
+
631
+ def _validate_terminal_journal(
632
+ run_dir: Path,
633
+ plan: dict[str, Any],
634
+ plan_sha: str,
635
+ manifest: dict[str, Any],
636
+ *,
637
+ durable_stop_present: bool,
638
+ ) -> None:
639
+ if manifest["status"] == "initialized":
640
+ if (run_dir / ATTEMPT_JOURNAL_ROOT).exists():
641
+ _fail("initialized run must not contain a terminal attempt journal")
642
+ return
643
+ ingested = manifest["next_sequence_index"] - 1
644
+ for index, cell in enumerate(plan["cells"]):
645
+ state = _journal_observed_state(run_dir, cell["cell_id"])
646
+ if state is None:
647
+ _fail(f"terminal journal guard is missing for {cell['cell_id']}")
648
+ raw = _read_file(run_dir / _journal_ref(state, cell["cell_id"]))
649
+ if raw != _journal_raw(plan_sha, cell):
650
+ _fail(f"terminal journal guard bytes drifted for {cell['cell_id']}")
651
+ if index < ingested:
652
+ if state not in {"active", "completed"}:
653
+ _fail(f"ingested cell terminal journal did not advance: {cell['cell_id']}")
654
+ elif manifest["stopped"] and index == ingested:
655
+ if state != "active":
656
+ _fail("blocked cell must retain its active retry guard")
657
+ elif index == ingested and state == "active" and not durable_stop_present:
658
+ _fail(
659
+ "active terminal journal permanently forbids retry after an "
660
+ "unrecoverable first-stop publication failure"
661
+ )
662
+ elif state != "ready":
663
+ _fail(f"future cell terminal journal advanced early: {cell['cell_id']}")
664
+
665
+
666
+ def _recover_completed_attempts(
667
+ run_dir: Path, plan: dict[str, Any], manifest: dict[str, Any]
668
+ ) -> None:
669
+ ingested = manifest["next_sequence_index"] - 1
670
+ for cell in plan["cells"][:ingested]:
671
+ active = run_dir / _journal_ref("active", cell["cell_id"])
672
+ completed = run_dir / _journal_ref("completed", cell["cell_id"])
673
+ if active.exists():
674
+ try:
675
+ os.rename(active, completed)
676
+ _fsync_directory(active.parent)
677
+ _fsync_directory(completed.parent)
678
+ except OSError as exc:
679
+ raise EnvelopeError(
680
+ f"cannot recover accepted terminal journal for {cell['cell_id']}: {exc}"
681
+ ) from exc
682
+
683
+
684
+ def _begin_ingestion_attempt(
685
+ run_dir: Path, plan: dict[str, Any], plan_sha: str, manifest: dict[str, Any]
686
+ ) -> None:
687
+ # Recover only already-accepted cells. No new external bytes are read until
688
+ # every prior journal transition is durable.
689
+ _recover_completed_attempts(run_dir, plan, manifest)
690
+ ingested = manifest["next_sequence_index"] - 1
691
+ cell = plan["cells"][ingested]
692
+ ready = run_dir / _journal_ref("ready", cell["cell_id"])
693
+ active = run_dir / _journal_ref("active", cell["cell_id"])
694
+ try:
695
+ os.rename(ready, active)
696
+ _fsync_directory(ready.parent)
697
+ _fsync_directory(active.parent)
698
+ except OSError as exc:
699
+ raise EnvelopeError(
700
+ "cannot durably activate the precommitted terminal attempt guard; "
701
+ "no transcript bytes were acquired"
702
+ ) from exc
703
+
704
+
705
+ def _complete_ingestion_attempt(run_dir: Path, cell_id: str) -> None:
706
+ active = run_dir / _journal_ref("active", cell_id)
707
+ completed = run_dir / _journal_ref("completed", cell_id)
708
+ try:
709
+ os.rename(active, completed)
710
+ _fsync_directory(active.parent)
711
+ _fsync_directory(completed.parent)
712
+ except OSError as exc:
713
+ raise EnvelopeError(
714
+ f"accepted state is durable but terminal journal recovery remains pending: {exc}"
715
+ ) from exc
716
+
717
+
718
+ def _schema(path: Path) -> dict[str, Any]:
719
+ schema = _strict_loads(_read_file(path))
720
+ try:
721
+ Draft202012Validator.check_schema(schema)
722
+ except Exception as exc:
723
+ raise EnvelopeError(f"invalid Draft 2020-12 schema {path}: {exc}") from exc
724
+ return schema
725
+
726
+
727
+ def _validate_schema(path: Path, value: dict[str, Any], label: str) -> None:
728
+ errors = sorted(
729
+ Draft202012Validator(
730
+ _schema(path), format_checker=FormatChecker()
731
+ ).iter_errors(value),
732
+ key=lambda error: list(error.absolute_path),
733
+ )
734
+ if errors:
735
+ first = errors[0]
736
+ location = "$" + "".join(f"[{part!r}]" for part in first.absolute_path)
737
+ _fail(f"{label} schema failure at {location}: {first.message}")
738
+
739
+
740
+ def _timestamp(value: str, label: str) -> datetime:
741
+ if not isinstance(value, str):
742
+ _fail(f"invalid {label} date-time")
743
+ try:
744
+ parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
745
+ except ValueError as exc:
746
+ raise EnvelopeError(f"invalid {label} date-time") from exc
747
+ if parsed.tzinfo is None:
748
+ _fail(f"{label} must include a UTC offset")
749
+ return parsed
750
+
751
+
752
+ def _repo_path(ref: str) -> Path:
753
+ path = Path(ref)
754
+ if path.is_absolute() or ".." in path.parts or not ref:
755
+ _fail(f"unsafe repository asset reference {ref!r}")
756
+ return REPO_ROOT / path
757
+
758
+
759
+ def _asset_bindings() -> list[dict[str, str]]:
760
+ return [
761
+ {"path": ref, "sha256": _sha(_read_file(_repo_path(ref)))}
762
+ for ref in ASSET_PATHS
763
+ ]
764
+
765
+
766
+ def _scenario_index(heldout: dict[str, Any]) -> dict[str, dict[str, Any]]:
767
+ return {row["scenario_id"]: row for row in heldout["scenarios"]}
768
+
769
+
770
+ def _actor_packet(cell_id: str, scenario: dict[str, Any], blind_fields: list[str]) -> dict[str, Any]:
771
+ return {
772
+ "schema_version": "ideation-diversity-actor-packet/1.0",
773
+ "suite": SUITE,
774
+ "actor_session_id": cell_id,
775
+ "content_class": "repository-owned synthetic scholar role card",
776
+ "blind_fields": blind_fields,
777
+ "role_card": {
778
+ key: scenario[key]
779
+ for key in (
780
+ "language",
781
+ "domain",
782
+ "role_brief",
783
+ "initial_message",
784
+ "owned_framings",
785
+ "response_policy",
786
+ "forbidden_out_of_role",
787
+ )
788
+ },
789
+ "model_generated_actor": False,
790
+ }
791
+
792
+
793
+ def _session_envelope(cell: dict[str, Any]) -> dict[str, Any]:
794
+ return {
795
+ "schema_version": "ideation-diversity-session-envelope/1.0",
796
+ "suite": SUITE,
797
+ "cell_id": cell["cell_id"],
798
+ "sequence_index": cell["sequence_index"],
799
+ "prompt_path": cell["prompt"]["path"],
800
+ "prompt_sha256": cell["prompt"]["sha256"],
801
+ "environment": cell["prompt"]["environment"],
802
+ "actor_packet_path": cell["actor_packet_path"],
803
+ "actor_packet_sha256": cell["actor_packet_sha256"],
804
+ "fresh_context_required": True,
805
+ "tools": [],
806
+ "web_enabled": False,
807
+ "runner_transport": "none",
808
+ "dispatch_available": False,
809
+ "api_spend_ceiling_usd": 0,
810
+ "api_fallback": False,
811
+ "envelope_grants_consent": False,
812
+ "fresh_external_authorization_required": True,
813
+ }
814
+
815
+
816
+ def _prompt_binding(arm_id: str, production_sha: str, variant_sha: str) -> dict[str, Any]:
817
+ if arm_id == "adjacent_probe_on":
818
+ return {
819
+ "kind": "production",
820
+ "path": "materials/prompts/production-socratic-mentor.md",
821
+ "sha256": production_sha,
822
+ "environment": {"ARS_SOCRATIC_ADJACENT_PROBE": "1"},
823
+ }
824
+ if arm_id == "exploratory_guardrails_ablated":
825
+ return {
826
+ "kind": "nonproduction_ablation",
827
+ "path": "materials/prompts/exploratory-guardrails-ablated.md",
828
+ "sha256": variant_sha,
829
+ "environment": {},
830
+ }
831
+ return {
832
+ "kind": "production",
833
+ "path": "materials/prompts/production-socratic-mentor.md",
834
+ "sha256": production_sha,
835
+ "environment": {},
836
+ }
837
+
838
+
839
+ def _ordered_assignments(seed: str, scenarios: list[dict[str, Any]]) -> list[tuple[str, str, dict[str, Any], int, int]]:
840
+ blocks: list[tuple[str, tuple[str, str], dict[str, Any]]] = []
841
+ for experiment_id, arms in EXPERIMENTS:
842
+ for scenario in scenarios:
843
+ blocks.append((experiment_id, arms, scenario))
844
+ blocks.sort(
845
+ key=lambda block: _sha(
846
+ f"{seed}\0{block[0]}\0{block[2]['scenario_id']}".encode("utf-8")
847
+ )
848
+ )
849
+ assignments: list[tuple[str, str, dict[str, Any], int, int]] = []
850
+ for block_index, (experiment_id, arms, scenario) in enumerate(blocks, 1):
851
+ orientation = int(
852
+ _sha(f"{seed}\0orientation\0{experiment_id}\0{scenario['scenario_id']}".encode()),
853
+ 16,
854
+ ) % 2
855
+ first = arms if orientation == 0 else tuple(reversed(arms))
856
+ for replicate, arm_order in ((1, first), (2, tuple(reversed(first)))):
857
+ for arm_id in arm_order:
858
+ assignments.append(
859
+ (experiment_id, arm_id, scenario, replicate, block_index)
860
+ )
861
+ return assignments
862
+
863
+
864
+ def _build_plan(config: dict[str, Any]) -> dict[str, Any]:
865
+ heldout, variant = phase1.load_assets()
866
+ production_raw = _read_file(PRODUCTION_PROMPT)
867
+ rendered_raw = phase1.render_variant(variant).encode("utf-8")
868
+ production_sha = _sha(production_raw)
869
+ variant_sha = _sha(rendered_raw)
870
+ cells: list[dict[str, Any]] = []
871
+ for sequence_index, assignment in enumerate(
872
+ _ordered_assignments(config["order_seed"], heldout["scenarios"]), 1
873
+ ):
874
+ experiment_id, arm_id, scenario, replicate, block_index = assignment
875
+ cell_id = f"cell-{sequence_index:03d}"
876
+ scenario_sha = _sha(_canonical(scenario))
877
+ actor_packet_path = f"materials/actor-packets/{cell_id}.json"
878
+ actor_packet = _actor_packet(
879
+ cell_id, scenario, heldout["actor_blind_fields"]
880
+ )
881
+ cell: dict[str, Any] = {
882
+ "cell_id": cell_id,
883
+ "sequence_index": sequence_index,
884
+ "experiment_id": experiment_id,
885
+ "arm_id": arm_id,
886
+ "scenario_id": scenario["scenario_id"],
887
+ "pair_id": scenario["pair_id"],
888
+ "language": scenario["language"],
889
+ "replicate": replicate,
890
+ "actor_block_id": f"actor-block-{block_index:02d}-r{replicate}",
891
+ "prompt": _prompt_binding(arm_id, production_sha, variant_sha),
892
+ "scenario_sha256": scenario_sha,
893
+ "initial_message_sha256": _sha(scenario["initial_message"].encode("utf-8")),
894
+ "actor_packet_path": actor_packet_path,
895
+ "actor_packet_sha256": _sha(_json_bytes(actor_packet)),
896
+ "session_envelope_path": f"materials/session-envelopes/{cell_id}.json",
897
+ "session_envelope_sha256": "0" * 64,
898
+ "fresh_context_required": True,
899
+ }
900
+ cell["session_envelope_sha256"] = _sha(_json_bytes(_session_envelope(cell)))
901
+ cells.append(cell)
902
+ cell_ids = [cell["cell_id"] for cell in cells]
903
+ order_projection = [
904
+ {
905
+ "cell_id": cell["cell_id"],
906
+ "sequence_index": cell["sequence_index"],
907
+ "experiment_id": cell["experiment_id"],
908
+ "scenario_id": cell["scenario_id"],
909
+ "arm_id": cell["arm_id"],
910
+ "replicate": cell["replicate"],
911
+ "actor_block_id": cell["actor_block_id"],
912
+ }
913
+ for cell in cells
914
+ ]
915
+ return {
916
+ "schema_version": "ideation-diversity-run-plan/1.0",
917
+ "suite": SUITE,
918
+ "phase": "phase_2_no_call_execution_envelope",
919
+ "status": "frozen_no_call",
920
+ "run_id": config["run_id"],
921
+ "suite_commit": config["suite_commit"],
922
+ "suite_commit_provenance": {
923
+ "status": "operator_declared_unverified",
924
+ "verified_by_no_call_runner": False,
925
+ "verifiable_authority": "run_plan_sha256_and_asset_bindings",
926
+ },
927
+ "content_class": "repository-owned synthetic scholar role cards",
928
+ "created_without_dispatch": True,
929
+ "asset_bindings": _asset_bindings(),
930
+ "rendered_variant_sha256": variant_sha,
931
+ "execution": {
932
+ "subject_provider": config["subject_provider"],
933
+ "subject_model": config["subject_model"],
934
+ "subject_runtime": config["subject_runtime"],
935
+ "subject_runtime_version": config["subject_runtime_version"],
936
+ "auth_mode": config["auth_mode"],
937
+ "reasoning_effort": config["reasoning_effort"],
938
+ "input_token_cap": config["input_token_cap"],
939
+ "output_token_cap": config["output_token_cap"],
940
+ "token_cap_verification": {
941
+ "status": "operator_declared_unverified",
942
+ "enforced_by_no_call_runner": False,
943
+ "observed_usage_recorded": False,
944
+ "provider_tokenizer_verified": False,
945
+ },
946
+ "tools": [],
947
+ "web_enabled": False,
948
+ "runner_transport": "none",
949
+ "dispatch_available": False,
950
+ "api_spend_ceiling_usd": 0,
951
+ "api_fallback": False,
952
+ "envelope_grants_consent": False,
953
+ "fresh_external_authorization_required": True,
954
+ },
955
+ "design": {
956
+ "experiments": 2,
957
+ "scenarios": 6,
958
+ "arms_per_experiment": 2,
959
+ "replicates_per_scenario_arm": 2,
960
+ "subject_session_cells": 48,
961
+ "fresh_context_per_cell": True,
962
+ "adaptive_actor_forbidden": True,
963
+ "model_generated_actor_forbidden": True,
964
+ },
965
+ "order": {
966
+ "algorithm": "sha256-balanced-block-v1",
967
+ "seed": config["order_seed"],
968
+ "block_count": 12,
969
+ "cell_ids": cell_ids,
970
+ "order_sha256": _sha(_canonical(order_projection)),
971
+ },
972
+ "cells": cells,
973
+ "stop_rules": STOP_RULES,
974
+ "judge_requirements": {
975
+ "minimum_independent_judges": 2,
976
+ "separate_arm_blind_adjudicator_required": True,
977
+ "raw_labels_must_be_retained": True,
978
+ "human_evidence_may_not_be_fabricated": True,
979
+ "packet_contains_no_labels": True,
980
+ "first_round_assignment_ledger_required_before_delivery": True,
981
+ "same_role_card_cross_arm_or_replicate_exposure_forbidden": True,
982
+ "bundle_alone_proves_judge_exposure_blindness": False,
983
+ },
984
+ }
985
+
986
+
987
+ def _config_from_plan(plan: dict[str, Any]) -> dict[str, Any]:
988
+ execution = plan["execution"]
989
+ return {
990
+ "run_id": plan["run_id"],
991
+ "suite_commit": plan["suite_commit"],
992
+ "order_seed": plan["order"]["seed"],
993
+ "subject_provider": execution["subject_provider"],
994
+ "subject_model": execution["subject_model"],
995
+ "subject_runtime": execution["subject_runtime"],
996
+ "subject_runtime_version": execution["subject_runtime_version"],
997
+ "auth_mode": execution["auth_mode"],
998
+ "reasoning_effort": execution["reasoning_effort"],
999
+ "input_token_cap": execution["input_token_cap"],
1000
+ "output_token_cap": execution["output_token_cap"],
1001
+ }
1002
+
1003
+
1004
+ def _validate_execution_text(config: dict[str, Any]) -> None:
1005
+ for key in (
1006
+ "subject_provider",
1007
+ "subject_model",
1008
+ "subject_runtime",
1009
+ "subject_runtime_version",
1010
+ "auth_mode",
1011
+ "reasoning_effort",
1012
+ ):
1013
+ _require_identity_text(config[key], f"execution {key}")
1014
+
1015
+
1016
+ def _validate_authorization_record(
1017
+ record: dict[str, Any],
1018
+ plan: dict[str, Any],
1019
+ plan_sha: str,
1020
+ ) -> None:
1021
+ _validate_schema(AUTHORIZATION_SCHEMA, record, "authorization record")
1022
+ expected = {
1023
+ "run_plan_sha256": plan_sha,
1024
+ "run_id": plan["run_id"],
1025
+ "suite_commit": plan["suite_commit"],
1026
+ "suite_commit_provenance": plan["suite_commit_provenance"],
1027
+ "execution": plan["execution"],
1028
+ }
1029
+ for key, value in expected.items():
1030
+ if record[key] != value:
1031
+ _fail(f"authorization record {key} differs from frozen run plan")
1032
+ scope = record["scope"]
1033
+ if scope["cell_ids"] != plan["order"]["cell_ids"]:
1034
+ _fail("authorization record is not scoped to the exact ordered 48 cells")
1035
+ if scope["order_sha256"] != plan["order"]["order_sha256"]:
1036
+ _fail("authorization record order hash differs from frozen run plan")
1037
+ if record["decision"]["status"] != "authorized":
1038
+ _fail("authorization record decision is not authorized")
1039
+ _require_identity_text(
1040
+ record["decision"]["operator_reference"],
1041
+ "authorization operator_reference",
1042
+ )
1043
+ _require_identity_text(
1044
+ record["decision"]["statement"],
1045
+ "authorization statement",
1046
+ )
1047
+ _timestamp(record["decision"]["decided_at"], "authorization decided_at")
1048
+
1049
+
1050
+ def _validate_plan(plan: dict[str, Any]) -> None:
1051
+ _validate_schema(RUN_PLAN_SCHEMA, plan, "run plan")
1052
+ config = _config_from_plan(plan)
1053
+ _validate_execution_text(config)
1054
+ expected = _build_plan(config)
1055
+ if _canonical(plan) != _canonical(expected):
1056
+ _fail("run plan differs from the exact current asset/order/hash expansion")
1057
+ cells = plan["cells"]
1058
+ if len({cell["cell_id"] for cell in cells}) != 48:
1059
+ _fail("run plan cell ids must be unique")
1060
+ observed = {
1061
+ (
1062
+ cell["experiment_id"],
1063
+ cell["scenario_id"],
1064
+ cell["arm_id"],
1065
+ cell["replicate"],
1066
+ )
1067
+ for cell in cells
1068
+ }
1069
+ expected_cross_product = {
1070
+ (experiment, scenario["scenario_id"], arm, replicate)
1071
+ for experiment, arms in EXPERIMENTS
1072
+ for scenario in phase1.load_assets()[0]["scenarios"]
1073
+ for arm in arms
1074
+ for replicate in (1, 2)
1075
+ }
1076
+ if observed != expected_cross_product:
1077
+ _fail("run plan is not the exact 2 x 6 x 2 x 2 cross-product")
1078
+ for experiment, arms in EXPERIMENTS:
1079
+ for scenario in phase1.load_assets()[0]["scenarios"]:
1080
+ rows = [
1081
+ cell
1082
+ for cell in cells
1083
+ if cell["experiment_id"] == experiment
1084
+ and cell["scenario_id"] == scenario["scenario_id"]
1085
+ ]
1086
+ orders = [
1087
+ [cell["arm_id"] for cell in rows if cell["replicate"] == replicate]
1088
+ for replicate in (1, 2)
1089
+ ]
1090
+ if len(orders[0]) != 2 or orders[1] != list(reversed(orders[0])):
1091
+ _fail(f"arm order is not counterbalanced for {experiment}/{scenario['scenario_id']}")
1092
+
1093
+
1094
+ def _initial_manifest(plan: dict[str, Any], plan_sha: str) -> dict[str, Any]:
1095
+ return {
1096
+ "schema_version": "ideation-diversity-ingestion-manifest/1.0",
1097
+ "suite": SUITE,
1098
+ "run_plan_sha256": plan_sha,
1099
+ "authorization_record_path": None,
1100
+ "authorization_record_sha256": None,
1101
+ "blind_manifest_ref": None,
1102
+ "blind_manifest_sha256": None,
1103
+ "status": "initialized",
1104
+ "next_sequence_index": 1,
1105
+ "stopped": False,
1106
+ "stop_receipt": None,
1107
+ "cells": [
1108
+ {
1109
+ "cell_id": cell["cell_id"],
1110
+ "sequence_index": cell["sequence_index"],
1111
+ "status": "pending",
1112
+ "transcript_ref": None,
1113
+ "transcript_sha256": None,
1114
+ "ingestion_receipt_ref": None,
1115
+ }
1116
+ for cell in plan["cells"]
1117
+ ],
1118
+ }
1119
+
1120
+
1121
+ def _validate_manifest(manifest: dict[str, Any], plan: dict[str, Any], plan_sha: str) -> None:
1122
+ _validate_schema(INGESTION_SCHEMA, manifest, "ingestion manifest")
1123
+ if manifest["run_plan_sha256"] != plan_sha:
1124
+ _fail("ingestion manifest is bound to a different run plan")
1125
+ authorization_bound = (
1126
+ manifest["authorization_record_path"] is not None,
1127
+ manifest["authorization_record_sha256"] is not None,
1128
+ )
1129
+ if authorization_bound not in {(False, False), (True, True)}:
1130
+ _fail("authorization-record manifest fields must be both null or both bound")
1131
+ blind_bound = (
1132
+ manifest["blind_manifest_ref"] is not None,
1133
+ manifest["blind_manifest_sha256"] is not None,
1134
+ )
1135
+ if blind_bound not in {(False, False), (True, True)}:
1136
+ _fail("blind-manifest fields must be both null or both bound")
1137
+ pairs = [(row["cell_id"], row["sequence_index"]) for row in manifest["cells"]]
1138
+ expected = [(row["cell_id"], row["sequence_index"]) for row in plan["cells"]]
1139
+ if pairs != expected:
1140
+ _fail("ingestion manifest cell order differs from run plan")
1141
+ rows = manifest["cells"]
1142
+ ingested = sum(row["status"] == "ingested" for row in rows)
1143
+ if ingested and authorization_bound != (True, True):
1144
+ _fail("ingested transcripts require a preserved authorization record")
1145
+ if manifest["next_sequence_index"] != ingested + 1:
1146
+ _fail("ingestion manifest next_sequence_index is inconsistent")
1147
+ if manifest["stopped"] != (manifest["status"] == "stopped"):
1148
+ _fail("ingestion stopped/status fields disagree")
1149
+ for index, row in enumerate(rows):
1150
+ if index < ingested:
1151
+ expected_status = "ingested"
1152
+ elif manifest["stopped"] and index == ingested:
1153
+ expected_status = "blocked"
1154
+ else:
1155
+ expected_status = "pending"
1156
+ if row["status"] != expected_status:
1157
+ _fail("ingestion manifest states must form an exact append-only prefix")
1158
+ populated = (
1159
+ row["transcript_ref"] is not None,
1160
+ row["transcript_sha256"] is not None,
1161
+ row["ingestion_receipt_ref"] is not None,
1162
+ )
1163
+ expected_populated = {
1164
+ "pending": (False, False, False),
1165
+ "ingested": (True, True, True),
1166
+ "blocked": (True, True, False),
1167
+ }[row["status"]]
1168
+ if populated != expected_populated:
1169
+ _fail(f"ingestion manifest references disagree for {row['cell_id']}")
1170
+ if row["status"] == "ingested" and (
1171
+ row["transcript_ref"], row["ingestion_receipt_ref"]
1172
+ ) != (
1173
+ f"transcripts/{row['cell_id']}.json",
1174
+ f"receipts/{row['cell_id']}.json",
1175
+ ):
1176
+ _fail(f"ingested artifact paths drifted for {row['cell_id']}")
1177
+ if row["status"] == "blocked":
1178
+ base_ref = (
1179
+ f"blocked/{row['cell_id']}.transcript."
1180
+ f"{row['transcript_sha256']}.raw"
1181
+ )
1182
+ if row["transcript_ref"] != base_ref and not row[
1183
+ "transcript_ref"
1184
+ ].startswith(base_ref + ".collision-"):
1185
+ _fail(f"blocked evidence path drifted for {row['cell_id']}")
1186
+ state = manifest["status"]
1187
+ if state in {"initialized", "materialized"} and ingested != 0:
1188
+ _fail(f"{state} manifest cannot contain ingested cells")
1189
+ if state == "ingesting" and not 1 <= ingested <= 47:
1190
+ _fail("ingesting manifest must contain 1 through 47 ingested cells")
1191
+ if state in {"complete", "blind_finalized"} and ingested != 48:
1192
+ _fail(f"{state} manifest must contain 48 ingested cells")
1193
+ if state == "stopped":
1194
+ if ingested >= 48:
1195
+ _fail("stopped manifest requires one remaining blocked cell")
1196
+ if manifest["stop_receipt"] is None:
1197
+ _fail("stopped manifest requires one stop receipt")
1198
+ blocked = rows[ingested]
1199
+ receipt = manifest["stop_receipt"]
1200
+ if (
1201
+ receipt["cell_id"],
1202
+ receipt["sequence_index"],
1203
+ receipt["raw_ref"],
1204
+ receipt["raw_sha256"],
1205
+ ) != (
1206
+ blocked["cell_id"],
1207
+ blocked["sequence_index"],
1208
+ blocked["transcript_ref"],
1209
+ blocked["transcript_sha256"],
1210
+ ):
1211
+ _fail("stop receipt is not bound to the single blocked cell")
1212
+ if receipt["stop_intent_ref"] != STOP_INTENT_REF:
1213
+ _fail("stopped manifest is not bound to the durable stop intent")
1214
+ elif manifest["stop_receipt"] is not None:
1215
+ _fail("unstopped manifest must not contain a stop receipt")
1216
+ if state == "blind_finalized":
1217
+ if blind_bound != (True, True):
1218
+ _fail("blind_finalized manifest requires a bound blind manifest")
1219
+ if manifest["blind_manifest_ref"] != "blind/manifest.json":
1220
+ _fail("blind manifest path drifted")
1221
+ elif blind_bound != (False, False):
1222
+ _fail("only blind_finalized state may bind blind artifacts")
1223
+
1224
+
1225
+ def _expected_run_inventory(
1226
+ run_dir: Path, plan: dict[str, Any], manifest: dict[str, Any]
1227
+ ) -> tuple[set[str], set[str]]:
1228
+ files = {"run-plan.json", "ingestion-manifest.json"}
1229
+ journal_directories: set[str] = set()
1230
+ if manifest["status"] != "initialized":
1231
+ files.update(_material_map(plan))
1232
+ journal_directories = {
1233
+ ATTEMPT_JOURNAL_ROOT,
1234
+ f"{ATTEMPT_JOURNAL_ROOT}/ready",
1235
+ f"{ATTEMPT_JOURNAL_ROOT}/active",
1236
+ f"{ATTEMPT_JOURNAL_ROOT}/completed",
1237
+ }
1238
+ for cell in plan["cells"]:
1239
+ state = _journal_observed_state(run_dir, cell["cell_id"])
1240
+ if state is not None:
1241
+ files.add(_journal_ref(state, cell["cell_id"]))
1242
+ if manifest["authorization_record_path"] is not None:
1243
+ files.add(manifest["authorization_record_path"])
1244
+ for row in manifest["cells"]:
1245
+ if row["transcript_ref"] is not None:
1246
+ files.add(row["transcript_ref"])
1247
+ if row["ingestion_receipt_ref"] is not None:
1248
+ files.add(row["ingestion_receipt_ref"])
1249
+ if manifest["stop_receipt"] is not None:
1250
+ for ref in (STOP_INTENT_REF, STOP_INTENT_FALLBACK_REF):
1251
+ if (run_dir / ref).exists():
1252
+ files.add(ref)
1253
+ files.update(
1254
+ row["ref"]
1255
+ for row in manifest["stop_receipt"]["preserved_auxiliary_artifacts"]
1256
+ )
1257
+ receipt = manifest["stop_receipt"]
1258
+ for ref in (
1259
+ receipt["stop_intent_publication_staging_ref"],
1260
+ receipt["manifest_replacement_staging_ref"],
1261
+ ):
1262
+ if (run_dir / ref).exists():
1263
+ files.add(ref)
1264
+ if (run_dir / BLIND_INTENT_REF).exists():
1265
+ files.add(BLIND_INTENT_REF)
1266
+ alias_root = run_dir / BLIND_ALIAS_ROOT
1267
+ alias_directories = {BLIND_ALIAS_ROOT} if alias_root.exists() else set()
1268
+ if alias_root.exists():
1269
+ observed_alias_files, observed_alias_directories = (
1270
+ _relative_files_and_directories(alias_root)
1271
+ )
1272
+ files.update(f"{BLIND_ALIAS_ROOT}/{ref}" for ref in observed_alias_files)
1273
+ alias_directories.update(
1274
+ f"{BLIND_ALIAS_ROOT}/{ref}" for ref in observed_alias_directories
1275
+ )
1276
+ staging_root = run_dir / BLIND_STAGING_REF
1277
+ if staging_root.exists():
1278
+ observed_staging_files, observed_staging_directories = (
1279
+ _relative_files_and_directories(staging_root)
1280
+ )
1281
+ files.update(
1282
+ f"{BLIND_STAGING_REF}/{ref}" for ref in observed_staging_files
1283
+ )
1284
+ staging_directories = {
1285
+ BLIND_STAGING_REF,
1286
+ *(
1287
+ f"{BLIND_STAGING_REF}/{ref}"
1288
+ for ref in observed_staging_directories
1289
+ ),
1290
+ }
1291
+ else:
1292
+ staging_directories = set()
1293
+ else:
1294
+ alias_directories = set()
1295
+ staging_directories = set()
1296
+ if manifest["status"] == "blind_finalized":
1297
+ blind_manifest_raw = _read_file(run_dir / "blind/manifest.json")
1298
+ blind_manifest = _strict_loads(blind_manifest_raw)
1299
+ files.update(
1300
+ {
1301
+ "blind/manifest.json",
1302
+ blind_manifest["inventory_ref"],
1303
+ blind_manifest["private_map_ref"],
1304
+ }
1305
+ )
1306
+ files.update(row["packet_ref"] for row in blind_manifest["packets"])
1307
+ return (
1308
+ files,
1309
+ _parent_directories(files)
1310
+ | staging_directories
1311
+ | alias_directories
1312
+ | journal_directories,
1313
+ )
1314
+
1315
+
1316
+ def _validate_run_inventory(
1317
+ run_dir: Path,
1318
+ plan: dict[str, Any],
1319
+ manifest: dict[str, Any],
1320
+ ) -> None:
1321
+ expected_files, expected_directories = _expected_run_inventory(
1322
+ run_dir, plan, manifest
1323
+ )
1324
+ observed_files, observed_directories = _relative_files_and_directories(run_dir)
1325
+ missing_files = observed_files ^ expected_files
1326
+ missing_directories = observed_directories ^ expected_directories
1327
+ if manifest["status"] == "stopped":
1328
+ missing_files = expected_files - observed_files
1329
+ missing_directories = expected_directories - observed_directories
1330
+ extra_files = observed_files - expected_files
1331
+ extra_directories = observed_directories - expected_directories
1332
+ observed_binding = _inventory_binding(
1333
+ run_dir, extra_files, extra_directories
1334
+ )
1335
+ if observed_binding != manifest["stop_receipt"]["pre_stop_inventory"]:
1336
+ _fail("pre-stop compact inventory digest/count drifted")
1337
+ if missing_files:
1338
+ unexpected = sorted(observed_files - expected_files)
1339
+ missing = sorted(expected_files - observed_files)
1340
+ _fail(
1341
+ "run file inventory drifted; "
1342
+ f"unexpected={unexpected[:3]!r}, missing={missing[:3]!r}"
1343
+ )
1344
+ if missing_directories:
1345
+ unexpected = sorted(observed_directories - expected_directories)
1346
+ missing = sorted(expected_directories - observed_directories)
1347
+ _fail(
1348
+ "run directory inventory drifted; "
1349
+ f"unexpected={unexpected[:3]!r}, missing={missing[:3]!r}"
1350
+ )
1351
+
1352
+
1353
+ def _decode_base64(value: str, label: str) -> bytes:
1354
+ try:
1355
+ decoded = base64.b64decode(value.encode("ascii"), validate=True)
1356
+ except (UnicodeError, ValueError) as exc:
1357
+ raise EnvelopeError(f"invalid {label} base64") from exc
1358
+ if base64.b64encode(decoded).decode("ascii") != value:
1359
+ _fail(f"non-canonical {label} base64")
1360
+ return decoded
1361
+
1362
+
1363
+ def _validate_stop_intent(
1364
+ raw: bytes,
1365
+ plan: dict[str, Any],
1366
+ plan_sha: str,
1367
+ ) -> tuple[dict[str, Any], dict[str, Any], bytes, bytes]:
1368
+ marker = _strict_loads(raw)
1369
+ if raw != _json_bytes(marker):
1370
+ _fail("stop intent is not canonical write-once replay bytes")
1371
+ _validate_schema(STOP_INTENT_SCHEMA, marker, "stop intent")
1372
+ if marker["run_plan_sha256"] != plan_sha:
1373
+ _fail("stop intent is bound to a different run plan")
1374
+ stopped_raw = _decode_base64(
1375
+ marker["stopped_manifest_base64"], "stopped manifest"
1376
+ )
1377
+ if _sha(stopped_raw) != marker["stopped_manifest_sha256"]:
1378
+ _fail("stop intent stopped-manifest hash drift")
1379
+ stopped = _strict_loads(stopped_raw)
1380
+ if stopped_raw != _json_bytes(stopped):
1381
+ _fail("stop intent stopped manifest is not canonical replay bytes")
1382
+ _validate_manifest(stopped, plan, plan_sha)
1383
+ raw_evidence = _decode_base64(marker["raw_base64"], "blocked raw evidence")
1384
+ if _sha(raw_evidence) != marker["raw_sha256"]:
1385
+ _fail("stop intent blocked-raw hash drift")
1386
+ receipt = stopped["stop_receipt"]
1387
+ bound = (
1388
+ marker["cell_id"],
1389
+ marker["sequence_index"],
1390
+ marker["reason_code"],
1391
+ marker["detail"],
1392
+ marker["raw_ref"],
1393
+ marker["raw_sha256"],
1394
+ marker["raw_capture_kind"],
1395
+ marker["pre_stop_inventory"],
1396
+ marker["stop_intent_publication_staging_ref"],
1397
+ marker["manifest_replacement_staging_ref"],
1398
+ )
1399
+ replayed = (
1400
+ receipt["cell_id"],
1401
+ receipt["sequence_index"],
1402
+ receipt["reason_code"],
1403
+ receipt["detail"],
1404
+ receipt["raw_ref"],
1405
+ receipt["raw_sha256"],
1406
+ receipt["raw_capture_kind"],
1407
+ receipt["pre_stop_inventory"],
1408
+ receipt["stop_intent_publication_staging_ref"],
1409
+ receipt["manifest_replacement_staging_ref"],
1410
+ )
1411
+ if bound != replayed:
1412
+ _fail("stop intent fields drifted from the stopped-manifest replay")
1413
+ return marker, stopped, stopped_raw, raw_evidence
1414
+
1415
+
1416
+ def _ensure_exact_new(path: Path, raw: bytes, *, mode: int = 0o644) -> None:
1417
+ if path.exists():
1418
+ if _read_file(path, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
1419
+ _fail(f"write-once evidence collision at {path}")
1420
+ return
1421
+ _write_new(path, raw, mode=mode)
1422
+
1423
+
1424
+ def _recover_stop_intent(
1425
+ run_dir: Path,
1426
+ plan: dict[str, Any],
1427
+ plan_sha: str,
1428
+ current_manifest_raw: bytes,
1429
+ marker_path: Path,
1430
+ ) -> tuple[dict[str, Any], bytes]:
1431
+ marker_raw = _read_file(
1432
+ marker_path, limit=MAX_STOP_INTENT_BYTES
1433
+ )
1434
+ marker, stopped, stopped_raw, raw_evidence = _validate_stop_intent(
1435
+ marker_raw, plan, plan_sha
1436
+ )
1437
+ publication_alias = run_dir / marker["stop_intent_publication_staging_ref"]
1438
+ if publication_alias.exists():
1439
+ if _read_file(publication_alias, limit=MAX_STOP_INTENT_BYTES) != marker_raw:
1440
+ _fail("registered stop-intent publication alias bytes drifted")
1441
+ if publication_alias.stat().st_ino != marker_path.stat().st_ino:
1442
+ _fail("registered stop-intent publication alias is not the marker hardlink")
1443
+ replacement_staging = run_dir / marker["manifest_replacement_staging_ref"]
1444
+ if replacement_staging.exists() and _read_file(
1445
+ replacement_staging, limit=max(MAX_INPUT_BYTES, len(stopped_raw))
1446
+ ) != stopped_raw:
1447
+ _fail("registered stopped-manifest replacement staging bytes drifted")
1448
+ current_sha = _sha(current_manifest_raw)
1449
+ if current_manifest_raw == stopped_raw:
1450
+ pass
1451
+ elif current_sha == marker["prior_manifest_sha256"]:
1452
+ try:
1453
+ _ensure_exact_new(run_dir / marker["raw_ref"], raw_evidence)
1454
+ _replace_json(
1455
+ run_dir / "ingestion-manifest.json",
1456
+ stopped,
1457
+ staging_ref=marker["manifest_replacement_staging_ref"],
1458
+ )
1459
+ except EnvelopeError as exc:
1460
+ raise EnvelopeError(
1461
+ "durable stop intent forbids retry; stopped-state recovery is "
1462
+ f"still pending: {exc}"
1463
+ ) from exc
1464
+ current_manifest_raw = _read_file(run_dir / "ingestion-manifest.json")
1465
+ if current_manifest_raw != stopped_raw:
1466
+ _fail("durable stop recovery did not publish exact stopped state")
1467
+ else:
1468
+ _fail("manifest drifted from both pre-stop and durable stopped state")
1469
+ try:
1470
+ _ensure_exact_new(run_dir / marker["raw_ref"], raw_evidence)
1471
+ except EnvelopeError as exc:
1472
+ raise EnvelopeError(
1473
+ "durable stop intent forbids retry; blocked transcript hash drift or "
1474
+ f"evidence recovery failure: {exc}"
1475
+ ) from exc
1476
+ return stopped, stopped_raw
1477
+
1478
+
1479
+ def _load_run(run_dir: Path, expected_sha: str | None = None) -> tuple[dict[str, Any], bytes, str, dict[str, Any]]:
1480
+ _validate_run_tree(run_dir)
1481
+ plan_raw = _read_file(run_dir / "run-plan.json")
1482
+ plan_sha = _sha(plan_raw)
1483
+ if expected_sha is not None and expected_sha != plan_sha:
1484
+ _fail("--plan-sha256 does not match frozen run-plan bytes")
1485
+ plan = _strict_loads(plan_raw)
1486
+ _validate_plan(plan)
1487
+ manifest_raw = _read_file(run_dir / "ingestion-manifest.json")
1488
+ marker_paths = [
1489
+ run_dir / ref
1490
+ for ref in (STOP_INTENT_REF, STOP_INTENT_FALLBACK_REF)
1491
+ if (run_dir / ref).exists()
1492
+ ]
1493
+ if len(marker_paths) > 1:
1494
+ _fail("multiple durable stop-intent publication slots are present")
1495
+ if marker_paths:
1496
+ manifest, manifest_raw = _recover_stop_intent(
1497
+ run_dir, plan, plan_sha, manifest_raw, marker_paths[0]
1498
+ )
1499
+ else:
1500
+ manifest = _strict_loads(manifest_raw)
1501
+ _validate_manifest(manifest, plan, plan_sha)
1502
+ if manifest["stopped"] and not marker_paths:
1503
+ _fail("stopped state is missing its durable write-once stop intent")
1504
+ _validate_terminal_journal(
1505
+ run_dir,
1506
+ plan,
1507
+ plan_sha,
1508
+ manifest,
1509
+ durable_stop_present=bool(marker_paths),
1510
+ )
1511
+ return plan, plan_raw, plan_sha, manifest
1512
+
1513
+
1514
+ def init_run(args: argparse.Namespace) -> dict[str, Any]:
1515
+ if args.run_dir.exists():
1516
+ _fail(f"run directory already exists: {args.run_dir}")
1517
+ config = {
1518
+ "run_id": args.run_id,
1519
+ "suite_commit": args.suite_commit,
1520
+ "order_seed": args.order_seed,
1521
+ "subject_provider": args.subject_provider,
1522
+ "subject_model": args.subject_model,
1523
+ "subject_runtime": args.subject_runtime,
1524
+ "subject_runtime_version": args.subject_runtime_version,
1525
+ "auth_mode": args.auth_mode,
1526
+ "reasoning_effort": args.reasoning_effort,
1527
+ "input_token_cap": args.input_token_cap,
1528
+ "output_token_cap": args.output_token_cap,
1529
+ }
1530
+ if RUN_ID_RE.fullmatch(args.run_id) is None:
1531
+ _fail("--run-id has invalid syntax")
1532
+ if COMMIT_RE.fullmatch(args.suite_commit) is None:
1533
+ _fail("--suite-commit must be 40 lowercase hex characters")
1534
+ _validate_execution_text(config)
1535
+ plan = _build_plan(config)
1536
+ _validate_plan(plan)
1537
+ plan_raw = _json_bytes(plan)
1538
+ plan_sha = _sha(plan_raw)
1539
+ args.run_dir.mkdir(parents=True)
1540
+ _validate_run_tree(args.run_dir)
1541
+ _write_new(args.run_dir / "run-plan.json", plan_raw)
1542
+ manifest = _initial_manifest(plan, plan_sha)
1543
+ _validate_manifest(manifest, plan, plan_sha)
1544
+ _write_new(args.run_dir / "ingestion-manifest.json", _json_bytes(manifest))
1545
+ return {"run_plan_sha256": plan_sha, "cells": 48, "dispatch_available": False}
1546
+
1547
+
1548
+ def _material_map(plan: dict[str, Any]) -> dict[str, bytes]:
1549
+ heldout, variant = phase1.load_assets()
1550
+ scenarios = _scenario_index(heldout)
1551
+ values: dict[str, bytes] = {
1552
+ "materials/prompts/production-socratic-mentor.md": _read_file(PRODUCTION_PROMPT),
1553
+ "materials/prompts/exploratory-guardrails-ablated.md": phase1.render_variant(variant).encode("utf-8"),
1554
+ }
1555
+ for cell in plan["cells"]:
1556
+ scenario = scenarios[cell["scenario_id"]]
1557
+ actor_packet = _actor_packet(
1558
+ cell["cell_id"], scenario, heldout["actor_blind_fields"]
1559
+ )
1560
+ values[cell["actor_packet_path"]] = _json_bytes(actor_packet)
1561
+ values[cell["session_envelope_path"]] = _json_bytes(_session_envelope(cell))
1562
+ return values
1563
+
1564
+
1565
+ def _validate_materials(run_dir: Path, plan: dict[str, Any], required: bool) -> None:
1566
+ expected = _material_map(plan)
1567
+ material_root = run_dir / "materials"
1568
+ if not material_root.exists():
1569
+ if required:
1570
+ _fail("materials have not been materialized")
1571
+ return
1572
+ for ref, raw in expected.items():
1573
+ actual = _read_file(run_dir / ref)
1574
+ if actual != raw:
1575
+ _fail(f"material bytes drifted: {ref}")
1576
+ expected_refs = set(expected)
1577
+ observed_refs = {
1578
+ str(path.relative_to(run_dir))
1579
+ for path in material_root.rglob("*")
1580
+ if path.is_file()
1581
+ }
1582
+ if observed_refs != expected_refs:
1583
+ _fail("material file inventory is not the exact frozen set")
1584
+
1585
+
1586
+ def materialize(args: argparse.Namespace) -> dict[str, Any]:
1587
+ plan, _, plan_sha, manifest = _load_run(args.run_dir, args.plan_sha256)
1588
+ _validate_run_inventory(args.run_dir, plan, manifest)
1589
+ if manifest["status"] != "initialized":
1590
+ _fail("materialize is allowed exactly once from initialized state")
1591
+ if (args.run_dir / "materials").exists():
1592
+ _fail("refusing to overwrite existing materials")
1593
+ for ref, raw in _material_map(plan).items():
1594
+ _write_new(args.run_dir / ref, raw)
1595
+ (args.run_dir / ATTEMPT_JOURNAL_ROOT).mkdir()
1596
+ for state in ("ready", "active", "completed"):
1597
+ (args.run_dir / ATTEMPT_JOURNAL_ROOT / state).mkdir()
1598
+ for cell in plan["cells"]:
1599
+ _write_new(
1600
+ args.run_dir / _journal_ref("ready", cell["cell_id"]),
1601
+ _journal_raw(plan_sha, cell),
1602
+ )
1603
+ _validate_materials(args.run_dir, plan, required=True)
1604
+ manifest["status"] = "materialized"
1605
+ _validate_manifest(manifest, plan, plan_sha)
1606
+ _replace_json(args.run_dir / "ingestion-manifest.json", manifest)
1607
+ return {"materialized_files": 146, "cells": 48, "dispatch_available": False}
1608
+
1609
+
1610
+ def _closed_embedded_object(
1611
+ raw_utf8: str,
1612
+ declared_sha256: str,
1613
+ label: str,
1614
+ ) -> dict[str, Any]:
1615
+ raw = raw_utf8.encode("utf-8")
1616
+ if _sha(raw) != declared_sha256:
1617
+ _fail(f"{label} hash mismatch")
1618
+ value = _strict_loads(raw)
1619
+ if raw != _canonical(value):
1620
+ _fail(f"{label} must use exact canonical JSON bytes")
1621
+ return value
1622
+
1623
+
1624
+ def _normalize_raw_event(event: dict[str, Any]) -> dict[str, Any]:
1625
+ try:
1626
+ normalized = _closed_embedded_object(
1627
+ event["raw_event_utf8"],
1628
+ event["raw_event_sha256"],
1629
+ f"raw event {event['event_index']}",
1630
+ )
1631
+ except EnvelopeError as exc:
1632
+ raise StopViolation("transcript_contract_failure", str(exc)) from exc
1633
+ event_kind = normalized.get("event_kind")
1634
+ if not isinstance(event_kind, str) or event_kind not in RAW_EVENT_KINDS:
1635
+ raise StopViolation(
1636
+ "transcript_contract_failure",
1637
+ f"raw event {event['event_index']} has an unknown action classification",
1638
+ )
1639
+ expected_keys = (
1640
+ {"event_kind", "turn_index", "text"}
1641
+ if event_kind in MESSAGE_EVENT_KINDS
1642
+ else {"event_kind", "turn_index"}
1643
+ )
1644
+ if set(normalized) != expected_keys:
1645
+ raise StopViolation(
1646
+ "transcript_contract_failure",
1647
+ f"raw event {event['event_index']} is not a closed normalized event",
1648
+ )
1649
+ turn_index = normalized["turn_index"]
1650
+ if turn_index is not None and type(turn_index) is not int:
1651
+ raise StopViolation(
1652
+ "transcript_contract_failure",
1653
+ f"raw event {event['event_index']} has an invalid turn index type",
1654
+ )
1655
+ raw_stop_codes = {
1656
+ "tool_action": "unplanned_tool_action",
1657
+ "network_action": "unplanned_network_action",
1658
+ "evidence_write_failure": "evidence_write_failure",
1659
+ "session_blocked": "partial_subject_output",
1660
+ "session_partial": "partial_subject_output",
1661
+ }
1662
+ if event_kind in raw_stop_codes:
1663
+ raise StopViolation(
1664
+ raw_stop_codes[event_kind],
1665
+ f"raw event stream reports stop condition {event_kind}",
1666
+ )
1667
+ if normalized["event_kind"] != event["event_kind"] or normalized[
1668
+ "turn_index"
1669
+ ] != event["turn_index"]:
1670
+ raise StopViolation(
1671
+ "transcript_contract_failure",
1672
+ f"raw event {event['event_index']} disagrees with normalized fields",
1673
+ )
1674
+ if event_kind in MESSAGE_EVENT_KINDS:
1675
+ if not _has_semantic_text(normalized["text"]):
1676
+ code = (
1677
+ "partial_subject_output"
1678
+ if event_kind == "subject_message"
1679
+ else "actor_protocol_deviation"
1680
+ )
1681
+ raise StopViolation(
1682
+ code,
1683
+ f"raw message event {event['event_index']} has no semantic text",
1684
+ )
1685
+ elif normalized["turn_index"] is not None:
1686
+ raise StopViolation(
1687
+ "transcript_contract_failure",
1688
+ f"non-message raw event {event['event_index']} claims a turn",
1689
+ )
1690
+ return normalized
1691
+
1692
+
1693
+ def _external_session_receipt(
1694
+ transcript: dict[str, Any], expected: dict[str, Any]
1695
+ ) -> dict[str, Any]:
1696
+ source = transcript["source"]
1697
+ try:
1698
+ receipt = _closed_embedded_object(
1699
+ source["external_session_receipt_utf8"],
1700
+ source["external_session_receipt_sha256"],
1701
+ "external session receipt",
1702
+ )
1703
+ except EnvelopeError as exc:
1704
+ raise StopViolation("transcript_contract_failure", str(exc)) from exc
1705
+ required = {
1706
+ "receipt_id",
1707
+ "external_session_id",
1708
+ "cell_id",
1709
+ "sequence_index",
1710
+ "started_at",
1711
+ "completed_at",
1712
+ "fresh_context",
1713
+ }
1714
+ if set(receipt) != required:
1715
+ raise StopViolation(
1716
+ "transcript_contract_failure",
1717
+ "external session receipt is not a closed exact record",
1718
+ )
1719
+ if not (
1720
+ isinstance(receipt["cell_id"], str)
1721
+ and type(receipt["sequence_index"]) is int
1722
+ and type(receipt["fresh_context"]) is bool
1723
+ and isinstance(receipt["started_at"], str)
1724
+ and isinstance(receipt["completed_at"], str)
1725
+ ):
1726
+ raise StopViolation(
1727
+ "transcript_contract_failure",
1728
+ "external session receipt field types are invalid",
1729
+ )
1730
+ if (
1731
+ receipt["cell_id"],
1732
+ receipt["sequence_index"],
1733
+ receipt["fresh_context"],
1734
+ ) != (expected["cell_id"], expected["sequence_index"], True):
1735
+ raise StopViolation(
1736
+ "out_of_order_ingestion",
1737
+ "external session receipt is not bound to the next fresh-context cell",
1738
+ )
1739
+ if not all(
1740
+ isinstance(receipt[key], str)
1741
+ and EXTERNAL_ID_RE.fullmatch(receipt[key]) is not None
1742
+ for key in ("receipt_id", "external_session_id")
1743
+ ):
1744
+ raise StopViolation(
1745
+ "transcript_contract_failure",
1746
+ "external session receipt identifiers are invalid",
1747
+ )
1748
+ started = _timestamp(receipt["started_at"], "external session started_at")
1749
+ completed = _timestamp(receipt["completed_at"], "external session completed_at")
1750
+ if started > completed:
1751
+ raise StopViolation(
1752
+ "transcript_contract_failure",
1753
+ "external session receipt starts after it completes",
1754
+ )
1755
+ if transcript["session"]["fresh_context"] is not True:
1756
+ raise StopViolation(
1757
+ "partial_subject_output",
1758
+ "transcript and external receipt do not attest a fresh context",
1759
+ )
1760
+ return receipt
1761
+
1762
+
1763
+ def _receipt_value(
1764
+ plan_sha: str,
1765
+ cell: dict[str, Any],
1766
+ transcript_ref: str,
1767
+ transcript_raw: bytes,
1768
+ transcript: dict[str, Any],
1769
+ ) -> dict[str, Any]:
1770
+ return {
1771
+ "schema_version": "ideation-diversity-ingestion-receipt/1.0",
1772
+ "suite": SUITE,
1773
+ "run_plan_sha256": plan_sha,
1774
+ "cell_id": cell["cell_id"],
1775
+ "sequence_index": cell["sequence_index"],
1776
+ "transcript_ref": transcript_ref,
1777
+ "transcript_sha256": _sha(transcript_raw),
1778
+ "authorization_record_ref": transcript["source"]["authorization_record_ref"],
1779
+ "authorization_record_sha256": transcript["source"][
1780
+ "authorization_record_sha256"
1781
+ ],
1782
+ "source_artifact_id": transcript["source"]["source_artifact_id"],
1783
+ "external_session_receipt_sha256": transcript["source"][
1784
+ "external_session_receipt_sha256"
1785
+ ],
1786
+ "accepted": True,
1787
+ "generated_evidence": False,
1788
+ }
1789
+
1790
+
1791
+ def _validate_ingested_artifacts(
1792
+ run_dir: Path,
1793
+ plan: dict[str, Any],
1794
+ plan_sha: str,
1795
+ manifest: dict[str, Any],
1796
+ *,
1797
+ semantic: bool = True,
1798
+ ) -> dict[str, Any]:
1799
+ scenarios = _scenario_index(phase1.load_assets()[0]) if semantic else {}
1800
+ authorization: dict[str, Any] | None = None
1801
+ if manifest["authorization_record_path"] is not None:
1802
+ authorization_raw = _read_file(
1803
+ run_dir / manifest["authorization_record_path"]
1804
+ )
1805
+ if _sha(authorization_raw) != manifest["authorization_record_sha256"]:
1806
+ _fail("preserved authorization record hash drift")
1807
+ authorization = _strict_loads(authorization_raw)
1808
+ _validate_authorization_record(authorization, plan, plan_sha)
1809
+ seen_source_artifact_ids: set[str] = set()
1810
+ seen_external_receipt_ids: set[str] = set()
1811
+ seen_external_session_ids: set[str] = set()
1812
+ last_completed_at: datetime | None = None
1813
+ for cell, row in zip(plan["cells"], manifest["cells"]):
1814
+ if row["status"] == "blocked":
1815
+ raw = _read_file(run_dir / row["transcript_ref"])
1816
+ if _sha(raw) != row["transcript_sha256"]:
1817
+ _fail(f"blocked transcript hash drift: {row['cell_id']}")
1818
+ receipt = manifest["stop_receipt"]
1819
+ if receipt is None or (
1820
+ receipt["raw_ref"], receipt["raw_sha256"]
1821
+ ) != (row["transcript_ref"], _sha(raw)):
1822
+ _fail(f"blocked evidence receipt drift: {row['cell_id']}")
1823
+ for artifact in receipt["preserved_auxiliary_artifacts"]:
1824
+ auxiliary_raw = _read_file(run_dir / artifact["ref"])
1825
+ if _sha(auxiliary_raw) != artifact["sha256"]:
1826
+ _fail(
1827
+ "preserved pre-stop artifact hash drift: "
1828
+ f"{artifact['ref']}"
1829
+ )
1830
+ if artifact["role"] == "authorization_record":
1831
+ _validate_authorization_record(
1832
+ _strict_loads(auxiliary_raw), plan, plan_sha
1833
+ )
1834
+ continue
1835
+ if row["status"] != "ingested":
1836
+ continue
1837
+ raw = _read_file(run_dir / row["transcript_ref"])
1838
+ if _sha(raw) != row["transcript_sha256"]:
1839
+ _fail(f"ingested transcript hash drift: {row['cell_id']}")
1840
+ transcript = _strict_loads(raw)
1841
+ if authorization is None or manifest["authorization_record_sha256"] != transcript["source"][
1842
+ "authorization_record_sha256"
1843
+ ]:
1844
+ _fail(f"transcript authorization binding drift: {row['cell_id']}")
1845
+ external_receipt = _external_session_receipt(transcript, cell)
1846
+ started_at = _timestamp(
1847
+ external_receipt["started_at"], "external session started_at"
1848
+ )
1849
+ completed_at = _timestamp(
1850
+ external_receipt["completed_at"], "external session completed_at"
1851
+ )
1852
+ if started_at < _timestamp(
1853
+ authorization["decision"]["decided_at"],
1854
+ "authorization decided_at",
1855
+ ):
1856
+ _fail(f"transcript predates authorization: {row['cell_id']}")
1857
+ identifiers = (
1858
+ (transcript["source"]["source_artifact_id"], seen_source_artifact_ids),
1859
+ (external_receipt["receipt_id"], seen_external_receipt_ids),
1860
+ (external_receipt["external_session_id"], seen_external_session_ids),
1861
+ )
1862
+ for identifier, seen in identifiers:
1863
+ if identifier in seen:
1864
+ _fail(f"external session identity was reused: {identifier}")
1865
+ seen.add(identifier)
1866
+ if last_completed_at is not None and started_at < last_completed_at:
1867
+ _fail(f"external session time/order regressed: {row['cell_id']}")
1868
+ last_completed_at = completed_at
1869
+ if semantic:
1870
+ _validate_transcript(
1871
+ transcript,
1872
+ cell,
1873
+ plan,
1874
+ plan_sha,
1875
+ scenarios[cell["scenario_id"]],
1876
+ )
1877
+ expected_receipt = _json_bytes(
1878
+ _receipt_value(
1879
+ plan_sha,
1880
+ cell,
1881
+ row["transcript_ref"],
1882
+ raw,
1883
+ transcript,
1884
+ )
1885
+ )
1886
+ actual_receipt = _read_file(run_dir / row["ingestion_receipt_ref"])
1887
+ if actual_receipt != expected_receipt:
1888
+ _fail(f"ingestion receipt drift: {row['cell_id']}")
1889
+ return {
1890
+ "source_artifact_ids": seen_source_artifact_ids,
1891
+ "external_receipt_ids": seen_external_receipt_ids,
1892
+ "external_session_ids": seen_external_session_ids,
1893
+ "last_completed_at": last_completed_at,
1894
+ }
1895
+
1896
+
1897
+ def validate_run(args: argparse.Namespace) -> dict[str, Any]:
1898
+ plan, _, plan_sha, manifest = _load_run(args.run_dir, args.plan_sha256)
1899
+ _validate_materials(
1900
+ args.run_dir, plan, required=manifest["status"] != "initialized"
1901
+ )
1902
+ _validate_ingested_artifacts(args.run_dir, plan, plan_sha, manifest)
1903
+ blind_transaction = _validate_blind_transaction(
1904
+ args.run_dir, plan, plan_sha, manifest
1905
+ )
1906
+ if manifest["status"] == "blind_finalized":
1907
+ if not blind_transaction["present"]:
1908
+ _fail("blind_finalized state is missing its durable blind intent")
1909
+ _validate_blind_bundle(args.run_dir, plan, plan_sha, manifest)
1910
+ elif (args.run_dir / "blind").exists():
1911
+ if not blind_transaction["present"]:
1912
+ _fail("atomic blind bundle exists without its durable blind intent")
1913
+ _validate_blind_bundle(args.run_dir, plan, plan_sha, manifest)
1914
+ _validate_run_inventory(args.run_dir, plan, manifest)
1915
+ return {
1916
+ "run_plan_sha256": plan_sha,
1917
+ "status": manifest["status"],
1918
+ "ingested": sum(row["status"] == "ingested" for row in manifest["cells"]),
1919
+ "cells": 48,
1920
+ "blind_transaction_pending": bool(
1921
+ blind_transaction["present"] and manifest["status"] != "blind_finalized"
1922
+ ),
1923
+ }
1924
+
1925
+
1926
+ def _validate_transcript(
1927
+ value: dict[str, Any],
1928
+ expected: dict[str, Any],
1929
+ plan: dict[str, Any],
1930
+ plan_sha: str,
1931
+ scenario: dict[str, Any],
1932
+ ) -> None:
1933
+ try:
1934
+ _validate_schema(TRANSCRIPT_SCHEMA, value, "transcript")
1935
+ except EnvelopeError as exc:
1936
+ raise StopViolation("transcript_contract_failure", str(exc)) from exc
1937
+ if value["cell_id"] != expected["cell_id"] or value["sequence_index"] != expected["sequence_index"]:
1938
+ raise StopViolation("out_of_order_ingestion", "transcript is not the next frozen cell")
1939
+ if value["run_plan_sha256"] != plan_sha:
1940
+ raise StopViolation("prompt_or_role_card_hash_mismatch", "transcript run-plan binding mismatch")
1941
+ binding_pairs = {
1942
+ "prompt_sha256": expected["prompt"]["sha256"],
1943
+ "scenario_sha256": expected["scenario_sha256"],
1944
+ "initial_message_sha256": expected["initial_message_sha256"],
1945
+ "actor_packet_sha256": expected["actor_packet_sha256"],
1946
+ "session_envelope_sha256": expected["session_envelope_sha256"],
1947
+ }
1948
+ if value["bindings"] != binding_pairs:
1949
+ raise StopViolation("prompt_or_role_card_hash_mismatch", "transcript prompt/role/envelope binding mismatch")
1950
+ _external_session_receipt(value, expected)
1951
+ observations = value["observations"]
1952
+ turns = value["turns"]
1953
+ events = value["events"]
1954
+ normalized_events = [_normalize_raw_event(event) for event in events]
1955
+ event_kinds = [event["event_kind"] for event in normalized_events]
1956
+ try:
1957
+ _assert_blindable_transcript(plan, value)
1958
+ except EnvelopeError as exc:
1959
+ raise StopViolation(
1960
+ "arm_leakage",
1961
+ f"transcript semantic blinding gate failed: {exc}",
1962
+ ) from exc
1963
+ transcript_text = "\n".join(turn["text"] for turn in turns).casefold()
1964
+ checks = (
1965
+ (
1966
+ not observations["prompt_hash_match"]
1967
+ or not observations["role_card_hash_match"],
1968
+ "prompt_or_role_card_hash_mismatch",
1969
+ ),
1970
+ (
1971
+ observations["arm_leakage"]
1972
+ or any(token in transcript_text for token in ARM_LEAK_TOKENS),
1973
+ "arm_leakage",
1974
+ ),
1975
+ (not observations["eligible"], "ineligible_session"),
1976
+ (
1977
+ bool(observations["unplanned_tool_actions"])
1978
+ or "tool_action" in event_kinds,
1979
+ "unplanned_tool_action",
1980
+ ),
1981
+ (
1982
+ bool(observations["unplanned_network_actions"])
1983
+ or "network_action" in event_kinds,
1984
+ "unplanned_network_action",
1985
+ ),
1986
+ (
1987
+ observations["evidence_write_failure"]
1988
+ or "evidence_write_failure" in event_kinds,
1989
+ "evidence_write_failure",
1990
+ ),
1991
+ (
1992
+ observations["partial_subject_output"]
1993
+ or "session_blocked" in event_kinds
1994
+ or "session_partial" in event_kinds,
1995
+ "partial_subject_output",
1996
+ ),
1997
+ (observations["actor_protocol_deviation"], "actor_protocol_deviation"),
1998
+ )
1999
+ for failed, code in checks:
2000
+ if failed:
2001
+ raise StopViolation(code, f"transcript reports stop condition {code}")
2002
+ session = value["session"]
2003
+ if not session["fresh_context"] or not session["complete"] or session["blocked"] or session["partial"]:
2004
+ raise StopViolation("partial_subject_output", "session is not a complete fresh-context record")
2005
+ if session["stop_reason"] not in {"freeze_point_reached", "actor_decided_early"}:
2006
+ raise StopViolation("actor_protocol_deviation", "session has a non-eligible stop reason")
2007
+ if [turn["turn_index"] for turn in turns] != list(range(1, len(turns) + 1)):
2008
+ raise StopViolation("transcript_contract_failure", "turn indices are not contiguous")
2009
+ if any(turn["role"] != ("actor" if index % 2 == 0 else "subject") for index, turn in enumerate(turns)):
2010
+ raise StopViolation("actor_protocol_deviation", "turn roles do not strictly alternate from actor")
2011
+ if turns[-1]["role"] != "actor" or turns[0]["text"] != scenario["initial_message"]:
2012
+ raise StopViolation("actor_protocol_deviation", "transcript first/final actor-turn contract failed")
2013
+ actor_turns = sum(turn["role"] == "actor" for turn in turns)
2014
+ subject_turns = len(turns) - actor_turns
2015
+ if (actor_turns, subject_turns) != (session["actor_turns"], session["subject_turns"]):
2016
+ raise StopViolation("transcript_contract_failure", "declared turn counts do not match transcript")
2017
+ if session["stop_reason"] == "freeze_point_reached" and actor_turns != 6:
2018
+ raise StopViolation("actor_protocol_deviation", "freeze-point session must end on actor turn 6")
2019
+ if [event["event_index"] for event in events] != list(range(1, len(events) + 1)):
2020
+ raise StopViolation("transcript_contract_failure", "event indices are not contiguous")
2021
+ if (
2022
+ event_kinds[0] != "session_started"
2023
+ or event_kinds[-1] != "session_completed"
2024
+ or event_kinds.count("session_started") != 1
2025
+ or event_kinds.count("session_completed") != 1
2026
+ ):
2027
+ raise StopViolation(
2028
+ "transcript_contract_failure",
2029
+ "eligible event stream requires exactly one first start and one last completion",
2030
+ )
2031
+ message_events = [
2032
+ event
2033
+ for event in normalized_events
2034
+ if event["event_kind"] in MESSAGE_EVENT_KINDS
2035
+ ]
2036
+ expected_messages = [
2037
+ {
2038
+ "event_kind": f"{turn['role']}_message",
2039
+ "turn_index": turn["turn_index"],
2040
+ "text": turn["text"],
2041
+ }
2042
+ for turn in turns
2043
+ ]
2044
+ if message_events != expected_messages:
2045
+ raise StopViolation(
2046
+ "transcript_contract_failure",
2047
+ "transcript turns are not derived byte-exactly from raw message events",
2048
+ )
2049
+
2050
+
2051
+ def _record_stop(
2052
+ run_dir: Path,
2053
+ plan: dict[str, Any],
2054
+ plan_sha: str,
2055
+ manifest: dict[str, Any],
2056
+ raw: bytes,
2057
+ violation: StopViolation,
2058
+ preserved_auxiliary_artifacts: list[dict[str, str]] | None = None,
2059
+ *,
2060
+ raw_capture_kind: str = "exact_input_bytes",
2061
+ ) -> None:
2062
+ index = manifest["next_sequence_index"] - 1
2063
+ expected = plan["cells"][index]
2064
+ raw_sha = _sha(raw)
2065
+ raw_ref = f"blocked/{expected['cell_id']}.transcript.{raw_sha}.raw"
2066
+ raw_path = run_dir / raw_ref
2067
+ if raw_path.exists():
2068
+ try:
2069
+ collision = _read_file(raw_path, limit=max(MAX_INPUT_BYTES, len(raw)))
2070
+ except EnvelopeError:
2071
+ collision = None
2072
+ if collision != raw:
2073
+ while True:
2074
+ candidate = raw_ref + f".collision-{secrets.token_hex(8)}"
2075
+ if not (run_dir / candidate).exists():
2076
+ raw_ref = candidate
2077
+ raw_path = run_dir / raw_ref
2078
+ break
2079
+ # Attempt complete-byte publication before freezing the marker so any
2080
+ # surviving atomic staging evidence can be hash-registered in its replay.
2081
+ raw_write_error: EnvelopeError | None = None
2082
+ try:
2083
+ _ensure_exact_new(raw_path, raw)
2084
+ except EnvelopeError as exc:
2085
+ raw_write_error = exc
2086
+ supplied = list(preserved_auxiliary_artifacts or [])
2087
+ if len(supplied) > 8:
2088
+ _fail("too many bounded ingestion artifacts to register")
2089
+ publication_staging_ref = (
2090
+ f".stop-intent.json.publish-"
2091
+ f"{_sha((plan_sha + expected['cell_id'] + raw_sha).encode('utf-8'))[:24]}.tmp"
2092
+ )
2093
+ manifest_replacement_staging_ref = (
2094
+ f".ingestion-manifest.json.stop-"
2095
+ f"{_sha((raw_sha + plan_sha + expected['cell_id']).encode('utf-8'))[:24]}.json"
2096
+ )
2097
+ stopped_manifest = copy.deepcopy(manifest)
2098
+ stopped_manifest["cells"][index].update(
2099
+ status="blocked",
2100
+ transcript_ref=raw_ref,
2101
+ transcript_sha256=raw_sha,
2102
+ ingestion_receipt_ref=None,
2103
+ )
2104
+ stopped_manifest["status"] = "stopped"
2105
+ stopped_manifest["stopped"] = True
2106
+ stopped_manifest["stop_receipt"] = {
2107
+ "cell_id": expected["cell_id"],
2108
+ "sequence_index": expected["sequence_index"],
2109
+ "reason_code": violation.code,
2110
+ "detail": violation.detail[:2000],
2111
+ "raw_ref": raw_ref,
2112
+ "raw_sha256": raw_sha,
2113
+ "raw_capture_kind": raw_capture_kind,
2114
+ "retry_forbidden": True,
2115
+ "stop_intent_ref": STOP_INTENT_REF,
2116
+ "stop_intent_committed_before_state_replacement": True,
2117
+ "stop_intent_publication_staging_ref": publication_staging_ref,
2118
+ "manifest_replacement_staging_ref": manifest_replacement_staging_ref,
2119
+ "pre_stop_inventory": {"entry_count": 0, "sha256": _sha(_canonical([]))},
2120
+ "preserved_auxiliary_artifacts": supplied,
2121
+ }
2122
+ expected_before, expected_directories = _expected_run_inventory(
2123
+ run_dir, plan, stopped_manifest
2124
+ )
2125
+ observed_before, observed_directories = _relative_files_and_directories(run_dir)
2126
+ stopped_manifest["stop_receipt"]["pre_stop_inventory"] = _inventory_binding(
2127
+ run_dir,
2128
+ observed_before - expected_before,
2129
+ observed_directories - expected_directories,
2130
+ )
2131
+ _validate_manifest(stopped_manifest, plan, plan_sha)
2132
+ prior_manifest_raw = _read_file(run_dir / "ingestion-manifest.json")
2133
+ if _strict_loads(prior_manifest_raw) != manifest:
2134
+ _fail("ingestion manifest changed while preparing durable stop intent")
2135
+ stopped_raw = _json_bytes(stopped_manifest)
2136
+ marker = {
2137
+ "schema_version": "ideation-diversity-stop-intent/1.0",
2138
+ "suite": SUITE,
2139
+ "run_plan_sha256": plan_sha,
2140
+ "prior_manifest_sha256": _sha(prior_manifest_raw),
2141
+ "cell_id": expected["cell_id"],
2142
+ "sequence_index": expected["sequence_index"],
2143
+ "reason_code": violation.code,
2144
+ "detail": violation.detail[:2000],
2145
+ "raw_ref": raw_ref,
2146
+ "raw_sha256": raw_sha,
2147
+ "raw_capture_kind": raw_capture_kind,
2148
+ "raw_base64": base64.b64encode(raw).decode("ascii"),
2149
+ "stopped_manifest_sha256": _sha(stopped_raw),
2150
+ "stopped_manifest_base64": base64.b64encode(stopped_raw).decode("ascii"),
2151
+ "pre_stop_inventory": stopped_manifest["stop_receipt"]["pre_stop_inventory"],
2152
+ "stop_intent_publication_staging_ref": publication_staging_ref,
2153
+ "manifest_replacement_staging_ref": manifest_replacement_staging_ref,
2154
+ "retry_forbidden": True,
2155
+ }
2156
+ marker_raw = _json_bytes(marker)
2157
+ _validate_stop_intent(marker_raw, plan, plan_sha)
2158
+ _publish_stop_intent(
2159
+ run_dir / STOP_INTENT_REF,
2160
+ marker_raw,
2161
+ publication_staging_ref,
2162
+ )
2163
+ try:
2164
+ _ensure_exact_new(raw_path, raw)
2165
+ raw_write_error = None
2166
+ except EnvelopeError as exc:
2167
+ raw_write_error = exc
2168
+ # The marker is the irreversible boundary. If this replacement fails,
2169
+ # every later load rejects retry and can replay the exact stopped state.
2170
+ _replace_json(
2171
+ run_dir / "ingestion-manifest.json",
2172
+ stopped_manifest,
2173
+ staging_ref=manifest_replacement_staging_ref,
2174
+ )
2175
+ if raw_write_error is not None:
2176
+ raise EnvelopeError(
2177
+ "run is permanently stopped; blocked raw recovery remains pending: "
2178
+ f"{raw_write_error}"
2179
+ ) from raw_write_error
2180
+
2181
+
2182
+ def _preserved_ingestion_artifacts(
2183
+ run_dir: Path,
2184
+ manifest_before: dict[str, Any],
2185
+ authorization_raw: bytes,
2186
+ transcript_ref: str,
2187
+ transcript_raw: bytes,
2188
+ receipt_ref: str,
2189
+ receipt_raw: bytes,
2190
+ ) -> list[dict[str, str]]:
2191
+ candidates: list[tuple[str, str, bytes]] = []
2192
+ if manifest_before["authorization_record_sha256"] is None:
2193
+ candidates.append(
2194
+ ("authorization_record", "authorization/record.json", authorization_raw)
2195
+ )
2196
+ candidates.extend(
2197
+ (
2198
+ (
2199
+ "accepted_transcript_before_state_commit",
2200
+ transcript_ref,
2201
+ transcript_raw,
2202
+ ),
2203
+ (
2204
+ "ingestion_receipt_before_state_commit",
2205
+ receipt_ref,
2206
+ receipt_raw,
2207
+ ),
2208
+ )
2209
+ )
2210
+ preserved: list[dict[str, str]] = []
2211
+ for role, ref, expected_raw in candidates:
2212
+ try:
2213
+ actual_raw = _read_file(run_dir / ref)
2214
+ except EnvelopeError:
2215
+ continue
2216
+ if actual_raw == expected_raw:
2217
+ preserved.append({"role": role, "ref": ref, "sha256": _sha(actual_raw)})
2218
+ return preserved
2219
+
2220
+
2221
+ def _bounded_acquisition_failure_evidence(
2222
+ path: Path,
2223
+ error: EnvelopeError,
2224
+ ) -> bytes:
2225
+ observed: dict[str, Any] = {
2226
+ "schema_version": "ideation-diversity-transcript-acquisition-failure/1.0",
2227
+ "kind": "bounded_acquisition_failure_record",
2228
+ "error": str(error)[:2000],
2229
+ "path_name": path.name[:255],
2230
+ "stat": None,
2231
+ "prefix_sha256": None,
2232
+ "prefix_bytes": 0,
2233
+ }
2234
+ try:
2235
+ metadata = path.lstat()
2236
+ observed["stat"] = {
2237
+ "device": metadata.st_dev,
2238
+ "inode": metadata.st_ino,
2239
+ "mode": stat.S_IFMT(metadata.st_mode),
2240
+ "size_bytes": metadata.st_size,
2241
+ "mtime_ns": metadata.st_mtime_ns,
2242
+ }
2243
+ if stat.S_ISREG(metadata.st_mode) and not path.is_symlink():
2244
+ with path.open("rb") as stream:
2245
+ prefix = stream.read(64 * 1024)
2246
+ observed["prefix_sha256"] = _sha(prefix)
2247
+ observed["prefix_bytes"] = len(prefix)
2248
+ except OSError as exc:
2249
+ observed["stat_error"] = str(exc)[:1000]
2250
+ return _json_bytes(observed)
2251
+
2252
+
2253
+ def ingest(args: argparse.Namespace) -> dict[str, Any]:
2254
+ plan, _, plan_sha, manifest = _load_run(args.run_dir, args.plan_sha256)
2255
+ if manifest["stopped"]:
2256
+ _fail("run is stopped; the frozen protocol forbids retry")
2257
+ if manifest["status"] in {"complete", "blind_finalized"}:
2258
+ _fail("all 48 transcripts are already ingested")
2259
+ if manifest["status"] not in {"materialized", "ingesting"}:
2260
+ _fail("transcripts may be ingested only after materialization")
2261
+ expected = plan["cells"][manifest["next_sequence_index"] - 1]
2262
+ scenario = _scenario_index(phase1.load_assets()[0])[expected["scenario_id"]]
2263
+ _begin_ingestion_attempt(args.run_dir, plan, plan_sha, manifest)
2264
+ try:
2265
+ raw = _read_file(args.transcript)
2266
+ except EnvelopeError as exc:
2267
+ violation = StopViolation(
2268
+ "transcript_contract_failure",
2269
+ f"cannot acquire bounded external transcript bytes: {exc}",
2270
+ )
2271
+ evidence = _bounded_acquisition_failure_evidence(args.transcript, exc)
2272
+ _record_stop(
2273
+ args.run_dir,
2274
+ plan,
2275
+ plan_sha,
2276
+ manifest,
2277
+ evidence,
2278
+ violation,
2279
+ raw_capture_kind="bounded_acquisition_failure_record",
2280
+ )
2281
+ raise violation from exc
2282
+ try:
2283
+ _validate_materials(args.run_dir, plan, required=True)
2284
+ sequence_state = _validate_ingested_artifacts(
2285
+ args.run_dir,
2286
+ plan,
2287
+ plan_sha,
2288
+ manifest,
2289
+ semantic=False,
2290
+ )
2291
+ _validate_run_inventory(args.run_dir, plan, manifest)
2292
+ except EnvelopeError as exc:
2293
+ violation = StopViolation(
2294
+ "evidence_write_failure",
2295
+ f"pre-ingestion run evidence/inventory validation failed: {exc}",
2296
+ )
2297
+ _record_stop(args.run_dir, plan, plan_sha, manifest, raw, violation)
2298
+ raise violation from exc
2299
+ try:
2300
+ value = _strict_loads(raw)
2301
+ _validate_transcript(value, expected, plan, plan_sha, scenario)
2302
+ try:
2303
+ authorization_raw = _read_file(args.authorization_record)
2304
+ authorization = _strict_loads(authorization_raw)
2305
+ _validate_authorization_record(authorization, plan, plan_sha)
2306
+ except EnvelopeError as exc:
2307
+ raise StopViolation(
2308
+ "authorization_record_missing_or_mismatch",
2309
+ f"cannot verify external authorization record: {exc}",
2310
+ ) from exc
2311
+ authorization_sha = _sha(authorization_raw)
2312
+ if authorization_sha != value["source"]["authorization_record_sha256"]:
2313
+ raise StopViolation(
2314
+ "authorization_record_missing_or_mismatch",
2315
+ "external authorization record hash does not match transcript binding",
2316
+ )
2317
+ if value["source"]["authorization_record_ref"] != authorization["record_id"]:
2318
+ raise StopViolation(
2319
+ "authorization_record_missing_or_mismatch",
2320
+ "transcript authorization reference differs from record id",
2321
+ )
2322
+ external_receipt = _external_session_receipt(value, expected)
2323
+ started_at = _timestamp(
2324
+ external_receipt["started_at"], "external session started_at"
2325
+ )
2326
+ if started_at < _timestamp(
2327
+ authorization["decision"]["decided_at"],
2328
+ "authorization decided_at",
2329
+ ):
2330
+ raise StopViolation(
2331
+ "authorization_record_missing_or_mismatch",
2332
+ "external session starts before the authorization decision",
2333
+ )
2334
+ uniqueness = (
2335
+ (
2336
+ value["source"]["source_artifact_id"],
2337
+ sequence_state["source_artifact_ids"],
2338
+ ),
2339
+ (
2340
+ external_receipt["receipt_id"],
2341
+ sequence_state["external_receipt_ids"],
2342
+ ),
2343
+ (
2344
+ external_receipt["external_session_id"],
2345
+ sequence_state["external_session_ids"],
2346
+ ),
2347
+ )
2348
+ if any(identifier in seen for identifier, seen in uniqueness):
2349
+ raise StopViolation(
2350
+ "out_of_order_ingestion",
2351
+ "external artifact, receipt, or fresh-context session id was reused",
2352
+ )
2353
+ last_completed_at = sequence_state["last_completed_at"]
2354
+ if last_completed_at is not None and started_at < last_completed_at:
2355
+ raise StopViolation(
2356
+ "out_of_order_ingestion",
2357
+ "external session time/order regressed across frozen cells",
2358
+ )
2359
+ if manifest["authorization_record_sha256"] not in {None, authorization_sha}:
2360
+ raise StopViolation(
2361
+ "authorization_record_missing_or_mismatch",
2362
+ "run is already bound to a different authorization record",
2363
+ )
2364
+ except StopViolation as violation:
2365
+ _record_stop(args.run_dir, plan, plan_sha, manifest, raw, violation)
2366
+ raise
2367
+ except EnvelopeError as exc:
2368
+ violation = StopViolation("transcript_contract_failure", str(exc))
2369
+ _record_stop(args.run_dir, plan, plan_sha, manifest, raw, violation)
2370
+ raise violation from exc
2371
+ cell_id = expected["cell_id"]
2372
+ transcript_ref = f"transcripts/{cell_id}.json"
2373
+ receipt_ref = f"receipts/{cell_id}.json"
2374
+ receipt = _receipt_value(plan_sha, expected, transcript_ref, raw, value)
2375
+ receipt_raw = _json_bytes(receipt)
2376
+ manifest_before_ingestion = copy.deepcopy(manifest)
2377
+ try:
2378
+ if manifest["authorization_record_sha256"] is None:
2379
+ _write_new(
2380
+ args.run_dir / "authorization/record.json",
2381
+ authorization_raw,
2382
+ )
2383
+ manifest["authorization_record_path"] = "authorization/record.json"
2384
+ manifest["authorization_record_sha256"] = authorization_sha
2385
+ _write_new(args.run_dir / transcript_ref, raw)
2386
+ _write_new(args.run_dir / receipt_ref, receipt_raw)
2387
+ except EnvelopeError as exc:
2388
+ violation = StopViolation(
2389
+ "evidence_write_failure",
2390
+ f"cannot preserve accepted transcript/receipt: {exc}",
2391
+ )
2392
+ try:
2393
+ _record_stop(
2394
+ args.run_dir,
2395
+ plan,
2396
+ plan_sha,
2397
+ manifest_before_ingestion,
2398
+ raw,
2399
+ violation,
2400
+ _preserved_ingestion_artifacts(
2401
+ args.run_dir,
2402
+ manifest_before_ingestion,
2403
+ authorization_raw,
2404
+ transcript_ref,
2405
+ raw,
2406
+ receipt_ref,
2407
+ receipt_raw,
2408
+ ),
2409
+ )
2410
+ except EnvelopeError as stop_exc:
2411
+ raise EnvelopeError(
2412
+ "evidence write failed and the stop receipt could not be persisted; "
2413
+ "do not retry this run"
2414
+ ) from stop_exc
2415
+ raise violation from exc
2416
+ manifest["cells"][expected["sequence_index"] - 1].update(
2417
+ status="ingested",
2418
+ transcript_ref=transcript_ref,
2419
+ transcript_sha256=_sha(raw),
2420
+ ingestion_receipt_ref=receipt_ref,
2421
+ )
2422
+ manifest["next_sequence_index"] += 1
2423
+ ingested = manifest["next_sequence_index"] - 1
2424
+ manifest["status"] = "complete" if ingested == 48 else "ingesting"
2425
+ _validate_manifest(manifest, plan, plan_sha)
2426
+ try:
2427
+ _replace_json(args.run_dir / "ingestion-manifest.json", manifest)
2428
+ except EnvelopeError as exc:
2429
+ violation = StopViolation(
2430
+ "evidence_write_failure",
2431
+ f"cannot persist ingestion manifest: {exc}",
2432
+ )
2433
+ try:
2434
+ _record_stop(
2435
+ args.run_dir,
2436
+ plan,
2437
+ plan_sha,
2438
+ manifest_before_ingestion,
2439
+ raw,
2440
+ violation,
2441
+ _preserved_ingestion_artifacts(
2442
+ args.run_dir,
2443
+ manifest_before_ingestion,
2444
+ authorization_raw,
2445
+ transcript_ref,
2446
+ raw,
2447
+ receipt_ref,
2448
+ receipt_raw,
2449
+ ),
2450
+ )
2451
+ except EnvelopeError as stop_exc:
2452
+ raise EnvelopeError(
2453
+ "ingestion manifest write failed and the stop receipt could not be "
2454
+ "persisted; do not retry this run"
2455
+ ) from stop_exc
2456
+ raise violation from exc
2457
+ _complete_ingestion_attempt(args.run_dir, expected["cell_id"])
2458
+ return {"ingested": ingested, "next_sequence_index": manifest["next_sequence_index"]}
2459
+
2460
+
2461
+ def _artifact_presence() -> dict[str, bool]:
2462
+ return {
2463
+ "structured_judge_labels_attached": False,
2464
+ "adjudication_artifact_attached": False,
2465
+ "human_evidence_artifact_attached": False,
2466
+ }
2467
+
2468
+
2469
+ def _assignment_boundary() -> dict[str, bool]:
2470
+ return {
2471
+ "future_closed_assignment_ledger_gate_required": True,
2472
+ "assignment_ledger_implemented_by_no_call_runner": False,
2473
+ "same_judge_equivalent_scholar_context_cross_condition_exposure_forbidden": True,
2474
+ "bundle_alone_proves_judge_exposure_blindness": False,
2475
+ }
2476
+
2477
+
2478
+ def _blind_identifiers(plan: dict[str, Any]) -> set[str]:
2479
+ identifiers: set[str] = set()
2480
+ for cell in plan["cells"]:
2481
+ identifiers.update(
2482
+ _normalized_blind_text(str(cell[key]))
2483
+ for key in (
2484
+ "cell_id",
2485
+ "experiment_id",
2486
+ "arm_id",
2487
+ "scenario_id",
2488
+ "pair_id",
2489
+ "actor_block_id",
2490
+ )
2491
+ )
2492
+ return identifiers
2493
+
2494
+
2495
+ def _normalized_blind_text(text: str) -> str:
2496
+ """Normalize compatibility forms and punctuation separators before matching."""
2497
+ normalized = unicodedata.normalize("NFKC", text).casefold()
2498
+ separated = "".join(
2499
+ " " if unicodedata.category(character)[0] in {"C", "P", "Z"} else character
2500
+ for character in normalized
2501
+ )
2502
+ return " ".join(separated.split())
2503
+
2504
+
2505
+ def _compact_blind_text(text: str) -> str:
2506
+ """Remove spaces from the mark-safe screening projection."""
2507
+ return "".join(_blind_screen_text(text).split())
2508
+
2509
+
2510
+ def _fold_blind_alphanumeric(source_character: str) -> str:
2511
+ visible = "".join(
2512
+ folded
2513
+ for folded in unicodedata.normalize("NFKD", source_character.casefold())
2514
+ if unicodedata.category(folded)[0] not in {"M", "C", "P", "S", "Z"}
2515
+ )
2516
+ if visible:
2517
+ return visible
2518
+ # Some compatibility letters decompose entirely to a separator plus a
2519
+ # mark. Retain the original letter as an alphanumeric boundary sentinel.
2520
+ return source_character.casefold()
2521
+
2522
+
2523
+ def _blind_screen_text(text: str) -> str:
2524
+ """Project text without allowing a mark to casefold into a letter.
2525
+
2526
+ Marks and format/control characters disappear. Punctuation and separators
2527
+ become spaces so real word boundaries survive, while a matcher can still
2528
+ allow those spaces between the characters of an obfuscated identifier.
2529
+ """
2530
+ projected: list[str] = []
2531
+ for source_character in text:
2532
+ source_category = unicodedata.category(source_character)[0]
2533
+ if source_category in {"M", "C"}:
2534
+ continue
2535
+ if source_category in {"P", "S", "Z"}:
2536
+ projected.append(" ")
2537
+ continue
2538
+ # Compatibility punctuation created by one source L/N code point is
2539
+ # not a real boundary. Keeping the source character atomic makes
2540
+ # `ĿCell` and parenthesized-number forms remain adjacent to `Cell`.
2541
+ projected.append(_fold_blind_alphanumeric(source_character))
2542
+ return " ".join("".join(projected).split())
2543
+
2544
+
2545
+ def _joined_blind_text(text: str) -> str:
2546
+ """Repair mark/format/punctuation splits while preserving real spacing."""
2547
+ projected: list[str] = []
2548
+ for source_character in text:
2549
+ source_category = unicodedata.category(source_character)[0]
2550
+ if source_category in {"M", "C", "P", "S"}:
2551
+ continue
2552
+ if source_category == "Z":
2553
+ projected.append(" ")
2554
+ continue
2555
+ projected.append(_fold_blind_alphanumeric(source_character))
2556
+ return " ".join("".join(projected).split())
2557
+
2558
+
2559
+ def _contains_complete_normalized_identifier(text: str, identifier: str) -> bool:
2560
+ if len(identifier) < 4:
2561
+ return False
2562
+ return re.search(
2563
+ r"(?<![^\W_])" + re.escape(identifier) + r"(?![^\W_])",
2564
+ text,
2565
+ ) is not None
2566
+
2567
+
2568
+ def _contains_complete_compact_identifier(text: str, identifier: str) -> bool:
2569
+ """Match an entire identifier despite inserted marks or separators.
2570
+
2571
+ Letter/number boundaries keep the compact comparison from turning ordinary
2572
+ surrounding prose into a match. The separator expression operates on the
2573
+ NFKD form so combining marks, format characters, punctuation, and spacing
2574
+ cannot split the characters of a frozen identifier.
2575
+ """
2576
+ compact = _compact_blind_text(identifier)
2577
+ return _contains_complete_compact_projection(_blind_screen_text(text), compact)
2578
+
2579
+
2580
+ def _contains_complete_compact_projection(screen: str, compact: str) -> bool:
2581
+ if len(compact) < 4:
2582
+ return False
2583
+ separator = r"\s*"
2584
+ return re.search(
2585
+ r"(?<![^\W_])"
2586
+ + separator.join(re.escape(character) for character in compact)
2587
+ + r"(?![^\W_])",
2588
+ screen,
2589
+ ) is not None
2590
+
2591
+
2592
+ def _contains_compact_phrase_projection(
2593
+ screen: str, compact_text: str, compact: str
2594
+ ) -> bool:
2595
+ if any(ord(character) > 127 for character in compact):
2596
+ return compact in compact_text
2597
+ return _contains_complete_compact_projection(screen, compact)
2598
+
2599
+
2600
+ def _obfuscated_token_pattern(token: str) -> str:
2601
+ return r"\s*".join(re.escape(character) for character in token)
2602
+
2603
+
2604
+ _COMPACT_MAPPING_RELATION_PATTERN = re.compile(
2605
+ r"(?<![^\W_])(?:"
2606
+ + "|".join(
2607
+ _obfuscated_token_pattern(token)
2608
+ for token in ("mechanism", "feature", "probe", "guardrail", "guardrails")
2609
+ )
2610
+ + r")(?:[^\W_]|\s){0,32}(?<![^\W_])(?:"
2611
+ + "|".join(
2612
+ _obfuscated_token_pattern(token)
2613
+ for token in ("enabled", "disabled", "ablated", "on", "off")
2614
+ )
2615
+ + r")(?![^\W_])",
2616
+ re.IGNORECASE,
2617
+ )
2618
+
2619
+
2620
+ def _assert_blindable_transcript(
2621
+ plan: dict[str, Any], transcript: dict[str, Any]
2622
+ ) -> None:
2623
+ _assert_blind_texts(plan, [turn["text"] for turn in transcript["turns"]])
2624
+
2625
+
2626
+ def _assert_blind_texts(plan: dict[str, Any], texts: list[str]) -> None:
2627
+ blind_identifiers = _blind_identifiers(plan)
2628
+ blind_identifier_projections = tuple(
2629
+ (identifier, _compact_blind_text(identifier))
2630
+ for identifier in blind_identifiers
2631
+ )
2632
+ mapping_phrase_projections = tuple(
2633
+ _compact_blind_text(phrase) for phrase in MAPPING_LEAK_COMPACT_PHRASES
2634
+ )
2635
+ human_phrase_projections = tuple(
2636
+ _compact_blind_text(phrase) for phrase in HUMAN_EVIDENCE_COMPACT_PHRASES
2637
+ )
2638
+ for text in texts:
2639
+ folded = _normalized_blind_text(text)
2640
+ joined = _joined_blind_text(text)
2641
+ screen = _blind_screen_text(text)
2642
+ compact_text = "".join(screen.split())
2643
+ leaked = next(
2644
+ (
2645
+ identifier
2646
+ for identifier, compact in blind_identifier_projections
2647
+ if _contains_complete_compact_projection(screen, compact)
2648
+ ),
2649
+ None,
2650
+ )
2651
+ if leaked is not None:
2652
+ _fail(f"transcript free text leaks frozen blind identifier {leaked!r}")
2653
+ if any(
2654
+ pattern.search(candidate)
2655
+ for pattern in MAPPING_LEAK_PATTERNS
2656
+ for candidate in (folded, joined)
2657
+ ):
2658
+ _fail("transcript free text leaks pair/arm/replicate mapping")
2659
+ if any(
2660
+ _contains_compact_phrase_projection(screen, compact_text, compact)
2661
+ for compact in mapping_phrase_projections
2662
+ ) or _COMPACT_MAPPING_RELATION_PATTERN.search(screen):
2663
+ _fail("transcript free text leaks pair/arm/replicate mapping")
2664
+ if any(
2665
+ pattern.search(candidate)
2666
+ for pattern in HUMAN_EVIDENCE_PATTERNS
2667
+ for candidate in (folded, joined)
2668
+ ):
2669
+ _fail("transcript free text contains prior label/adjudication/human evidence")
2670
+ if any(
2671
+ _contains_compact_phrase_projection(screen, compact_text, compact)
2672
+ for compact in human_phrase_projections
2673
+ ):
2674
+ _fail("transcript free text contains prior label/adjudication/human evidence")
2675
+
2676
+
2677
+ def _assert_blind_packet_structure(
2678
+ plan: dict[str, Any], packet: dict[str, Any]
2679
+ ) -> None:
2680
+ forbidden_keys = {
2681
+ "cell_id",
2682
+ "sequence_index",
2683
+ "experiment_id",
2684
+ "arm_id",
2685
+ "scenario_id",
2686
+ "pair_id",
2687
+ "replicate",
2688
+ "actor_block_id",
2689
+ "source",
2690
+ "events",
2691
+ "observations",
2692
+ }
2693
+
2694
+ def visit(value: Any) -> None:
2695
+ if isinstance(value, dict):
2696
+ leaked_keys = forbidden_keys.intersection(value)
2697
+ if leaked_keys:
2698
+ _fail(f"blind packet contains assignment/source keys {sorted(leaked_keys)!r}")
2699
+ for child in value.values():
2700
+ visit(child)
2701
+ elif isinstance(value, list):
2702
+ for child in value:
2703
+ visit(child)
2704
+
2705
+ visit(packet)
2706
+ role_card = packet["role_card"]
2707
+ role_texts = [
2708
+ role_card["role_brief"],
2709
+ role_card["initial_message"],
2710
+ *[row["statement"] for row in role_card["owned_framings"]],
2711
+ *role_card["forbidden_out_of_role"],
2712
+ packet["domain"],
2713
+ ]
2714
+ _assert_blind_texts(plan, role_texts)
2715
+
2716
+
2717
+ def _blind_packet_value(
2718
+ blind_id: str,
2719
+ scenario: dict[str, Any],
2720
+ transcript: dict[str, Any],
2721
+ codebook_raw: bytes,
2722
+ ) -> dict[str, Any]:
2723
+ return {
2724
+ "schema_version": "ideation-diversity-isolated-blind-session/1.0",
2725
+ "suite": SUITE,
2726
+ "blind_session_id": blind_id,
2727
+ "content_class": "repository-owned synthetic scholar role cards",
2728
+ "blinded_to": BLINDED_TO,
2729
+ "delivery": {
2730
+ "isolated_single_session": True,
2731
+ "deliver_other_sessions_together": False,
2732
+ "assignment_ledger_gate_required_before_delivery": True,
2733
+ },
2734
+ "assignment_boundary": _assignment_boundary(),
2735
+ "artifact_presence": _artifact_presence(),
2736
+ "free_text_screening": {
2737
+ "frozen_identifiers_rejected": True,
2738
+ "mapping_markers_rejected": True,
2739
+ "prior_label_adjudication_human_markers_rejected": True,
2740
+ },
2741
+ "codebook": {
2742
+ "sha256": _sha(codebook_raw),
2743
+ "text": codebook_raw.decode("utf-8"),
2744
+ },
2745
+ "language": scenario["language"],
2746
+ "domain": scenario["domain"],
2747
+ "role_card": {
2748
+ key: scenario[key]
2749
+ for key in (
2750
+ "role_brief",
2751
+ "initial_message",
2752
+ "owned_framings",
2753
+ "response_policy",
2754
+ "forbidden_out_of_role",
2755
+ )
2756
+ },
2757
+ "transcript": {
2758
+ "stop_reason": transcript["session"]["stop_reason"],
2759
+ "turns": transcript["turns"],
2760
+ },
2761
+ }
2762
+
2763
+
2764
+ def _blind_inventory_value(
2765
+ plan_sha: str, inventory_rows: list[dict[str, str]]
2766
+ ) -> dict[str, Any]:
2767
+ return {
2768
+ "schema_version": "ideation-diversity-blind-inventory/1.0",
2769
+ "suite": SUITE,
2770
+ "run_plan_sha256": plan_sha,
2771
+ "packet_count": 48,
2772
+ "delivery_rule": (
2773
+ "deliver_exactly_one_isolated_packet_per_first_round_judge_assignment"
2774
+ ),
2775
+ "assignment_boundary": _assignment_boundary(),
2776
+ "artifact_presence": _artifact_presence(),
2777
+ "packets": inventory_rows,
2778
+ }
2779
+
2780
+
2781
+ def _private_map_value(
2782
+ plan_sha: str,
2783
+ inventory_raw: bytes,
2784
+ mapping: list[dict[str, Any]],
2785
+ ) -> dict[str, Any]:
2786
+ return {
2787
+ "schema_version": "ideation-diversity-private-arm-map/1.0",
2788
+ "suite": SUITE,
2789
+ "run_plan_sha256": plan_sha,
2790
+ "inventory_sha256": _sha(inventory_raw),
2791
+ "private": True,
2792
+ "protection": {
2793
+ "kind": "procedural_nondisclosure_only",
2794
+ "encrypted": False,
2795
+ "directory_mode": "0700",
2796
+ "file_mode": "0600",
2797
+ "unblind_only_after_raw_labels_and_adjudication_sealed": True,
2798
+ },
2799
+ "mapping": mapping,
2800
+ }
2801
+
2802
+
2803
+ def _blind_manifest_value(
2804
+ plan_sha: str,
2805
+ source_ingestion_manifest_sha: str,
2806
+ inventory_raw: bytes,
2807
+ packets: list[tuple[str, bytes]],
2808
+ private_map_raw: bytes,
2809
+ ) -> dict[str, Any]:
2810
+ return {
2811
+ "schema_version": "ideation-diversity-blind-manifest/1.0",
2812
+ "suite": SUITE,
2813
+ "run_plan_sha256": plan_sha,
2814
+ "source_ingestion_manifest_sha256": source_ingestion_manifest_sha,
2815
+ "inventory_ref": "blind/inventory.json",
2816
+ "inventory_sha256": _sha(inventory_raw),
2817
+ "private_map_ref": "blind/private/arm-map.json",
2818
+ "private_map_sha256": _sha(private_map_raw),
2819
+ "packet_count": 48,
2820
+ "packets": [
2821
+ {
2822
+ "blind_session_id": blind_id,
2823
+ "packet_ref": f"blind/sessions/{blind_id}.json",
2824
+ "packet_sha256": _sha(packet_raw),
2825
+ }
2826
+ for blind_id, packet_raw in packets
2827
+ ],
2828
+ "finalized_artifacts": True,
2829
+ "exact_inventory_required": True,
2830
+ }
2831
+
2832
+
2833
+ def _blind_intent_value(
2834
+ plan_sha: str,
2835
+ source_ingestion_manifest_sha: str,
2836
+ private_id_nonce: str,
2837
+ inventory_raw: bytes,
2838
+ packets: list[tuple[str, bytes]],
2839
+ private_map_raw: bytes,
2840
+ blind_manifest_raw: bytes,
2841
+ ) -> dict[str, Any]:
2842
+ return {
2843
+ "schema_version": "ideation-diversity-blind-intent/1.0",
2844
+ "suite": SUITE,
2845
+ "run_plan_sha256": plan_sha,
2846
+ "source_ingestion_manifest_sha256": source_ingestion_manifest_sha,
2847
+ "private_id_nonce": private_id_nonce,
2848
+ "protection": {
2849
+ "procedural_nondisclosure_only": True,
2850
+ "file_mode": "0600",
2851
+ "deliver_to_judges": False,
2852
+ },
2853
+ "staging_ref": BLIND_STAGING_REF,
2854
+ "final_ref": "blind",
2855
+ "atomic_alias_root": BLIND_ALIAS_ROOT,
2856
+ "atomic_alias_policy": "deterministic_target_hash_hardlink_exact_replay",
2857
+ "blind_manifest_sha256": _sha(blind_manifest_raw),
2858
+ "inventory_sha256": _sha(inventory_raw),
2859
+ "private_map_sha256": _sha(private_map_raw),
2860
+ "packets": [
2861
+ {
2862
+ "blind_session_id": blind_id,
2863
+ "packet_sha256": _sha(packet_raw),
2864
+ }
2865
+ for blind_id, packet_raw in packets
2866
+ ],
2867
+ "recovery_policy": "exact_recovery_only_no_second_bundle",
2868
+ }
2869
+
2870
+
2871
+ def _blind_alias_ref(target_ref: str, raw: bytes) -> str:
2872
+ digest = _sha(f"{target_ref}\0{_sha(raw)}".encode("utf-8"))
2873
+ return f"{BLIND_ALIAS_ROOT}/{digest[:32]}.tmp"
2874
+
2875
+
2876
+ def _blind_alias_contract(expected_files: dict[str, bytes]) -> dict[str, tuple[str, bytes]]:
2877
+ return {
2878
+ _blind_alias_ref(target_ref, raw): (target_ref, raw)
2879
+ for target_ref, raw in expected_files.items()
2880
+ }
2881
+
2882
+
2883
+ def _ensure_blind_exact(
2884
+ run_dir: Path, target_ref: str, raw: bytes
2885
+ ) -> None:
2886
+ target = run_dir / BLIND_STAGING_REF / target_ref
2887
+ alias = run_dir / _blind_alias_ref(target_ref, raw)
2888
+ target.parent.mkdir(parents=True, exist_ok=True)
2889
+ if target.exists():
2890
+ if _read_file(target, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
2891
+ _fail(f"blind staging artifact collision at {target_ref}")
2892
+ if alias.exists():
2893
+ if _read_file(alias, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
2894
+ _fail(f"blind atomic alias collision at {alias.name}")
2895
+ if alias.stat().st_ino != target.stat().st_ino:
2896
+ _fail(f"blind atomic alias is not the target hardlink: {alias.name}")
2897
+ return
2898
+ if alias.exists():
2899
+ if _read_file(alias, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
2900
+ _fail(f"blind atomic alias collision at {alias.name}")
2901
+ else:
2902
+ descriptor: int | None = None
2903
+ try:
2904
+ flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
2905
+ if hasattr(os, "O_NOFOLLOW"):
2906
+ flags |= os.O_NOFOLLOW
2907
+ descriptor = os.open(alias, flags, 0o600)
2908
+ view = memoryview(raw)
2909
+ written = 0
2910
+ while written < len(view):
2911
+ count = os.write(descriptor, view[written:])
2912
+ if count <= 0:
2913
+ raise OSError("blind atomic alias write made no progress")
2914
+ written += count
2915
+ os.fsync(descriptor)
2916
+ os.close(descriptor)
2917
+ descriptor = None
2918
+ _fsync_directory(alias.parent)
2919
+ except OSError as exc:
2920
+ raise EnvelopeError(
2921
+ f"cannot stage intent-bound blind alias {alias}: {exc}"
2922
+ ) from exc
2923
+ finally:
2924
+ if descriptor is not None:
2925
+ try:
2926
+ os.close(descriptor)
2927
+ except OSError:
2928
+ pass
2929
+ try:
2930
+ os.link(alias, target, follow_symlinks=False)
2931
+ _fsync_directory(target.parent)
2932
+ except OSError as exc:
2933
+ raise EnvelopeError(
2934
+ f"cannot publish intent-bound blind artifact {target}: {exc}"
2935
+ ) from exc
2936
+ try:
2937
+ alias.unlink()
2938
+ _fsync_directory(alias.parent)
2939
+ except OSError:
2940
+ # This complete alias is deterministic and hash-bound by blind-intent.
2941
+ # It remains durable evidence and is exact-replayed on every load.
2942
+ pass
2943
+
2944
+
2945
+ def _validate_blind_aliases(
2946
+ run_dir: Path,
2947
+ expected_files: dict[str, bytes],
2948
+ *,
2949
+ final_published: bool,
2950
+ ) -> None:
2951
+ root = run_dir / BLIND_ALIAS_ROOT
2952
+ if not root.exists():
2953
+ return
2954
+ if root.is_symlink() or not root.is_dir() or stat.S_IMODE(root.stat().st_mode) != 0o700:
2955
+ _fail("blind atomic alias root must remain a real 0700 directory")
2956
+ observed_files, observed_directories = _relative_files_and_directories(root)
2957
+ if observed_directories:
2958
+ _fail("blind atomic alias root may not contain subdirectories")
2959
+ contract = _blind_alias_contract(expected_files)
2960
+ observed_refs = {f"{BLIND_ALIAS_ROOT}/{ref}" for ref in observed_files}
2961
+ if not observed_refs <= set(contract):
2962
+ _fail("blind atomic alias root contains an unbound artifact")
2963
+ target_root = run_dir / ("blind" if final_published else BLIND_STAGING_REF)
2964
+ for alias_ref in observed_refs:
2965
+ target_ref, raw = contract[alias_ref]
2966
+ alias = run_dir / alias_ref
2967
+ if _read_file(alias, limit=max(MAX_INPUT_BYTES, len(raw))) != raw:
2968
+ _fail(f"blind atomic alias bytes drifted: {alias_ref}")
2969
+ if stat.S_IMODE(alias.stat().st_mode) != 0o600:
2970
+ _fail(f"blind atomic alias mode must remain 0600: {alias_ref}")
2971
+ target = target_root / target_ref
2972
+ if target.exists() and alias.stat().st_ino != target.stat().st_ino:
2973
+ _fail(f"blind atomic alias is not target hardlink: {alias_ref}")
2974
+
2975
+
2976
+ def _deterministic_blind_id(
2977
+ plan_sha: str,
2978
+ source_ingestion_manifest_sha: str,
2979
+ cell_id: str,
2980
+ private_id_nonce: str,
2981
+ ) -> str:
2982
+ digest = _sha(
2983
+ (
2984
+ f"{private_id_nonce}\0{plan_sha}\0"
2985
+ f"{source_ingestion_manifest_sha}\0{cell_id}"
2986
+ ).encode("utf-8")
2987
+ )
2988
+ return f"blind-{digest[:24]}"
2989
+
2990
+
2991
+ def _blind_transaction_values(
2992
+ run_dir: Path,
2993
+ plan: dict[str, Any],
2994
+ plan_sha: str,
2995
+ manifest: dict[str, Any],
2996
+ private_id_nonce: str,
2997
+ ) -> dict[str, Any]:
2998
+ scenarios = _scenario_index(phase1.load_assets()[0])
2999
+ codebook_raw = _read_file(CODEBOOK)
3000
+ source_sha = _sha(_json_bytes(_source_ingestion_manifest(manifest)))
3001
+ packets: list[tuple[str, bytes]] = []
3002
+ inventory_rows: list[dict[str, str]] = []
3003
+ mapping: list[dict[str, Any]] = []
3004
+ for cell, state in zip(plan["cells"], manifest["cells"]):
3005
+ raw = _read_file(run_dir / state["transcript_ref"])
3006
+ if _sha(raw) != state["transcript_sha256"]:
3007
+ _fail(f"transcript drift before blinding: {cell['cell_id']}")
3008
+ transcript = _strict_loads(raw)
3009
+ _assert_blindable_transcript(plan, transcript)
3010
+ blind_id = _deterministic_blind_id(
3011
+ plan_sha,
3012
+ source_sha,
3013
+ cell["cell_id"],
3014
+ private_id_nonce,
3015
+ )
3016
+ isolated_packet = _blind_packet_value(
3017
+ blind_id, scenarios[cell["scenario_id"]], transcript, codebook_raw
3018
+ )
3019
+ _assert_blind_packet_structure(plan, isolated_packet)
3020
+ _validate_schema(BLIND_PACKET_SCHEMA, isolated_packet, "isolated blind packet")
3021
+ packet_raw = _json_bytes(isolated_packet)
3022
+ packets.append((blind_id, packet_raw))
3023
+ inventory_rows.append(
3024
+ {"blind_session_id": blind_id, "packet_sha256": _sha(packet_raw)}
3025
+ )
3026
+ mapping.append(
3027
+ {
3028
+ "blind_session_id": blind_id,
3029
+ "cell_id": cell["cell_id"],
3030
+ "experiment_id": cell["experiment_id"],
3031
+ "arm_id": cell["arm_id"],
3032
+ "scenario_id": cell["scenario_id"],
3033
+ "pair_id": cell["pair_id"],
3034
+ "replicate": cell["replicate"],
3035
+ }
3036
+ )
3037
+ packets.sort(key=lambda row: row[0])
3038
+ inventory_rows.sort(key=lambda row: row["blind_session_id"])
3039
+ mapping.sort(key=lambda row: row["blind_session_id"])
3040
+ if len({blind_id for blind_id, _ in packets}) != 48:
3041
+ _fail("deterministic blind ids collided")
3042
+ inventory = _blind_inventory_value(plan_sha, inventory_rows)
3043
+ _validate_schema(BLIND_INVENTORY_SCHEMA, inventory, "blind inventory")
3044
+ inventory_raw = _json_bytes(inventory)
3045
+ private_map = _private_map_value(plan_sha, inventory_raw, mapping)
3046
+ _validate_schema(PRIVATE_ARM_MAP_SCHEMA, private_map, "private arm map")
3047
+ private_map_raw = _json_bytes(private_map)
3048
+ blind_manifest = _blind_manifest_value(
3049
+ plan_sha,
3050
+ source_sha,
3051
+ inventory_raw,
3052
+ packets,
3053
+ private_map_raw,
3054
+ )
3055
+ _validate_schema(BLIND_MANIFEST_SCHEMA, blind_manifest, "blind manifest")
3056
+ blind_manifest_raw = _json_bytes(blind_manifest)
3057
+ intent = _blind_intent_value(
3058
+ plan_sha,
3059
+ source_sha,
3060
+ private_id_nonce,
3061
+ inventory_raw,
3062
+ packets,
3063
+ private_map_raw,
3064
+ blind_manifest_raw,
3065
+ )
3066
+ _validate_schema(BLIND_INTENT_SCHEMA, intent, "blind intent")
3067
+ expected_files = {
3068
+ **{
3069
+ f"sessions/{blind_id}.json": packet_raw
3070
+ for blind_id, packet_raw in packets
3071
+ },
3072
+ "inventory.json": inventory_raw,
3073
+ "private/arm-map.json": private_map_raw,
3074
+ "manifest.json": blind_manifest_raw,
3075
+ }
3076
+ return {
3077
+ "intent": intent,
3078
+ "intent_raw": _json_bytes(intent),
3079
+ "expected_staging_files": expected_files,
3080
+ "blind_manifest_sha256": _sha(blind_manifest_raw),
3081
+ "inventory_sha256": _sha(inventory_raw),
3082
+ }
3083
+
3084
+
3085
+ def _validate_blind_staging(
3086
+ staging: Path,
3087
+ expected_files: dict[str, bytes],
3088
+ *,
3089
+ require_complete: bool,
3090
+ ) -> None:
3091
+ if staging.is_symlink() or not staging.is_dir():
3092
+ _fail("blind staging must be a real non-symlink directory")
3093
+ observed_files, observed_directories = _relative_files_and_directories(staging)
3094
+ allowed_files = set(expected_files)
3095
+ allowed_directories = _parent_directories(allowed_files)
3096
+ if not observed_files <= allowed_files or not observed_directories <= allowed_directories:
3097
+ _fail("blind staging contains an unbound artifact or directory")
3098
+ if require_complete and (
3099
+ observed_files != allowed_files or observed_directories != allowed_directories
3100
+ ):
3101
+ _fail("blind staging transaction is incomplete")
3102
+ for ref in observed_files:
3103
+ if _read_file(staging / ref) != expected_files[ref]:
3104
+ _fail(f"blind staging artifact collision at {ref}")
3105
+ private_directory = staging / "private"
3106
+ private_map_path = private_directory / "arm-map.json"
3107
+ if private_directory.exists() and stat.S_IMODE(private_directory.stat().st_mode) != 0o700:
3108
+ _fail("blind staging private directory mode must be 0700")
3109
+ if private_map_path.exists() and stat.S_IMODE(private_map_path.stat().st_mode) != 0o600:
3110
+ _fail("blind staging private map mode must be 0600")
3111
+
3112
+
3113
+ def _validate_blind_transaction(
3114
+ run_dir: Path,
3115
+ plan: dict[str, Any],
3116
+ plan_sha: str,
3117
+ manifest: dict[str, Any],
3118
+ ) -> dict[str, Any]:
3119
+ intent_path = run_dir / BLIND_INTENT_REF
3120
+ if not intent_path.exists():
3121
+ if (run_dir / BLIND_STAGING_REF).exists():
3122
+ _fail("blind staging exists without its durable transaction intent")
3123
+ return {"present": False}
3124
+ if manifest["stopped"] or manifest["status"] not in {
3125
+ "complete",
3126
+ "blind_finalized",
3127
+ }:
3128
+ _fail("blind transaction intent is forbidden before complete ingestion")
3129
+ intent_raw = _read_file(intent_path)
3130
+ intent = _strict_loads(intent_raw)
3131
+ _validate_schema(BLIND_INTENT_SCHEMA, intent, "blind intent")
3132
+ if stat.S_IMODE(intent_path.stat().st_mode) != 0o600:
3133
+ _fail("blind transaction intent mode must remain 0600")
3134
+ values = _blind_transaction_values(
3135
+ run_dir,
3136
+ plan,
3137
+ plan_sha,
3138
+ manifest,
3139
+ intent["private_id_nonce"],
3140
+ )
3141
+ if intent_raw != values["intent_raw"]:
3142
+ _fail("blind transaction intent differs from deterministic exact replay")
3143
+ staging = run_dir / BLIND_STAGING_REF
3144
+ final = run_dir / "blind"
3145
+ if staging.exists() and final.exists():
3146
+ _fail("blind transaction has both staging and finalized directories")
3147
+ if staging.exists():
3148
+ _validate_blind_staging(
3149
+ staging,
3150
+ values["expected_staging_files"],
3151
+ require_complete=False,
3152
+ )
3153
+ if final.exists():
3154
+ final_manifest_raw = _read_file(final / "manifest.json")
3155
+ if _sha(final_manifest_raw) != values["blind_manifest_sha256"]:
3156
+ _fail("published blind bundle differs from its durable transaction intent")
3157
+ final_inventory_raw = _read_file(final / "inventory.json")
3158
+ if _sha(final_inventory_raw) != values["inventory_sha256"]:
3159
+ _fail("published blind inventory differs from its durable transaction intent")
3160
+ _validate_blind_aliases(
3161
+ run_dir,
3162
+ values["expected_staging_files"],
3163
+ final_published=final.exists(),
3164
+ )
3165
+ return {
3166
+ **values,
3167
+ "present": True,
3168
+ "staging_present": staging.exists(),
3169
+ "final_present": final.exists(),
3170
+ }
3171
+
3172
+
3173
+ def _source_ingestion_manifest(manifest: dict[str, Any]) -> dict[str, Any]:
3174
+ source = copy.deepcopy(manifest)
3175
+ source["status"] = "complete"
3176
+ source["blind_manifest_ref"] = None
3177
+ source["blind_manifest_sha256"] = None
3178
+ return source
3179
+
3180
+
3181
+ def _validate_blind_bundle(
3182
+ run_dir: Path,
3183
+ plan: dict[str, Any],
3184
+ plan_sha: str,
3185
+ manifest: dict[str, Any],
3186
+ ) -> tuple[str, str]:
3187
+ blind_manifest_raw = _read_file(run_dir / "blind/manifest.json")
3188
+ blind_manifest_sha = _sha(blind_manifest_raw)
3189
+ blind_manifest = _strict_loads(blind_manifest_raw)
3190
+ _validate_schema(BLIND_MANIFEST_SCHEMA, blind_manifest, "blind manifest")
3191
+ if blind_manifest["run_plan_sha256"] != plan_sha:
3192
+ _fail("blind manifest is bound to a different run plan")
3193
+ source_sha = _sha(_json_bytes(_source_ingestion_manifest(manifest)))
3194
+ if blind_manifest["source_ingestion_manifest_sha256"] != source_sha:
3195
+ _fail("blind manifest source ingestion state drifted")
3196
+ if manifest["status"] == "blind_finalized" and (
3197
+ manifest["blind_manifest_ref"], manifest["blind_manifest_sha256"]
3198
+ ) != ("blind/manifest.json", blind_manifest_sha):
3199
+ _fail("finalized ingestion state disagrees with blind manifest bytes")
3200
+
3201
+ expected_bundle_files = {
3202
+ "manifest.json",
3203
+ str(Path(blind_manifest["inventory_ref"]).relative_to("blind")),
3204
+ str(Path(blind_manifest["private_map_ref"]).relative_to("blind")),
3205
+ }
3206
+ expected_bundle_files.update(
3207
+ str(Path(row["packet_ref"]).relative_to("blind"))
3208
+ for row in blind_manifest["packets"]
3209
+ )
3210
+ observed_files, observed_directories = _relative_files_and_directories(
3211
+ run_dir / "blind"
3212
+ )
3213
+ if observed_files != expected_bundle_files or observed_directories != _parent_directories(
3214
+ expected_bundle_files
3215
+ ):
3216
+ _fail("blind bundle file/directory inventory is not exact")
3217
+
3218
+ inventory_raw = _read_file(run_dir / blind_manifest["inventory_ref"])
3219
+ if _sha(inventory_raw) != blind_manifest["inventory_sha256"]:
3220
+ _fail("blind inventory hash drift")
3221
+ inventory = _strict_loads(inventory_raw)
3222
+ _validate_schema(BLIND_INVENTORY_SCHEMA, inventory, "blind inventory")
3223
+
3224
+ private_map_path = run_dir / blind_manifest["private_map_ref"]
3225
+ private_map_raw = _read_file(private_map_path)
3226
+ if _sha(private_map_raw) != blind_manifest["private_map_sha256"]:
3227
+ _fail("private arm-map hash drift")
3228
+ if stat.S_IMODE(private_map_path.parent.stat().st_mode) != 0o700:
3229
+ _fail("private arm-map directory mode must remain 0700")
3230
+ if stat.S_IMODE(private_map_path.stat().st_mode) != 0o600:
3231
+ _fail("private arm-map file mode must remain 0600")
3232
+ private_map = _strict_loads(private_map_raw)
3233
+ _validate_schema(PRIVATE_ARM_MAP_SCHEMA, private_map, "private arm map")
3234
+ mapping = private_map.get("mapping")
3235
+ if not isinstance(mapping, list) or len(mapping) != 48:
3236
+ _fail("private arm map must contain exactly 48 mappings")
3237
+ mapping_by_id = {row.get("blind_session_id"): row for row in mapping}
3238
+ if None in mapping_by_id or len(mapping_by_id) != 48:
3239
+ _fail("private arm map blind ids must be unique")
3240
+
3241
+ scenarios = _scenario_index(phase1.load_assets()[0])
3242
+ codebook_raw = _read_file(CODEBOOK)
3243
+ packet_pairs: list[tuple[str, bytes]] = []
3244
+ inventory_rows: list[dict[str, str]] = []
3245
+ expected_mapping: list[dict[str, Any]] = []
3246
+ plan_by_cell = {cell["cell_id"]: cell for cell in plan["cells"]}
3247
+ state_by_cell = {row["cell_id"]: row for row in manifest["cells"]}
3248
+ for packet_row in blind_manifest["packets"]:
3249
+ blind_id = packet_row["blind_session_id"]
3250
+ mapped = mapping_by_id.get(blind_id)
3251
+ if mapped is None or mapped.get("cell_id") not in plan_by_cell:
3252
+ _fail(f"blind mapping is missing or invalid for {blind_id}")
3253
+ cell = plan_by_cell[mapped["cell_id"]]
3254
+ expected_map_row = {
3255
+ "blind_session_id": blind_id,
3256
+ "cell_id": cell["cell_id"],
3257
+ "experiment_id": cell["experiment_id"],
3258
+ "arm_id": cell["arm_id"],
3259
+ "scenario_id": cell["scenario_id"],
3260
+ "pair_id": cell["pair_id"],
3261
+ "replicate": cell["replicate"],
3262
+ }
3263
+ if mapped != expected_map_row:
3264
+ _fail(f"private arm mapping drift for {blind_id}")
3265
+ transcript_raw = _read_file(
3266
+ run_dir / state_by_cell[cell["cell_id"]]["transcript_ref"]
3267
+ )
3268
+ transcript = _strict_loads(transcript_raw)
3269
+ _assert_blindable_transcript(plan, transcript)
3270
+ expected_packet = _blind_packet_value(
3271
+ blind_id,
3272
+ scenarios[cell["scenario_id"]],
3273
+ transcript,
3274
+ codebook_raw,
3275
+ )
3276
+ _assert_blind_packet_structure(plan, expected_packet)
3277
+ expected_packet_raw = _json_bytes(expected_packet)
3278
+ packet_raw = _read_file(run_dir / packet_row["packet_ref"])
3279
+ if packet_raw != expected_packet_raw or _sha(packet_raw) != packet_row[
3280
+ "packet_sha256"
3281
+ ]:
3282
+ _fail(f"blind packet replay/hash drift for {blind_id}")
3283
+ packet_pairs.append((blind_id, packet_raw))
3284
+ inventory_rows.append(
3285
+ {"blind_session_id": blind_id, "packet_sha256": _sha(packet_raw)}
3286
+ )
3287
+ expected_mapping.append(expected_map_row)
3288
+
3289
+ packet_pairs.sort(key=lambda row: row[0])
3290
+ inventory_rows.sort(key=lambda row: row["blind_session_id"])
3291
+ expected_mapping.sort(key=lambda row: row["blind_session_id"])
3292
+ if inventory != _blind_inventory_value(plan_sha, inventory_rows):
3293
+ _fail("blind inventory replay drift")
3294
+ expected_private_map = _private_map_value(
3295
+ plan_sha, inventory_raw, expected_mapping
3296
+ )
3297
+ if private_map != expected_private_map:
3298
+ _fail("private arm-map replay drift")
3299
+ expected_blind_manifest = _blind_manifest_value(
3300
+ plan_sha,
3301
+ source_sha,
3302
+ inventory_raw,
3303
+ packet_pairs,
3304
+ private_map_raw,
3305
+ )
3306
+ if blind_manifest != expected_blind_manifest:
3307
+ _fail("blind manifest replay drift")
3308
+ return blind_manifest_sha, _sha(inventory_raw)
3309
+
3310
+
3311
+ def _finalize_blind_state(
3312
+ run_dir: Path,
3313
+ plan: dict[str, Any],
3314
+ plan_sha: str,
3315
+ manifest: dict[str, Any],
3316
+ blind_manifest_sha: str,
3317
+ ) -> dict[str, Any]:
3318
+ finalized = copy.deepcopy(manifest)
3319
+ finalized["status"] = "blind_finalized"
3320
+ finalized["blind_manifest_ref"] = "blind/manifest.json"
3321
+ finalized["blind_manifest_sha256"] = blind_manifest_sha
3322
+ _validate_manifest(finalized, plan, plan_sha)
3323
+ _replace_json(run_dir / "ingestion-manifest.json", finalized)
3324
+ return finalized
3325
+
3326
+
3327
+ def prepare_blind_packet(args: argparse.Namespace) -> dict[str, Any]:
3328
+ plan, _, plan_sha, manifest = _load_run(args.run_dir, args.plan_sha256)
3329
+ _recover_completed_attempts(args.run_dir, plan, manifest)
3330
+ _validate_materials(args.run_dir, plan, required=True)
3331
+ _validate_ingested_artifacts(args.run_dir, plan, plan_sha, manifest)
3332
+ if manifest["status"] == "blind_finalized":
3333
+ _fail("blind packet is already finalized and write-once")
3334
+ if manifest["status"] != "complete" or manifest["stopped"]:
3335
+ _fail("blind packet requires 48 complete, unstopped transcript ingestions")
3336
+
3337
+ blind_root = args.run_dir / "blind"
3338
+ intent_path = args.run_dir / BLIND_INTENT_REF
3339
+ intent_preexisting = intent_path.exists()
3340
+ if not intent_preexisting:
3341
+ _validate_run_inventory(args.run_dir, plan, manifest)
3342
+ private_id_nonce = secrets.token_hex(32)
3343
+ else:
3344
+ existing_intent = _strict_loads(_read_file(intent_path))
3345
+ _validate_schema(BLIND_INTENT_SCHEMA, existing_intent, "blind intent")
3346
+ private_id_nonce = existing_intent["private_id_nonce"]
3347
+ values = _blind_transaction_values(
3348
+ args.run_dir,
3349
+ plan,
3350
+ plan_sha,
3351
+ manifest,
3352
+ private_id_nonce,
3353
+ )
3354
+ _ensure_exact_new(intent_path, values["intent_raw"], mode=0o600)
3355
+ if intent_preexisting:
3356
+ if stat.S_IMODE(intent_path.stat().st_mode) != 0o600:
3357
+ _fail("blind transaction intent mode must remain 0600")
3358
+ else:
3359
+ os.chmod(intent_path, 0o600)
3360
+ alias_root = args.run_dir / BLIND_ALIAS_ROOT
3361
+ if not alias_root.exists():
3362
+ alias_root.mkdir(mode=0o700)
3363
+ _fsync_directory(args.run_dir)
3364
+ os.chmod(alias_root, 0o700)
3365
+ transaction = _validate_blind_transaction(
3366
+ args.run_dir, plan, plan_sha, manifest
3367
+ )
3368
+ staging = args.run_dir / BLIND_STAGING_REF
3369
+ if not blind_root.exists():
3370
+ _validate_run_inventory(args.run_dir, plan, manifest)
3371
+ recovered = bool(
3372
+ intent_preexisting or transaction["staging_present"] or blind_root.exists()
3373
+ )
3374
+ if not blind_root.exists():
3375
+ if not staging.exists():
3376
+ staging.mkdir(mode=0o755)
3377
+ _fsync_directory(args.run_dir)
3378
+ _validate_blind_staging(
3379
+ staging,
3380
+ values["expected_staging_files"],
3381
+ require_complete=False,
3382
+ )
3383
+ expected_staging_files = values["expected_staging_files"]
3384
+ ordered_refs = [
3385
+ *sorted(ref for ref in expected_staging_files if ref.startswith("sessions/")),
3386
+ "inventory.json",
3387
+ "private/arm-map.json",
3388
+ "manifest.json",
3389
+ ]
3390
+ for ref in ordered_refs:
3391
+ raw = expected_staging_files[ref]
3392
+ target = staging / ref
3393
+ if ref == "private/arm-map.json":
3394
+ target.parent.mkdir(mode=0o700, exist_ok=True)
3395
+ os.chmod(target.parent, 0o700)
3396
+ _ensure_blind_exact(args.run_dir, ref, raw)
3397
+ os.chmod(target, 0o600)
3398
+ else:
3399
+ _ensure_blind_exact(args.run_dir, ref, raw)
3400
+ _validate_blind_staging(
3401
+ staging,
3402
+ values["expected_staging_files"],
3403
+ require_complete=True,
3404
+ )
3405
+ try:
3406
+ os.rename(staging, blind_root)
3407
+ _fsync_directory(args.run_dir)
3408
+ except OSError as exc:
3409
+ raise EnvelopeError(
3410
+ f"cannot atomically finalize deterministic blind bundle: {exc}"
3411
+ ) from exc
3412
+
3413
+ blind_manifest_sha, inventory_sha = _validate_blind_bundle(
3414
+ args.run_dir, plan, plan_sha, manifest
3415
+ )
3416
+ finalized = _finalize_blind_state(
3417
+ args.run_dir, plan, plan_sha, manifest, blind_manifest_sha
3418
+ )
3419
+ _validate_run_inventory(args.run_dir, plan, finalized)
3420
+ return {
3421
+ "inventory_sha256": inventory_sha,
3422
+ "sessions": 48,
3423
+ "artifact_presence": _artifact_presence(),
3424
+ "state": "blind_finalized",
3425
+ "recovered_existing_atomic_bundle": recovered,
3426
+ }
3427
+
3428
+
3429
+ def _parser() -> argparse.ArgumentParser:
3430
+ parser = argparse.ArgumentParser(description=__doc__)
3431
+ sub = parser.add_subparsers(dest="command", required=True)
3432
+ init = sub.add_parser("init-run", help="freeze a 48-cell no-call run plan")
3433
+ init.add_argument("--run-dir", type=Path, required=True)
3434
+ init.add_argument("--run-id", required=True)
3435
+ init.add_argument(
3436
+ "--suite-commit",
3437
+ required=True,
3438
+ help="operator-declared 40-hex provenance; existence is not verified",
3439
+ )
3440
+ init.add_argument("--order-seed", required=True)
3441
+ init.add_argument("--subject-provider", required=True)
3442
+ init.add_argument("--subject-model", required=True)
3443
+ init.add_argument("--subject-runtime", required=True)
3444
+ init.add_argument("--subject-runtime-version", required=True)
3445
+ init.add_argument("--auth-mode", required=True)
3446
+ init.add_argument("--reasoning-effort", required=True)
3447
+ init.add_argument(
3448
+ "--input-token-cap",
3449
+ type=int,
3450
+ required=True,
3451
+ help="operator-declared cap; this no-call runner does not verify usage",
3452
+ )
3453
+ init.add_argument(
3454
+ "--output-token-cap",
3455
+ type=int,
3456
+ required=True,
3457
+ help="operator-declared cap; this no-call runner does not verify usage",
3458
+ )
3459
+ materialize_parser = sub.add_parser(
3460
+ "materialize",
3461
+ help="materialize prompt, actor, and operator packets only",
3462
+ )
3463
+ validate_parser = sub.add_parser(
3464
+ "validate",
3465
+ help="validate frozen plan, materials, and ingested hashes",
3466
+ )
3467
+ blind_parser = sub.add_parser(
3468
+ "prepare-blind-packet",
3469
+ help="prepare isolated unlabeled arm-blind packets",
3470
+ )
3471
+ for command_parser in (materialize_parser, validate_parser, blind_parser):
3472
+ command_parser.add_argument("--run-dir", type=Path, required=True)
3473
+ command_parser.add_argument("--plan-sha256", required=True)
3474
+ ingest_parser = sub.add_parser("ingest", help="ingest one external transcript in frozen order")
3475
+ ingest_parser.add_argument("--run-dir", type=Path, required=True)
3476
+ ingest_parser.add_argument("--plan-sha256", required=True)
3477
+ ingest_parser.add_argument("--transcript", type=Path, required=True)
3478
+ ingest_parser.add_argument("--authorization-record", type=Path, required=True)
3479
+ return parser
3480
+
3481
+
3482
+ def main(argv: list[str] | None = None) -> int:
3483
+ args = _parser().parse_args(argv)
3484
+ try:
3485
+ if args.command == "init-run":
3486
+ result = init_run(args)
3487
+ elif args.command == "materialize":
3488
+ result = materialize(args)
3489
+ elif args.command == "validate":
3490
+ result = validate_run(args)
3491
+ elif args.command == "ingest":
3492
+ result = ingest(args)
3493
+ elif args.command == "prepare-blind-packet":
3494
+ result = prepare_blind_packet(args)
3495
+ else:
3496
+ raise AssertionError(f"unhandled command {args.command!r}")
3497
+ except EnvelopeError as exc:
3498
+ print(f"{SUITE}: FAIL: {exc}", file=sys.stderr)
3499
+ return 2
3500
+ print(json.dumps(result, ensure_ascii=False, sort_keys=True))
3501
+ return 0
3502
+
3503
+
3504
+ if __name__ == "__main__":
3505
+ raise SystemExit(main())