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,2310 @@
1
+ #!/usr/bin/env python3
2
+ """Build a deterministic, pointer-only human-subjects packet manifest.
3
+
4
+ This checker observes only explicitly inventoried regular files. It does not
5
+ scan a packet, interpret document prose, choose an authority, determine a
6
+ review pathway, verify institutional acceptance, or grant authorization.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import copy
12
+ import errno
13
+ import hashlib
14
+ import json
15
+ import os
16
+ import re
17
+ import stat
18
+ import sys
19
+ import unicodedata
20
+ from datetime import date
21
+ from pathlib import Path, PurePosixPath
22
+ from typing import Any, NoReturn
23
+
24
+ try: # Direct script execution puts scripts/ on sys.path.
25
+ from resolve_human_subjects_authority import (
26
+ ContractError,
27
+ load_json,
28
+ validate_resolved_context,
29
+ )
30
+ except ModuleNotFoundError: # Package-style imports used by some test runners.
31
+ from scripts.resolve_human_subjects_authority import ( # type: ignore[no-redef]
32
+ ContractError,
33
+ load_json,
34
+ validate_resolved_context,
35
+ )
36
+
37
+
38
+ INVENTORY_SCHEMA_VERSION = "submission-packet-inventory/1.0"
39
+ MANIFEST_SCHEMA_VERSION = "submission-packet-manifest/1.0"
40
+ BOUNDARY_FOOTER = (
41
+ "Human-subjects boundary: This output does not authorize recruitment, "
42
+ "consent, access to identifiable data, intervention, or data collection."
43
+ )
44
+ REVIEW_PATHWAY = "institutional determination required"
45
+ REVIEW_TIMELINE = "unknown — obtain current institutional estimate"
46
+ ACCEPTANCE_REASON = "INSTITUTIONAL_DETERMINATION_REQUIRED"
47
+
48
+ STATUSES = {
49
+ "DOCUMENTED",
50
+ "NOT_LOCATED",
51
+ "CONFLICTING",
52
+ "APPLICABILITY_UNRESOLVED",
53
+ "ACCEPTANCE_UNVERIFIED",
54
+ }
55
+ READINESS_VALUES = {"gaps_located", "no_listed_gaps_located", "unresolved"}
56
+ AUTHORIZATION_VALUES = {"documented", "not_provided", "cannot_verify"}
57
+ GLOBAL_REASON_CODES = {
58
+ "AUTHORITY_INPUT_NOT_PROVIDED",
59
+ "AUTHORITY_RESOLUTION_NOT_PERMITTED",
60
+ "V1_ENVELOPE_FACT_MISSING",
61
+ "V1_ENVELOPE_FACT_UNKNOWN",
62
+ "V1_ENVELOPE_OUTSIDE",
63
+ "OVERLAY_SELECTION_NOT_PROVIDED",
64
+ }
65
+ ENTRY_REASON_ORDER = (
66
+ "STRUCTURE_DOCUMENTED",
67
+ "EXTERNAL_HOLDER_DEPENDENCY",
68
+ "WAIVER_OR_EXCEPTION_CLAIM_UNVERIFIED",
69
+ "EXPECTED_PACKET_ARTIFACT_NOT_LOCATED",
70
+ "DUPLICATE_EVIDENCE_BINDING",
71
+ "DECLARED_ATTACHMENT_NOT_LOCATED",
72
+ "ARTIFACT_DIGEST_MISMATCH",
73
+ "ARTIFACT_SIZE_MISMATCH",
74
+ "ARTIFACT_TYPE_MISMATCH",
75
+ "ARTIFACT_HOLDER_MISMATCH",
76
+ "CERTIFICATE_HOLDER_MISMATCH",
77
+ "DECLARED_STRUCTURE_CONFLICT",
78
+ )
79
+ ENTRY_REASON_CODES = set(ENTRY_REASON_ORDER)
80
+ OBSERVATION_REASON_CODES = {
81
+ "STRUCTURE_DOCUMENTED",
82
+ "DECLARED_ATTACHMENT_NOT_LOCATED",
83
+ "ARTIFACT_DIGEST_MISMATCH",
84
+ "ARTIFACT_SIZE_MISMATCH",
85
+ "CERTIFICATE_HOLDER_MISMATCH",
86
+ "DECLARED_STRUCTURE_CONFLICT",
87
+ }
88
+ ENVELOPE_FACTS: tuple[tuple[str, bool], ...] = (
89
+ ("packet_v1.non_clinical", True),
90
+ ("packet_v1.single_institution", True),
91
+ ("packet_v1.competent_adults_only", True),
92
+ ("packet_v1.biospecimens_involved", False),
93
+ ("packet_v1.regulated_clinical_trial", False),
94
+ ("packet_v1.cross_border_material_transfer", False),
95
+ ("packet_v1.multisite_reliance", False),
96
+ )
97
+
98
+ ID_RE = re.compile(r"^[a-z0-9][a-z0-9._-]*$")
99
+ VERSION_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$")
100
+ SHA_RE = re.compile(r"^[0-9a-f]{64}$")
101
+ MEDIA_TYPE_RE = re.compile(r"^[A-Za-z0-9!#$&^_.+-]+/[A-Za-z0-9!#$&^_.+-]+$")
102
+ MAX_ARTIFACT_BYTES = 64 * 1024 * 1024
103
+ MAX_TOTAL_BYTES = 256 * 1024 * 1024
104
+ MAX_ITEMS = 512
105
+ MAX_MANIFEST_ENTRIES = 4096
106
+ MAX_EXCLUDED_REQUIREMENTS = 4096
107
+ MAX_CONSUMER_SCOPES = 512
108
+ MAX_MANIFEST_BYTES = 8 * 1024 * 1024
109
+ DESCRIPTOR_RELATIVE_OPEN_SUPPORTED = (
110
+ os.name == "posix"
111
+ and hasattr(os, "O_NOFOLLOW")
112
+ and hasattr(os, "O_DIRECTORY")
113
+ and os.open in os.supports_dir_fd
114
+ )
115
+
116
+
117
+ def _fail(path: str, message: str) -> NoReturn:
118
+ raise ContractError(f"{path}: {message}")
119
+
120
+
121
+ def _closed(value: Any, path: str, fields: set[str]) -> dict[str, Any]:
122
+ if not isinstance(value, dict):
123
+ _fail(path, "must be an object")
124
+ missing = fields - set(value)
125
+ extra = set(value) - fields
126
+ if missing:
127
+ _fail(path, f"missing field(s): {', '.join(sorted(missing))}")
128
+ if extra:
129
+ _fail(path, f"undeclared field(s): {', '.join(sorted(extra))}")
130
+ return value
131
+
132
+
133
+ def _array(
134
+ value: Any,
135
+ path: str,
136
+ *,
137
+ maximum: int = MAX_ITEMS,
138
+ minimum: int = 0,
139
+ ) -> list[Any]:
140
+ if not isinstance(value, list):
141
+ _fail(path, "must be an array")
142
+ if len(value) < minimum:
143
+ _fail(path, f"must contain at least {minimum} item(s)")
144
+ if len(value) > maximum:
145
+ _fail(path, f"exceeds {maximum} items")
146
+ return value
147
+
148
+
149
+ def _text(value: Any, path: str, *, maximum: int = 1024) -> str:
150
+ if not isinstance(value, str) or not value:
151
+ _fail(path, "must be a non-empty string")
152
+ if len(value) > maximum:
153
+ _fail(path, f"must not exceed {maximum} characters")
154
+ if any(
155
+ unicodedata.category(ch) in {"Cc", "Cf", "Cs"}
156
+ or "\U00013430" <= ch <= "\U0001345f"
157
+ for ch in value
158
+ ):
159
+ _fail(path, "must not contain control, format, or surrogate characters")
160
+ return value
161
+
162
+
163
+ def _identifier(value: Any, path: str) -> str:
164
+ text = _text(value, path, maximum=200)
165
+ if ID_RE.fullmatch(text) is None:
166
+ _fail(path, "must use lowercase letters, digits, dot, underscore, or hyphen")
167
+ return text
168
+
169
+
170
+ def _version(value: Any, path: str) -> str:
171
+ text = _text(value, path, maximum=200)
172
+ if VERSION_RE.fullmatch(text) is None:
173
+ _fail(path, "has an invalid version token")
174
+ return text
175
+
176
+
177
+ def _sha(value: Any, path: str, *, nullable: bool = False) -> str | None:
178
+ if nullable and value is None:
179
+ return None
180
+ text = _text(value, path, maximum=64)
181
+ if SHA_RE.fullmatch(text) is None:
182
+ _fail(path, "must be 64 lowercase hexadecimal characters")
183
+ return text
184
+
185
+
186
+ def _iso_date(value: Any, path: str, *, nullable: bool = False) -> date | None:
187
+ if nullable and value is None:
188
+ return None
189
+ text = _text(value, path, maximum=10)
190
+ try:
191
+ if re.fullmatch(r"[0-9]{4}-[0-9]{2}-[0-9]{2}", text) is None:
192
+ raise ValueError
193
+ return date.fromisoformat(text)
194
+ except ValueError as exc:
195
+ raise ContractError(f"{path}: must be a real ISO 8601 date") from exc
196
+
197
+
198
+ def _unique_identifiers(
199
+ value: Any,
200
+ path: str,
201
+ *,
202
+ maximum: int = MAX_ITEMS,
203
+ minimum: int = 0,
204
+ ) -> list[str]:
205
+ rows = _array(value, path, maximum=maximum, minimum=minimum)
206
+ result: list[str] = []
207
+ seen: set[str] = set()
208
+ for index, item in enumerate(rows):
209
+ item_id = _identifier(item, f"{path}[{index}]")
210
+ if item_id in seen:
211
+ _fail(path, f"duplicate identifier: {item_id}")
212
+ seen.add(item_id)
213
+ result.append(item_id)
214
+ return result
215
+
216
+
217
+ def _relative_path(value: Any, path: str) -> str:
218
+ text = _text(value, path, maximum=1024)
219
+ if "\\" in text:
220
+ _fail(path, "must use POSIX separators")
221
+ if text in {".", ".."}:
222
+ _fail(path, "must name a file below the packet root")
223
+ if re.match(r"^[A-Za-z]:", text):
224
+ _fail(path, "must not use a drive-qualified path")
225
+ pure = PurePosixPath(text)
226
+ if pure.is_absolute():
227
+ _fail(path, "must be packet-relative")
228
+ if text.startswith("/") or text.endswith("/") or "//" in text:
229
+ _fail(path, "must be a canonical POSIX relative path")
230
+ if any(part in {"", ".", ".."} for part in pure.parts):
231
+ _fail(path, "must not contain empty, dot, or dot-dot components")
232
+ if pure.as_posix() != text:
233
+ _fail(path, "must be a normalized POSIX relative path")
234
+ return text
235
+
236
+
237
+ def _canonical_bytes(value: Any) -> bytes:
238
+ try:
239
+ return json.dumps(
240
+ value,
241
+ ensure_ascii=False,
242
+ sort_keys=True,
243
+ separators=(",", ":"),
244
+ allow_nan=False,
245
+ ).encode("utf-8")
246
+ except (TypeError, ValueError, UnicodeError, RecursionError) as exc:
247
+ raise ContractError(f"value is not canonical JSON: {exc}") from exc
248
+
249
+
250
+ def _enforce_manifest_byte_limit(manifest: dict[str, Any]) -> None:
251
+ manifest_size = len(_canonical_bytes(manifest))
252
+ if manifest_size > MAX_MANIFEST_BYTES:
253
+ _fail(
254
+ "manifest",
255
+ f"canonical JSON exceeds the {MAX_MANIFEST_BYTES}-byte V1 limit",
256
+ )
257
+
258
+
259
+ def _digest(value: Any) -> str:
260
+ return hashlib.sha256(_canonical_bytes(value)).hexdigest()
261
+
262
+
263
+ def _semantic_inventory(inventory: dict[str, Any]) -> dict[str, Any]:
264
+ """Normalize arrays whose inventory meaning is explicitly order-insensitive."""
265
+ normalized = copy.deepcopy(inventory)
266
+ normalized["packet_responsibility_role_ids"].sort()
267
+ for claim in normalized["waiver_or_exception_claims"]:
268
+ claim["route_requirement_ids"].sort()
269
+ normalized["waiver_or_exception_claims"].sort(
270
+ key=lambda row: (row["requirement_id"], row["decision_artifact_id"] or "")
271
+ )
272
+ for artifact in normalized["artifacts"]:
273
+ artifact["evidence_bindings"].sort(
274
+ key=lambda row: (row["requirement_id"], row["evidence_id"])
275
+ )
276
+ artifact["declared_structure"]["signature_blocks"].sort(
277
+ key=lambda row: (row["role_id"], row["state"])
278
+ )
279
+ normalized["artifacts"].sort(
280
+ key=lambda row: (row["artifact_id"], row["relative_path"])
281
+ )
282
+ return normalized
283
+
284
+
285
+ def validate_submission_packet_inventory(inventory: dict[str, Any]) -> None:
286
+ """Validate the closed submission-packet inventory contract."""
287
+ root = _closed(
288
+ inventory,
289
+ "inventory",
290
+ {
291
+ "schema_version",
292
+ "inventory_id",
293
+ "declared_by",
294
+ "packet_responsibility_role_ids",
295
+ "authorization_status_input",
296
+ "waiver_or_exception_claims",
297
+ "artifacts",
298
+ },
299
+ )
300
+ if root["schema_version"] != INVENTORY_SCHEMA_VERSION:
301
+ _fail("inventory.schema_version", f"must equal {INVENTORY_SCHEMA_VERSION}")
302
+ _identifier(root["inventory_id"], "inventory.inventory_id")
303
+ if root["declared_by"] != "author":
304
+ _fail("inventory.declared_by", "must equal author")
305
+ _unique_identifiers(
306
+ root["packet_responsibility_role_ids"],
307
+ "inventory.packet_responsibility_role_ids",
308
+ maximum=128,
309
+ minimum=1,
310
+ )
311
+
312
+ authorization = _closed(
313
+ root["authorization_status_input"],
314
+ "inventory.authorization_status_input",
315
+ {"value", "source_reference", "provenance"},
316
+ )
317
+ if authorization["value"] not in AUTHORIZATION_VALUES:
318
+ _fail(
319
+ "inventory.authorization_status_input.value",
320
+ "must be documented, not_provided, or cannot_verify",
321
+ )
322
+ if authorization["provenance"] != "caller_supplied_no_derivation":
323
+ _fail(
324
+ "inventory.authorization_status_input.provenance",
325
+ "must equal caller_supplied_no_derivation",
326
+ )
327
+ if authorization["value"] == "documented":
328
+ _identifier(
329
+ authorization["source_reference"],
330
+ "inventory.authorization_status_input.source_reference",
331
+ )
332
+ elif authorization["source_reference"] is not None:
333
+ _fail(
334
+ "inventory.authorization_status_input.source_reference",
335
+ "must be null unless value is documented",
336
+ )
337
+
338
+ claim_requirement_ids: set[str] = set()
339
+ for index, raw_claim in enumerate(
340
+ _array(root["waiver_or_exception_claims"], "inventory.waiver_or_exception_claims")
341
+ ):
342
+ path = f"inventory.waiver_or_exception_claims[{index}]"
343
+ claim = _closed(
344
+ raw_claim,
345
+ path,
346
+ {"requirement_id", "route_requirement_ids", "decision_artifact_id", "declared_by"},
347
+ )
348
+ requirement_id = _identifier(claim["requirement_id"], f"{path}.requirement_id")
349
+ if requirement_id in claim_requirement_ids:
350
+ _fail(f"{path}.requirement_id", "duplicates another waiver or exception claim")
351
+ claim_requirement_ids.add(requirement_id)
352
+ _unique_identifiers(
353
+ claim["route_requirement_ids"], f"{path}.route_requirement_ids", maximum=128
354
+ )
355
+ if claim["decision_artifact_id"] is not None:
356
+ _identifier(claim["decision_artifact_id"], f"{path}.decision_artifact_id")
357
+ if claim["declared_by"] != "author":
358
+ _fail(f"{path}.declared_by", "must equal author")
359
+
360
+ artifact_ids: set[str] = set()
361
+ relative_paths: set[str] = set()
362
+ artifacts = _array(root["artifacts"], "inventory.artifacts")
363
+ for index, raw_artifact in enumerate(artifacts):
364
+ path = f"inventory.artifacts[{index}]"
365
+ artifact = _closed(
366
+ raw_artifact,
367
+ path,
368
+ {
369
+ "artifact_id",
370
+ "relative_path",
371
+ "declared_sha256",
372
+ "declared_size_bytes",
373
+ "media_type",
374
+ "artifact_type",
375
+ "declared_holder_role_id",
376
+ "evidence_bindings",
377
+ "declared_structure",
378
+ },
379
+ )
380
+ artifact_id = _identifier(artifact["artifact_id"], f"{path}.artifact_id")
381
+ if artifact_id in artifact_ids:
382
+ _fail(f"{path}.artifact_id", "duplicates another artifact")
383
+ artifact_ids.add(artifact_id)
384
+ relative_path = _relative_path(artifact["relative_path"], f"{path}.relative_path")
385
+ if relative_path in relative_paths:
386
+ _fail(f"{path}.relative_path", "duplicates another artifact path")
387
+ relative_paths.add(relative_path)
388
+ _sha(artifact["declared_sha256"], f"{path}.declared_sha256")
389
+ size = artifact["declared_size_bytes"]
390
+ if isinstance(size, bool) or not isinstance(size, int) or not 0 <= size <= MAX_ARTIFACT_BYTES:
391
+ _fail(
392
+ f"{path}.declared_size_bytes",
393
+ f"must be an integer from 0 through {MAX_ARTIFACT_BYTES}",
394
+ )
395
+ media_type = _text(artifact["media_type"], f"{path}.media_type", maximum=200)
396
+ if MEDIA_TYPE_RE.fullmatch(media_type) is None:
397
+ _fail(f"{path}.media_type", "must be a syntactically valid media type")
398
+ _identifier(artifact["artifact_type"], f"{path}.artifact_type")
399
+ _identifier(artifact["declared_holder_role_id"], f"{path}.declared_holder_role_id")
400
+
401
+ seen_bindings: set[tuple[str, str]] = set()
402
+ for binding_index, raw_binding in enumerate(
403
+ _array(artifact["evidence_bindings"], f"{path}.evidence_bindings")
404
+ ):
405
+ binding_path = f"{path}.evidence_bindings[{binding_index}]"
406
+ binding = _closed(raw_binding, binding_path, {"requirement_id", "evidence_id"})
407
+ key = (
408
+ _identifier(binding["requirement_id"], f"{binding_path}.requirement_id"),
409
+ _identifier(binding["evidence_id"], f"{binding_path}.evidence_id"),
410
+ )
411
+ if key in seen_bindings:
412
+ _fail(binding_path, "duplicates another binding on this artifact")
413
+ seen_bindings.add(key)
414
+
415
+ structure = _closed(
416
+ artifact["declared_structure"],
417
+ f"{path}.declared_structure",
418
+ {"version_id", "document_date", "signature_blocks", "certificate"},
419
+ )
420
+ if structure["version_id"] is not None:
421
+ _version(structure["version_id"], f"{path}.declared_structure.version_id")
422
+ _iso_date(
423
+ structure["document_date"],
424
+ f"{path}.declared_structure.document_date",
425
+ nullable=True,
426
+ )
427
+ signature_roles: set[str] = set()
428
+ for signature_index, raw_signature in enumerate(
429
+ _array(
430
+ structure["signature_blocks"],
431
+ f"{path}.declared_structure.signature_blocks",
432
+ maximum=128,
433
+ )
434
+ ):
435
+ signature_path = (
436
+ f"{path}.declared_structure.signature_blocks[{signature_index}]"
437
+ )
438
+ signature = _closed(raw_signature, signature_path, {"role_id", "state"})
439
+ role_id = _identifier(signature["role_id"], f"{signature_path}.role_id")
440
+ if role_id in signature_roles:
441
+ _fail(f"{signature_path}.role_id", "duplicates another signature role")
442
+ signature_roles.add(role_id)
443
+ if signature["state"] not in {"present", "not_located", "unknown"}:
444
+ _fail(
445
+ f"{signature_path}.state", "must be present, not_located, or unknown"
446
+ )
447
+ certificate = structure["certificate"]
448
+ if certificate is not None:
449
+ certificate_path = f"{path}.declared_structure.certificate"
450
+ cert = _closed(
451
+ certificate,
452
+ certificate_path,
453
+ {"certificate_id", "holder_role_id", "issued_on", "expires_on"},
454
+ )
455
+ _identifier(cert["certificate_id"], f"{certificate_path}.certificate_id")
456
+ _identifier(cert["holder_role_id"], f"{certificate_path}.holder_role_id")
457
+ _iso_date(cert["issued_on"], f"{certificate_path}.issued_on")
458
+ _iso_date(
459
+ cert["expires_on"], f"{certificate_path}.expires_on", nullable=True
460
+ )
461
+
462
+ for index, claim in enumerate(root["waiver_or_exception_claims"]):
463
+ artifact_id = claim["decision_artifact_id"]
464
+ if artifact_id is not None and artifact_id not in artifact_ids:
465
+ _fail(
466
+ f"inventory.waiver_or_exception_claims[{index}].decision_artifact_id",
467
+ "does not resolve to an inventoried artifact",
468
+ )
469
+
470
+
471
+ def _descriptor_flags(*, directory: bool) -> int:
472
+ """Return fail-closed POSIX flags for descriptor-relative packet access."""
473
+ if (
474
+ not DESCRIPTOR_RELATIVE_OPEN_SUPPORTED
475
+ or not hasattr(os, "O_NOFOLLOW")
476
+ or not hasattr(os, "O_DIRECTORY")
477
+ ):
478
+ _fail(
479
+ "packet_root",
480
+ "descriptor-relative O_NOFOLLOW packet access is unavailable",
481
+ )
482
+ flags = os.O_RDONLY | os.O_NOFOLLOW
483
+ if directory:
484
+ flags |= os.O_DIRECTORY
485
+ elif hasattr(os, "O_NONBLOCK"):
486
+ # Prevent an inventoried FIFO or device from blocking before fstat can
487
+ # reject it. O_NONBLOCK has no effect on ordinary regular-file reads.
488
+ flags |= os.O_NONBLOCK
489
+ else:
490
+ _fail("packet_root", "nonblocking safe packet access is unavailable")
491
+ if hasattr(os, "O_CLOEXEC"):
492
+ flags |= os.O_CLOEXEC
493
+ return flags
494
+
495
+
496
+ def _open_directory_at(
497
+ parent_fd: int,
498
+ component: str,
499
+ contract_path: str,
500
+ *,
501
+ missing_ok: bool,
502
+ ) -> int | None:
503
+ """Open one directory component without following it or any replacement."""
504
+ try:
505
+ opened = os.open(
506
+ component,
507
+ _descriptor_flags(directory=True),
508
+ dir_fd=parent_fd,
509
+ )
510
+ except FileNotFoundError:
511
+ if missing_ok:
512
+ return None
513
+ _fail(contract_path, "does not exist")
514
+ except OSError as exc:
515
+ if exc.errno in {errno.ELOOP, errno.ENOTDIR}:
516
+ _fail(contract_path, "contains a symlink or non-directory component")
517
+ raise ContractError(
518
+ f"{contract_path}: cannot safely open directory component: {exc}"
519
+ ) from exc
520
+ try:
521
+ if not stat.S_ISDIR(os.fstat(opened).st_mode):
522
+ _fail(contract_path, "contains a non-directory component")
523
+ except BaseException:
524
+ os.close(opened)
525
+ raise
526
+ return opened
527
+
528
+
529
+ def _open_packet_root(packet_root: Path) -> int:
530
+ """Anchor an explicit packet root by walking every component by descriptor."""
531
+ if ".." in packet_root.parts:
532
+ _fail("packet_root", "must not contain dot-dot traversal")
533
+ absolute = packet_root.absolute()
534
+ if absolute == Path(absolute.anchor):
535
+ _fail("packet_root", "must be a bounded packet directory, not a filesystem root")
536
+ if absolute.anchor != "/":
537
+ _fail("packet_root", "must be an absolute POSIX packet path")
538
+ try:
539
+ current_fd = os.open(absolute.anchor, _descriptor_flags(directory=True))
540
+ except OSError as exc:
541
+ raise ContractError(f"packet_root: cannot anchor filesystem root: {exc}") from exc
542
+ try:
543
+ for component in absolute.parts[1:]:
544
+ next_fd = _open_directory_at(
545
+ current_fd,
546
+ component,
547
+ "packet_root",
548
+ missing_ok=False,
549
+ )
550
+ assert next_fd is not None
551
+ os.close(current_fd)
552
+ current_fd = next_fd
553
+ return current_fd
554
+ except BaseException:
555
+ os.close(current_fd)
556
+ raise
557
+
558
+
559
+ def _open_explicit_regular_file(root_fd: int, relative_path: str) -> int | None:
560
+ """Open one named packet member relative to an anchored root descriptor."""
561
+ parts = PurePosixPath(relative_path).parts
562
+ contract_path = f"packet[{relative_path!r}]"
563
+ try:
564
+ current_fd = os.dup(root_fd)
565
+ except OSError as exc:
566
+ raise ContractError(f"{contract_path}: cannot duplicate packet root: {exc}") from exc
567
+ try:
568
+ for component in parts[:-1]:
569
+ next_fd = _open_directory_at(
570
+ current_fd,
571
+ component,
572
+ contract_path,
573
+ missing_ok=True,
574
+ )
575
+ if next_fd is None:
576
+ return None
577
+ os.close(current_fd)
578
+ current_fd = next_fd
579
+ try:
580
+ opened = os.open(
581
+ parts[-1],
582
+ _descriptor_flags(directory=False),
583
+ dir_fd=current_fd,
584
+ )
585
+ except FileNotFoundError:
586
+ return None
587
+ except OSError as exc:
588
+ if exc.errno in {errno.ELOOP, errno.ENOTDIR}:
589
+ _fail(contract_path, "symlinks are not allowed")
590
+ raise ContractError(
591
+ f"{contract_path}: cannot safely open explicitly named file: {exc}"
592
+ ) from exc
593
+ try:
594
+ item_stat = os.fstat(opened)
595
+ if not stat.S_ISREG(item_stat.st_mode):
596
+ _fail(contract_path, "must be a regular file")
597
+ if item_stat.st_size > MAX_ARTIFACT_BYTES:
598
+ _fail(
599
+ contract_path,
600
+ f"exceeds the {MAX_ARTIFACT_BYTES}-byte V1 observation limit",
601
+ )
602
+ except BaseException:
603
+ os.close(opened)
604
+ raise
605
+ return opened
606
+ finally:
607
+ os.close(current_fd)
608
+
609
+
610
+ def _hash_explicit_file(file_fd: int, relative_path: str) -> tuple[str, int]:
611
+ hasher = hashlib.sha256()
612
+ observed_size = 0
613
+ try:
614
+ while True:
615
+ chunk = os.read(file_fd, 1024 * 1024)
616
+ if not chunk:
617
+ break
618
+ observed_size += len(chunk)
619
+ if observed_size > MAX_ARTIFACT_BYTES:
620
+ _fail(
621
+ f"packet[{relative_path!r}]",
622
+ f"exceeds the {MAX_ARTIFACT_BYTES}-byte V1 observation limit",
623
+ )
624
+ hasher.update(chunk)
625
+ except OSError as exc:
626
+ raise ContractError(f"packet[{relative_path!r}]: cannot read regular file: {exc}") from exc
627
+ return hasher.hexdigest(), observed_size
628
+
629
+
630
+ def _located_observation_reasons(
631
+ artifact: dict[str, Any], observed_sha256: str, observed_size: int
632
+ ) -> list[str]:
633
+ """Compare only inventory-owned byte and internal-structure declarations."""
634
+ reasons: list[str] = []
635
+ if observed_sha256 != artifact["declared_sha256"]:
636
+ reasons.append("ARTIFACT_DIGEST_MISMATCH")
637
+ if observed_size != artifact["declared_size_bytes"]:
638
+ reasons.append("ARTIFACT_SIZE_MISMATCH")
639
+ certificate = artifact["declared_structure"]["certificate"]
640
+ if (
641
+ certificate is not None
642
+ and certificate["expires_on"] is not None
643
+ and certificate["expires_on"] < certificate["issued_on"]
644
+ ):
645
+ reasons.append("DECLARED_STRUCTURE_CONFLICT")
646
+ if (
647
+ certificate is not None
648
+ and certificate["holder_role_id"] != artifact["declared_holder_role_id"]
649
+ ):
650
+ reasons.append("CERTIFICATE_HOLDER_MISMATCH")
651
+ return sorted(reasons)
652
+
653
+
654
+ def observe_submission_packet(
655
+ inventory: dict[str, Any], packet_root: Path | str
656
+ ) -> list[dict[str, Any]]:
657
+ """Observe exactly the regular files listed by a validated inventory."""
658
+ validate_submission_packet_inventory(inventory)
659
+ root = Path(packet_root)
660
+ root_fd = _open_packet_root(root)
661
+ observations: list[dict[str, Any]] = []
662
+ total_observed_size = 0
663
+ try:
664
+ for artifact in inventory["artifacts"]:
665
+ relative_path = artifact["relative_path"]
666
+ file_fd = _open_explicit_regular_file(root_fd, relative_path)
667
+ if file_fd is None:
668
+ observations.append(
669
+ {
670
+ "artifact_id": artifact["artifact_id"],
671
+ "relative_path": relative_path,
672
+ "state": "not_located",
673
+ "observed_sha256": None,
674
+ "observed_size_bytes": None,
675
+ "status": "CONFLICTING",
676
+ "reason_codes": ["DECLARED_ATTACHMENT_NOT_LOCATED"],
677
+ }
678
+ )
679
+ continue
680
+ try:
681
+ observed_sha256, observed_size = _hash_explicit_file(
682
+ file_fd, relative_path
683
+ )
684
+ finally:
685
+ os.close(file_fd)
686
+ total_observed_size += observed_size
687
+ if total_observed_size > MAX_TOTAL_BYTES:
688
+ _fail(
689
+ "packet",
690
+ f"explicitly listed files exceed the {MAX_TOTAL_BYTES}-byte V1 total limit",
691
+ )
692
+ reasons = _located_observation_reasons(
693
+ artifact, observed_sha256, observed_size
694
+ )
695
+ observations.append(
696
+ {
697
+ "artifact_id": artifact["artifact_id"],
698
+ "relative_path": relative_path,
699
+ "state": "located",
700
+ "observed_sha256": observed_sha256,
701
+ "observed_size_bytes": observed_size,
702
+ "status": "CONFLICTING" if reasons else "DOCUMENTED",
703
+ "reason_codes": reasons or ["STRUCTURE_DOCUMENTED"],
704
+ }
705
+ )
706
+ finally:
707
+ os.close(root_fd)
708
+ observations.sort(key=lambda row: (row["artifact_id"], row["relative_path"]))
709
+ return observations
710
+
711
+
712
+ _REQUIREMENT_REF_FIELDS = (
713
+ "requirement_id",
714
+ "authority_kind",
715
+ "authority_id",
716
+ "authority_version",
717
+ "authority_digest",
718
+ "axis",
719
+ "obligated_actor",
720
+ "consumer_scopes",
721
+ "applicability",
722
+ "requirement_digest",
723
+ "requirement_pointer",
724
+ "authority_anchor_pointer",
725
+ )
726
+
727
+
728
+ def _requirement_ref(row: dict[str, Any]) -> dict[str, Any]:
729
+ try:
730
+ return {field: copy.deepcopy(row[field]) for field in _REQUIREMENT_REF_FIELDS}
731
+ except KeyError as exc:
732
+ _fail("resolved.requirement_results", f"missing replay-bound field: {exc.args[0]}")
733
+
734
+
735
+ def _pointer_get(root: dict[str, Any], pointer: str, contract_path: str) -> Any:
736
+ if not isinstance(pointer, str) or re.fullmatch(
737
+ r"/(profiles|overlays)/[0-9]+/requirements/[0-9]+", pointer
738
+ ) is None:
739
+ _fail(contract_path, "is not an exact requirement pointer")
740
+ value: Any = root
741
+ for token in pointer[1:].split("/"):
742
+ if isinstance(value, dict):
743
+ if token not in value:
744
+ _fail(contract_path, "does not resolve in the replay-bound registry")
745
+ value = value[token]
746
+ elif isinstance(value, list):
747
+ index = int(token)
748
+ if index >= len(value):
749
+ _fail(contract_path, "array index is outside the replay-bound registry")
750
+ value = value[index]
751
+ else:
752
+ _fail(contract_path, "traverses a non-container registry value")
753
+ if not isinstance(value, dict):
754
+ _fail(contract_path, "must resolve to a requirement object")
755
+ return value
756
+
757
+
758
+ def _capability_envelope(
759
+ context: dict[str, Any] | None,
760
+ ) -> tuple[dict[str, Any], list[dict[str, Any]]]:
761
+ declared_by_id: dict[str, dict[str, Any]] = {}
762
+ if context is not None:
763
+ raw_facts = context.get("declared_facts")
764
+ if not isinstance(raw_facts, list):
765
+ _fail("context.declared_facts", "must be an array")
766
+ for index, row in enumerate(raw_facts):
767
+ if not isinstance(row, dict) or not isinstance(row.get("fact_id"), str):
768
+ _fail(f"context.declared_facts[{index}]", "must be a validated declared fact")
769
+ declared_by_id[row["fact_id"]] = row
770
+
771
+ facts: list[dict[str, Any]] = []
772
+ reasons: list[dict[str, Any]] = []
773
+ reason_codes: list[str] = []
774
+ for fact_id, required_value in ENVELOPE_FACTS:
775
+ declaration = declared_by_id.get(fact_id)
776
+ if declaration is None:
777
+ declared_state = "missing"
778
+ declared_value = None
779
+ reason_code = "V1_ENVELOPE_FACT_MISSING"
780
+ elif declaration.get("state") == "unknown":
781
+ declared_state = "unknown"
782
+ declared_value = None
783
+ reason_code = "V1_ENVELOPE_FACT_UNKNOWN"
784
+ else:
785
+ declared_state = "declared"
786
+ declared_value = declaration.get("value")
787
+ if not isinstance(declared_value, bool):
788
+ _fail(
789
+ f"context.declared_facts[{fact_id}]",
790
+ "V1 envelope facts must be replay-validated booleans",
791
+ )
792
+ reason_code = (
793
+ None if declared_value == required_value else "V1_ENVELOPE_OUTSIDE"
794
+ )
795
+ facts.append(
796
+ {
797
+ "fact_id": fact_id,
798
+ "required_value": required_value,
799
+ "declared_state": declared_state,
800
+ "declared_value": declared_value,
801
+ }
802
+ )
803
+ if reason_code is not None:
804
+ if reason_code not in reason_codes:
805
+ reason_codes.append(reason_code)
806
+ reasons.append(
807
+ {
808
+ "status": "APPLICABILITY_UNRESOLVED",
809
+ "code": reason_code,
810
+ "fact_id": fact_id,
811
+ "overlay_kind": None,
812
+ }
813
+ )
814
+ return (
815
+ {
816
+ "state": "within_v1" if not reason_codes else "unresolved",
817
+ "facts": facts,
818
+ "reason_codes": reason_codes,
819
+ },
820
+ reasons,
821
+ )
822
+
823
+
824
+ def _authority_binding(
825
+ resolved: dict[str, Any] | None,
826
+ ) -> dict[str, Any]:
827
+ if resolved is None:
828
+ return {
829
+ "state": "not_provided",
830
+ "context_pointer": None,
831
+ "registry_pointer": None,
832
+ "resolved_digest": None,
833
+ "resolution_state": None,
834
+ "downstream_gate": None,
835
+ }
836
+ _iso_date(
837
+ resolved["registry_pointer"]["as_of"],
838
+ "resolved.registry_pointer.as_of",
839
+ )
840
+ return {
841
+ "state": "replay_validated",
842
+ "context_pointer": copy.deepcopy(resolved["context_pointer"]),
843
+ "registry_pointer": copy.deepcopy(resolved["registry_pointer"]),
844
+ "resolved_digest": resolved["resolved_digest"],
845
+ "resolution_state": resolved["resolution_state"],
846
+ "downstream_gate": copy.deepcopy(resolved["downstream_gate"]),
847
+ }
848
+
849
+
850
+ def _global_unresolved_reasons(
851
+ *,
852
+ resolved: dict[str, Any] | None,
853
+ context: dict[str, Any] | None,
854
+ envelope_reasons: list[dict[str, Any]],
855
+ ) -> list[dict[str, Any]]:
856
+ reasons: list[dict[str, Any]] = []
857
+ if resolved is None:
858
+ reasons.append(
859
+ {
860
+ "status": "APPLICABILITY_UNRESOLVED",
861
+ "code": "AUTHORITY_INPUT_NOT_PROVIDED",
862
+ "fact_id": None,
863
+ "overlay_kind": None,
864
+ }
865
+ )
866
+ elif not resolved["downstream_gate"]["profile_dependent_result_allowed"]:
867
+ reasons.append(
868
+ {
869
+ "status": "APPLICABILITY_UNRESOLVED",
870
+ "code": "AUTHORITY_RESOLUTION_NOT_PERMITTED",
871
+ "fact_id": None,
872
+ "overlay_kind": None,
873
+ }
874
+ )
875
+ reasons.extend(copy.deepcopy(envelope_reasons))
876
+ if context is not None:
877
+ overlay_state = context["overlay_selection_state"]
878
+ for kind in ("institutional", "funder"):
879
+ if overlay_state[kind] == "not_provided":
880
+ reasons.append(
881
+ {
882
+ "status": "APPLICABILITY_UNRESOLVED",
883
+ "code": "OVERLAY_SELECTION_NOT_PROVIDED",
884
+ "fact_id": None,
885
+ "overlay_kind": kind,
886
+ }
887
+ )
888
+ reasons.sort(
889
+ key=lambda row: (
890
+ row["code"],
891
+ row["fact_id"] or "",
892
+ row["overlay_kind"] or "",
893
+ )
894
+ )
895
+ return reasons
896
+
897
+
898
+ def _observation_by_id(
899
+ observations: list[dict[str, Any]],
900
+ ) -> dict[str, dict[str, Any]]:
901
+ return {row["artifact_id"]: row for row in observations}
902
+
903
+
904
+ def _inventory_indexes(
905
+ inventory: dict[str, Any],
906
+ ) -> tuple[
907
+ dict[str, dict[str, Any]],
908
+ dict[tuple[str, str], list[dict[str, Any]]],
909
+ dict[tuple[str, str], list[str]],
910
+ dict[str, dict[str, Any]],
911
+ ]:
912
+ """Index bounded inventory joins once; entry expansion must never rescan it."""
913
+ artifacts_by_id: dict[str, dict[str, Any]] = {}
914
+ artifacts_by_evidence: dict[tuple[str, str], list[dict[str, Any]]] = {}
915
+ evidence_ids_by_artifact_requirement: dict[tuple[str, str], list[str]] = {}
916
+ for artifact in inventory["artifacts"]:
917
+ artifact_id = artifact["artifact_id"]
918
+ artifacts_by_id[artifact_id] = artifact
919
+ for binding in artifact["evidence_bindings"]:
920
+ requirement_id = binding["requirement_id"]
921
+ evidence_id = binding["evidence_id"]
922
+ artifacts_by_evidence.setdefault(
923
+ (requirement_id, evidence_id), []
924
+ ).append(artifact)
925
+ evidence_ids_by_artifact_requirement.setdefault(
926
+ (artifact_id, requirement_id), []
927
+ ).append(evidence_id)
928
+ for artifacts in artifacts_by_evidence.values():
929
+ artifacts.sort(key=lambda row: row["artifact_id"])
930
+ for evidence_ids in evidence_ids_by_artifact_requirement.values():
931
+ evidence_ids.sort()
932
+ claims_by_requirement = {
933
+ claim["requirement_id"]: claim
934
+ for claim in inventory["waiver_or_exception_claims"]
935
+ }
936
+ return (
937
+ artifacts_by_id,
938
+ artifacts_by_evidence,
939
+ evidence_ids_by_artifact_requirement,
940
+ claims_by_requirement,
941
+ )
942
+
943
+
944
+ def _resolved_requirement(
945
+ result: dict[str, Any], registry: dict[str, Any]
946
+ ) -> tuple[dict[str, Any], list[dict[str, Any]]]:
947
+ requirement = _pointer_get(
948
+ registry, result["requirement_pointer"], "resolved.requirement_pointer"
949
+ )
950
+ if requirement.get("requirement_id") != result["requirement_id"]:
951
+ _fail("resolved.requirement_pointer", "does not identify its requirement_id")
952
+ if _digest(requirement) != result["requirement_digest"]:
953
+ _fail("resolved.requirement_digest", "does not match the pointed requirement")
954
+ evidence_rows = requirement.get("evidence_expected")
955
+ if not isinstance(evidence_rows, list):
956
+ _fail("resolved.requirement_pointer", "has no mechanical evidence roster")
957
+ if any(not isinstance(row, dict) for row in evidence_rows):
958
+ _fail("resolved.requirement_pointer", "has a malformed evidence pointer")
959
+ return requirement, evidence_rows
960
+
961
+
962
+ def _artifact_conflict_reasons(
963
+ artifact: dict[str, Any],
964
+ evidence: dict[str, Any],
965
+ observation: dict[str, Any],
966
+ ) -> list[str]:
967
+ reasons = (
968
+ list(observation["reason_codes"])
969
+ if observation["status"] == "CONFLICTING"
970
+ else []
971
+ )
972
+ if observation["state"] != "located":
973
+ return sorted(set(reasons))
974
+ if artifact["artifact_type"] != evidence["artifact_type"]:
975
+ reasons.append("ARTIFACT_TYPE_MISMATCH")
976
+ if artifact["declared_holder_role_id"] != evidence["held_by"]:
977
+ reasons.append("ARTIFACT_HOLDER_MISMATCH")
978
+
979
+ structure = artifact["declared_structure"]
980
+ certificate = structure["certificate"]
981
+ if certificate is not None and (
982
+ certificate["holder_role_id"] != evidence["held_by"]
983
+ or certificate["holder_role_id"] != artifact["declared_holder_role_id"]
984
+ ):
985
+ reasons.append("CERTIFICATE_HOLDER_MISMATCH")
986
+ return sorted(set(reasons))
987
+
988
+
989
+ def _located_profiled_route_claim(
990
+ requirement_id: str,
991
+ requirement: dict[str, Any],
992
+ observations: dict[str, dict[str, Any]],
993
+ registry: dict[str, Any],
994
+ scoped_by_id: dict[str, dict[str, Any]],
995
+ responsibility_roles: set[str],
996
+ artifacts_by_id: dict[str, dict[str, Any]],
997
+ artifacts_by_evidence: dict[tuple[str, str], list[dict[str, Any]]],
998
+ evidence_ids_by_artifact_requirement: dict[tuple[str, str], list[str]],
999
+ claims_by_requirement: dict[str, dict[str, Any]],
1000
+ resolved_requirements: dict[
1001
+ str, tuple[dict[str, Any], list[dict[str, Any]]]
1002
+ ],
1003
+ evidence_by_requirement: dict[str, dict[str, dict[str, Any]]],
1004
+ ) -> bool:
1005
+ route = requirement.get("waiver_or_exception_route")
1006
+ if not isinstance(route, dict) or route.get("state") != "profiled":
1007
+ return False
1008
+ profiled_ids = route.get("requirement_ids")
1009
+ if not isinstance(profiled_ids, list) or not profiled_ids:
1010
+ return False
1011
+ claim = claims_by_requirement.get(requirement_id)
1012
+ if claim is None or set(claim["route_requirement_ids"]) != set(
1013
+ profiled_ids
1014
+ ) or len(claim["route_requirement_ids"]) != len(profiled_ids):
1015
+ return False
1016
+ artifact_id = claim["decision_artifact_id"]
1017
+ if artifact_id is None:
1018
+ return False
1019
+ observation = observations.get(artifact_id)
1020
+ artifact = artifacts_by_id.get(artifact_id)
1021
+ if artifact is None or observation is None or observation["state"] != "located":
1022
+ return False
1023
+
1024
+ for route_requirement_id in profiled_ids:
1025
+ route_result = scoped_by_id.get(route_requirement_id)
1026
+ if (
1027
+ route_result is None
1028
+ or route_result["applicability"] != "true"
1029
+ or "submission_packet" not in route_result["consumer_scopes"]
1030
+ or route_result["obligated_actor"] not in responsibility_roles
1031
+ ):
1032
+ return False
1033
+ if route_requirement_id not in resolved_requirements:
1034
+ resolved_requirements[route_requirement_id] = _resolved_requirement(
1035
+ route_result, registry
1036
+ )
1037
+ if route_requirement_id not in evidence_by_requirement:
1038
+ evidence_by_requirement[route_requirement_id] = {
1039
+ row["evidence_id"]: row
1040
+ for row in resolved_requirements[route_requirement_id][1]
1041
+ }
1042
+ evidence_by_id = evidence_by_requirement[route_requirement_id]
1043
+ bound_evidence_ids = evidence_ids_by_artifact_requirement.get(
1044
+ (artifact_id, route_requirement_id), []
1045
+ )
1046
+ route_evidence_documented = False
1047
+ for evidence_id in bound_evidence_ids:
1048
+ evidence = evidence_by_id.get(evidence_id)
1049
+ if (
1050
+ evidence is None
1051
+ or evidence["held_by"] not in responsibility_roles
1052
+ or artifacts_by_evidence.get(
1053
+ (route_requirement_id, evidence_id), []
1054
+ )
1055
+ != [artifact]
1056
+ ):
1057
+ continue
1058
+ if not _artifact_conflict_reasons(artifact, evidence, observation):
1059
+ route_evidence_documented = True
1060
+ break
1061
+ if not route_evidence_documented:
1062
+ return False
1063
+ return True
1064
+
1065
+
1066
+ def _packet_owned_entry_state(
1067
+ *,
1068
+ requirement_id: str,
1069
+ evidence: dict[str, Any],
1070
+ requirement: dict[str, Any],
1071
+ observations: dict[str, dict[str, Any]],
1072
+ registry: dict[str, Any],
1073
+ scoped_by_id: dict[str, dict[str, Any]],
1074
+ responsibility_roles: set[str],
1075
+ artifacts_by_evidence: dict[tuple[str, str], list[dict[str, Any]]],
1076
+ route_claim_results: dict[str, bool],
1077
+ artifacts_by_id: dict[str, dict[str, Any]],
1078
+ evidence_ids_by_artifact_requirement: dict[tuple[str, str], list[str]],
1079
+ claims_by_requirement: dict[str, dict[str, Any]],
1080
+ resolved_requirements: dict[
1081
+ str, tuple[dict[str, Any], list[dict[str, Any]]]
1082
+ ],
1083
+ evidence_by_requirement: dict[str, dict[str, dict[str, Any]]],
1084
+ ) -> tuple[str, str, list[str], list[str]]:
1085
+ matches = artifacts_by_evidence.get((requirement_id, evidence["evidence_id"]), [])
1086
+ matched_ids = [row["artifact_id"] for row in matches]
1087
+ if not matches:
1088
+ if requirement_id not in route_claim_results:
1089
+ route_claim_results[requirement_id] = _located_profiled_route_claim(
1090
+ requirement_id,
1091
+ requirement,
1092
+ observations,
1093
+ registry,
1094
+ scoped_by_id,
1095
+ responsibility_roles,
1096
+ artifacts_by_id,
1097
+ artifacts_by_evidence,
1098
+ evidence_ids_by_artifact_requirement,
1099
+ claims_by_requirement,
1100
+ resolved_requirements,
1101
+ evidence_by_requirement,
1102
+ )
1103
+ if route_claim_results[requirement_id]:
1104
+ return (
1105
+ "ACCEPTANCE_UNVERIFIED",
1106
+ "unresolved",
1107
+ ["WAIVER_OR_EXCEPTION_CLAIM_UNVERIFIED"],
1108
+ [],
1109
+ )
1110
+ return (
1111
+ "NOT_LOCATED",
1112
+ "gap",
1113
+ ["EXPECTED_PACKET_ARTIFACT_NOT_LOCATED"],
1114
+ [],
1115
+ )
1116
+ if len(matches) > 1:
1117
+ return (
1118
+ "CONFLICTING",
1119
+ "gap",
1120
+ ["DUPLICATE_EVIDENCE_BINDING"],
1121
+ matched_ids,
1122
+ )
1123
+
1124
+ artifact = matches[0]
1125
+ observation = observations[artifact["artifact_id"]]
1126
+ if observation["state"] != "located":
1127
+ return (
1128
+ "CONFLICTING",
1129
+ "gap",
1130
+ list(observation["reason_codes"]),
1131
+ matched_ids,
1132
+ )
1133
+
1134
+ reasons = _artifact_conflict_reasons(
1135
+ artifact, evidence, observation
1136
+ )
1137
+ if reasons:
1138
+ return "CONFLICTING", "gap", reasons, matched_ids
1139
+ return "DOCUMENTED", "none", ["STRUCTURE_DOCUMENTED"], matched_ids
1140
+
1141
+
1142
+ def _evidence_entries(
1143
+ inventory: dict[str, Any],
1144
+ context: dict[str, Any],
1145
+ registry: dict[str, Any],
1146
+ resolved: dict[str, Any],
1147
+ observations: list[dict[str, Any]],
1148
+ ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
1149
+ scoped: list[dict[str, Any]] = []
1150
+ for row in resolved["requirement_results"]:
1151
+ if "submission_packet" not in row["consumer_scopes"]:
1152
+ continue
1153
+ if len(row["consumer_scopes"]) > MAX_CONSUMER_SCOPES:
1154
+ _fail(
1155
+ "resolved.requirement_results",
1156
+ f"submission-packet consumer scopes exceed {MAX_CONSUMER_SCOPES} items",
1157
+ )
1158
+ scoped.append(row)
1159
+ scoped.sort(
1160
+ key=lambda row: (
1161
+ row["axis"],
1162
+ row["authority_kind"],
1163
+ row["authority_id"],
1164
+ row["requirement_id"],
1165
+ )
1166
+ )
1167
+ scoped_by_id: dict[str, dict[str, Any]] = {}
1168
+ for row in scoped:
1169
+ if row["requirement_id"] in scoped_by_id:
1170
+ _fail(
1171
+ "resolved.requirement_results",
1172
+ f"duplicate submission-packet requirement_id: {row['requirement_id']}",
1173
+ )
1174
+ scoped_by_id[row["requirement_id"]] = row
1175
+ entries_with_order: list[tuple[tuple[str, ...], dict[str, Any]]] = []
1176
+ excluded: list[dict[str, Any]] = []
1177
+ observed_by_id = _observation_by_id(observations)
1178
+ responsibility_roles = set(inventory["packet_responsibility_role_ids"])
1179
+ (
1180
+ artifacts_by_id,
1181
+ artifacts_by_evidence,
1182
+ evidence_ids_by_artifact_requirement,
1183
+ claims_by_requirement,
1184
+ ) = _inventory_indexes(inventory)
1185
+ resolved_requirements: dict[
1186
+ str, tuple[dict[str, Any], list[dict[str, Any]]]
1187
+ ] = {}
1188
+ evidence_by_requirement: dict[str, dict[str, dict[str, Any]]] = {}
1189
+ route_claim_results: dict[str, bool] = {}
1190
+
1191
+ for result in scoped:
1192
+ reference = _requirement_ref(result)
1193
+ if result["applicability"] == "false":
1194
+ if len(excluded) >= MAX_EXCLUDED_REQUIREMENTS:
1195
+ _fail(
1196
+ "resolved.requirement_results",
1197
+ f"exceeds {MAX_EXCLUDED_REQUIREMENTS} excluded submission-packet requirements",
1198
+ )
1199
+ excluded.append(
1200
+ {
1201
+ "requirement_ref": reference,
1202
+ "exclusion_basis": "resolved_predicate_false",
1203
+ }
1204
+ )
1205
+ continue
1206
+ if result["applicability"] != "true":
1207
+ _fail(
1208
+ "resolved.requirement_results",
1209
+ "a permitted downstream gate may contain only true or false applicability",
1210
+ )
1211
+ requirement_id = result["requirement_id"]
1212
+ if requirement_id not in resolved_requirements:
1213
+ resolved_requirements[requirement_id] = _resolved_requirement(
1214
+ result, registry
1215
+ )
1216
+ requirement, evidence_rows = resolved_requirements[requirement_id]
1217
+ for evidence_index, evidence in enumerate(evidence_rows):
1218
+ if len(entries_with_order) >= MAX_MANIFEST_ENTRIES:
1219
+ _fail(
1220
+ "resolved.requirement_results",
1221
+ f"exceeds {MAX_MANIFEST_ENTRIES} submission-packet evidence entries",
1222
+ )
1223
+ evidence_id = evidence.get("evidence_id")
1224
+ artifact_type = evidence.get("artifact_type")
1225
+ held_by = evidence.get("held_by")
1226
+ _identifier(evidence_id, "registry.evidence_expected.evidence_id")
1227
+ _identifier(artifact_type, "registry.evidence_expected.artifact_type")
1228
+ _identifier(held_by, "registry.evidence_expected.held_by")
1229
+ evidence_ref = {
1230
+ "evidence_id": evidence_id,
1231
+ "evidence_pointer": (
1232
+ f"{result['requirement_pointer']}/evidence_expected/{evidence_index}"
1233
+ ),
1234
+ "evidence_digest": _digest(evidence),
1235
+ "artifact_type": artifact_type,
1236
+ "held_by": held_by,
1237
+ }
1238
+ matched = artifacts_by_evidence.get(
1239
+ (result["requirement_id"], evidence_id), []
1240
+ )
1241
+ matched_ids = [row["artifact_id"] for row in matched]
1242
+ packet_owned = (
1243
+ held_by in responsibility_roles
1244
+ and result["obligated_actor"] in responsibility_roles
1245
+ )
1246
+ if not packet_owned:
1247
+ status = "ACCEPTANCE_UNVERIFIED"
1248
+ readiness_effect = "none"
1249
+ reason_codes = ["EXTERNAL_HOLDER_DEPENDENCY"]
1250
+ else:
1251
+ status, readiness_effect, reason_codes, matched_ids = (
1252
+ _packet_owned_entry_state(
1253
+ requirement_id=result["requirement_id"],
1254
+ evidence=evidence,
1255
+ requirement=requirement,
1256
+ observations=observed_by_id,
1257
+ registry=registry,
1258
+ scoped_by_id=scoped_by_id,
1259
+ responsibility_roles=responsibility_roles,
1260
+ artifacts_by_evidence=artifacts_by_evidence,
1261
+ route_claim_results=route_claim_results,
1262
+ artifacts_by_id=artifacts_by_id,
1263
+ evidence_ids_by_artifact_requirement=(
1264
+ evidence_ids_by_artifact_requirement
1265
+ ),
1266
+ claims_by_requirement=claims_by_requirement,
1267
+ resolved_requirements=resolved_requirements,
1268
+ evidence_by_requirement=evidence_by_requirement,
1269
+ )
1270
+ )
1271
+ entries_with_order.append(
1272
+ (
1273
+ (
1274
+ result["axis"],
1275
+ result["authority_kind"],
1276
+ result["authority_id"],
1277
+ result["requirement_id"],
1278
+ evidence_ref["evidence_pointer"],
1279
+ ),
1280
+ {
1281
+ "requirement_ref": reference,
1282
+ "evidence_ref": evidence_ref,
1283
+ "responsibility": (
1284
+ "packet_owned"
1285
+ if packet_owned
1286
+ else "external_dependency"
1287
+ ),
1288
+ "status": status,
1289
+ "readiness_effect": readiness_effect,
1290
+ "reason_codes": reason_codes,
1291
+ "matched_artifact_ids": matched_ids,
1292
+ },
1293
+ )
1294
+ )
1295
+ entries_with_order.sort(key=lambda item: item[0])
1296
+ excluded.sort(
1297
+ key=lambda row: (
1298
+ row["requirement_ref"]["axis"],
1299
+ row["requirement_ref"]["authority_kind"],
1300
+ row["requirement_ref"]["authority_id"],
1301
+ row["requirement_ref"]["requirement_id"],
1302
+ )
1303
+ )
1304
+ return [item[1] for item in entries_with_order], excluded
1305
+
1306
+
1307
+ def _readiness(
1308
+ unresolved_reasons: list[dict[str, Any]], entries: list[dict[str, Any]]
1309
+ ) -> str:
1310
+ if unresolved_reasons or any(
1311
+ row["readiness_effect"] == "unresolved" for row in entries
1312
+ ):
1313
+ return "unresolved"
1314
+ if any(row["readiness_effect"] == "gap" for row in entries):
1315
+ return "gaps_located"
1316
+ return "no_listed_gaps_located"
1317
+
1318
+
1319
+ def build_submission_packet_manifest(
1320
+ inventory: dict[str, Any],
1321
+ packet_root: Path | str,
1322
+ *,
1323
+ context: dict[str, Any] | None = None,
1324
+ registry: dict[str, Any] | None = None,
1325
+ resolved: dict[str, Any] | None = None,
1326
+ ) -> dict[str, Any]:
1327
+ """Build a manifest; a supplied authority triplet is replayed before dereference."""
1328
+ supplied = (context is not None, registry is not None, resolved is not None)
1329
+ if any(supplied) and not all(supplied):
1330
+ raise ContractError("context, registry, and resolved authority inputs are all-or-none")
1331
+
1332
+ # This replay is deliberately the first semantic operation on a complete
1333
+ # authority triplet. A failure stops before packet or authority pointers
1334
+ # are dereferenced by this layer.
1335
+ if all(supplied):
1336
+ assert context is not None and registry is not None and resolved is not None
1337
+ validate_resolved_context(resolved, context, registry)
1338
+
1339
+ validate_submission_packet_inventory(inventory)
1340
+ binding = _authority_binding(resolved)
1341
+ envelope, envelope_reasons = _capability_envelope(context)
1342
+ overlay_state = (
1343
+ copy.deepcopy(context["overlay_selection_state"])
1344
+ if context is not None
1345
+ else None
1346
+ )
1347
+ unresolved_reasons = _global_unresolved_reasons(
1348
+ resolved=resolved,
1349
+ context=context,
1350
+ envelope_reasons=envelope_reasons,
1351
+ )
1352
+
1353
+ gate_allowed = bool(
1354
+ resolved is not None
1355
+ and resolved["resolution_state"] == "resolved"
1356
+ and resolved["downstream_gate"]["profile_dependent_result_allowed"]
1357
+ )
1358
+ within_envelope = envelope["state"] == "within_v1"
1359
+ if gate_allowed and within_envelope:
1360
+ assert context is not None and registry is not None and resolved is not None
1361
+ observations = observe_submission_packet(inventory, packet_root)
1362
+ entries, excluded = _evidence_entries(
1363
+ inventory, context, registry, resolved, observations
1364
+ )
1365
+ else:
1366
+ # Fail-closed states intentionally do not touch packet_root or follow
1367
+ # registry pointers. The empty observation digest proves that scope.
1368
+ observations = []
1369
+ entries = []
1370
+ excluded = []
1371
+
1372
+ manifest: dict[str, Any] = {
1373
+ "schema_version": MANIFEST_SCHEMA_VERSION,
1374
+ "inventory_pointer": {
1375
+ "inventory_id": inventory["inventory_id"],
1376
+ "schema_version": inventory["schema_version"],
1377
+ "inventory_digest": _digest(_semantic_inventory(inventory)),
1378
+ },
1379
+ "authority_binding": binding,
1380
+ "packet_pointer": {
1381
+ "scope": "explicit_inventory_paths_only",
1382
+ "observation_digest": _digest(observations),
1383
+ },
1384
+ "packet_observations": observations,
1385
+ "capability_envelope": envelope,
1386
+ "overlay_selection_state": overlay_state,
1387
+ "administrative_status": {
1388
+ "review_pathway": REVIEW_PATHWAY,
1389
+ "submission_readiness": _readiness(unresolved_reasons, entries),
1390
+ "authorization_status": copy.deepcopy(
1391
+ inventory["authorization_status_input"]
1392
+ ),
1393
+ "review_timeline": REVIEW_TIMELINE,
1394
+ },
1395
+ "acceptance_boundary": {
1396
+ "status": "ACCEPTANCE_UNVERIFIED",
1397
+ "reason_code": ACCEPTANCE_REASON,
1398
+ "affects_submission_readiness": False,
1399
+ },
1400
+ "entries": entries,
1401
+ "excluded_requirements": excluded,
1402
+ "unresolved_reasons": unresolved_reasons,
1403
+ "boundary_footer": BOUNDARY_FOOTER,
1404
+ }
1405
+ manifest["manifest_digest"] = _digest(manifest)
1406
+ _enforce_manifest_byte_limit(manifest)
1407
+ return manifest
1408
+
1409
+
1410
+ def _validate_authorization_status(value: Any, path: str) -> None:
1411
+ row = _closed(value, path, {"value", "source_reference", "provenance"})
1412
+ if row["value"] not in AUTHORIZATION_VALUES:
1413
+ _fail(f"{path}.value", "has an invalid authorization input status")
1414
+ if row["provenance"] != "caller_supplied_no_derivation":
1415
+ _fail(f"{path}.provenance", "must equal caller_supplied_no_derivation")
1416
+ if row["value"] == "documented":
1417
+ _identifier(row["source_reference"], f"{path}.source_reference")
1418
+ elif row["source_reference"] is not None:
1419
+ _fail(f"{path}.source_reference", "must be null for this status")
1420
+
1421
+
1422
+ def _validate_context_pointer(value: Any, path: str) -> None:
1423
+ row = _closed(
1424
+ value, path, {"context_record_id", "schema_version", "context_digest"}
1425
+ )
1426
+ _identifier(row["context_record_id"], f"{path}.context_record_id")
1427
+ if row["schema_version"] != "irb-context-record/1.0":
1428
+ _fail(f"{path}.schema_version", "must equal irb-context-record/1.0")
1429
+ _sha(row["context_digest"], f"{path}.context_digest")
1430
+
1431
+
1432
+ def _validate_registry_pointer(value: Any, path: str) -> None:
1433
+ row = _closed(
1434
+ value,
1435
+ path,
1436
+ {"registry_id", "registry_version", "as_of", "registry_digest"},
1437
+ )
1438
+ _identifier(row["registry_id"], f"{path}.registry_id")
1439
+ _version(row["registry_version"], f"{path}.registry_version")
1440
+ _iso_date(row["as_of"], f"{path}.as_of")
1441
+ _sha(row["registry_digest"], f"{path}.registry_digest")
1442
+
1443
+
1444
+ def _validate_downstream_gate(value: Any, path: str) -> dict[str, Any]:
1445
+ row = _closed(
1446
+ value,
1447
+ path,
1448
+ {
1449
+ "both_axes_selected",
1450
+ "exact_selection_resolved",
1451
+ "all_applicability_resolved",
1452
+ "profile_dependent_result_allowed",
1453
+ },
1454
+ )
1455
+ for field, field_value in row.items():
1456
+ if not isinstance(field_value, bool):
1457
+ _fail(f"{path}.{field}", "must be boolean")
1458
+ return row
1459
+
1460
+
1461
+ def _validate_requirement_ref(value: Any, path: str, applicability: str) -> None:
1462
+ row = _closed(value, path, set(_REQUIREMENT_REF_FIELDS))
1463
+ _identifier(row["requirement_id"], f"{path}.requirement_id")
1464
+ if row["authority_kind"] not in {
1465
+ "profile",
1466
+ "institutional_overlay",
1467
+ "funder_overlay",
1468
+ }:
1469
+ _fail(f"{path}.authority_kind", "has an invalid authority kind")
1470
+ _identifier(row["authority_id"], f"{path}.authority_id")
1471
+ _version(row["authority_version"], f"{path}.authority_version")
1472
+ _sha(row["authority_digest"], f"{path}.authority_digest")
1473
+ if row["axis"] not in {"review_ethics", "data_protection"}:
1474
+ _fail(f"{path}.axis", "has an invalid authority axis")
1475
+ _identifier(row["obligated_actor"], f"{path}.obligated_actor")
1476
+ scopes = _unique_identifiers(
1477
+ row["consumer_scopes"],
1478
+ f"{path}.consumer_scopes",
1479
+ maximum=MAX_CONSUMER_SCOPES,
1480
+ minimum=1,
1481
+ )
1482
+ if "submission_packet" not in scopes:
1483
+ _fail(f"{path}.consumer_scopes", "must include submission_packet")
1484
+ if row["applicability"] != applicability:
1485
+ _fail(f"{path}.applicability", f"must equal {applicability}")
1486
+ _sha(row["requirement_digest"], f"{path}.requirement_digest")
1487
+ pointer = row["requirement_pointer"]
1488
+ if not isinstance(pointer, str) or re.fullmatch(
1489
+ r"/(profiles|overlays)/[0-9]+/requirements/[0-9]+", pointer
1490
+ ) is None:
1491
+ _fail(f"{path}.requirement_pointer", "has an invalid exact pointer")
1492
+ if row["authority_anchor_pointer"] != f"{pointer}/authority_anchor":
1493
+ _fail(
1494
+ f"{path}.authority_anchor_pointer",
1495
+ "must identify the anchor below requirement_pointer",
1496
+ )
1497
+
1498
+
1499
+ def _validate_evidence_ref(value: Any, path: str, requirement_pointer: str) -> None:
1500
+ row = _closed(
1501
+ value,
1502
+ path,
1503
+ {"evidence_id", "evidence_pointer", "evidence_digest", "artifact_type", "held_by"},
1504
+ )
1505
+ _identifier(row["evidence_id"], f"{path}.evidence_id")
1506
+ pointer = row["evidence_pointer"]
1507
+ if not isinstance(pointer, str) or re.fullmatch(
1508
+ re.escape(requirement_pointer) + r"/evidence_expected/[0-9]+", pointer
1509
+ ) is None:
1510
+ _fail(f"{path}.evidence_pointer", "must be below its requirement pointer")
1511
+ _sha(row["evidence_digest"], f"{path}.evidence_digest")
1512
+ _identifier(row["artifact_type"], f"{path}.artifact_type")
1513
+ _identifier(row["held_by"], f"{path}.held_by")
1514
+
1515
+
1516
+ def _validate_manifest_shape(manifest: dict[str, Any]) -> None:
1517
+ root = _closed(
1518
+ manifest,
1519
+ "manifest",
1520
+ {
1521
+ "schema_version",
1522
+ "inventory_pointer",
1523
+ "authority_binding",
1524
+ "packet_pointer",
1525
+ "packet_observations",
1526
+ "capability_envelope",
1527
+ "overlay_selection_state",
1528
+ "administrative_status",
1529
+ "acceptance_boundary",
1530
+ "entries",
1531
+ "excluded_requirements",
1532
+ "unresolved_reasons",
1533
+ "boundary_footer",
1534
+ "manifest_digest",
1535
+ },
1536
+ )
1537
+ _enforce_manifest_byte_limit(root)
1538
+ if root["schema_version"] != MANIFEST_SCHEMA_VERSION:
1539
+ _fail("manifest.schema_version", f"must equal {MANIFEST_SCHEMA_VERSION}")
1540
+ claimed_digest = _sha(root["manifest_digest"], "manifest.manifest_digest")
1541
+ unsigned = copy.deepcopy(root)
1542
+ unsigned.pop("manifest_digest")
1543
+ if _digest(unsigned) != claimed_digest:
1544
+ _fail(
1545
+ "manifest.manifest_digest",
1546
+ "does not match the canonical artifact digest with itself excluded",
1547
+ )
1548
+
1549
+ inventory_pointer = _closed(
1550
+ root["inventory_pointer"],
1551
+ "manifest.inventory_pointer",
1552
+ {"inventory_id", "schema_version", "inventory_digest"},
1553
+ )
1554
+ _identifier(inventory_pointer["inventory_id"], "manifest.inventory_pointer.inventory_id")
1555
+ if inventory_pointer["schema_version"] != INVENTORY_SCHEMA_VERSION:
1556
+ _fail(
1557
+ "manifest.inventory_pointer.schema_version",
1558
+ f"must equal {INVENTORY_SCHEMA_VERSION}",
1559
+ )
1560
+ _sha(
1561
+ inventory_pointer["inventory_digest"],
1562
+ "manifest.inventory_pointer.inventory_digest",
1563
+ )
1564
+
1565
+ authority = _closed(
1566
+ root["authority_binding"],
1567
+ "manifest.authority_binding",
1568
+ {
1569
+ "state",
1570
+ "context_pointer",
1571
+ "registry_pointer",
1572
+ "resolved_digest",
1573
+ "resolution_state",
1574
+ "downstream_gate",
1575
+ },
1576
+ )
1577
+ if authority["state"] == "not_provided":
1578
+ for field in (
1579
+ "context_pointer",
1580
+ "registry_pointer",
1581
+ "resolved_digest",
1582
+ "resolution_state",
1583
+ "downstream_gate",
1584
+ ):
1585
+ if authority[field] is not None:
1586
+ _fail(f"manifest.authority_binding.{field}", "must be null")
1587
+ elif authority["state"] == "replay_validated":
1588
+ _validate_context_pointer(
1589
+ authority["context_pointer"], "manifest.authority_binding.context_pointer"
1590
+ )
1591
+ _validate_registry_pointer(
1592
+ authority["registry_pointer"], "manifest.authority_binding.registry_pointer"
1593
+ )
1594
+ _sha(authority["resolved_digest"], "manifest.authority_binding.resolved_digest")
1595
+ if authority["resolution_state"] not in {
1596
+ "resolved",
1597
+ "jurisdiction_unresolved",
1598
+ "applicability_unresolved",
1599
+ }:
1600
+ _fail(
1601
+ "manifest.authority_binding.resolution_state",
1602
+ "has an invalid resolution state",
1603
+ )
1604
+ gate = _validate_downstream_gate(
1605
+ authority["downstream_gate"], "manifest.authority_binding.downstream_gate"
1606
+ )
1607
+ if authority["resolution_state"] == "resolved":
1608
+ if gate != {
1609
+ "both_axes_selected": True,
1610
+ "exact_selection_resolved": True,
1611
+ "all_applicability_resolved": True,
1612
+ "profile_dependent_result_allowed": True,
1613
+ }:
1614
+ _fail(
1615
+ "manifest.authority_binding.downstream_gate",
1616
+ "must be fully open when resolution_state is resolved",
1617
+ )
1618
+ elif gate["profile_dependent_result_allowed"] is not False:
1619
+ _fail(
1620
+ "manifest.authority_binding.downstream_gate.profile_dependent_result_allowed",
1621
+ "must be false when authority resolution is unresolved",
1622
+ )
1623
+ else:
1624
+ _fail("manifest.authority_binding.state", "has an invalid binding state")
1625
+
1626
+ packet_pointer = _closed(
1627
+ root["packet_pointer"],
1628
+ "manifest.packet_pointer",
1629
+ {"scope", "observation_digest"},
1630
+ )
1631
+ if packet_pointer["scope"] != "explicit_inventory_paths_only":
1632
+ _fail(
1633
+ "manifest.packet_pointer.scope", "must equal explicit_inventory_paths_only"
1634
+ )
1635
+ _sha(packet_pointer["observation_digest"], "manifest.packet_pointer.observation_digest")
1636
+ observations = _array(root["packet_observations"], "manifest.packet_observations")
1637
+ observation_ids: set[str] = set()
1638
+ observation_paths: set[str] = set()
1639
+ for index, raw_observation in enumerate(observations):
1640
+ path = f"manifest.packet_observations[{index}]"
1641
+ observation = _closed(
1642
+ raw_observation,
1643
+ path,
1644
+ {
1645
+ "artifact_id",
1646
+ "relative_path",
1647
+ "state",
1648
+ "observed_sha256",
1649
+ "observed_size_bytes",
1650
+ "status",
1651
+ "reason_codes",
1652
+ },
1653
+ )
1654
+ artifact_id = _identifier(observation["artifact_id"], f"{path}.artifact_id")
1655
+ relative_path = _relative_path(observation["relative_path"], f"{path}.relative_path")
1656
+ if artifact_id in observation_ids or relative_path in observation_paths:
1657
+ _fail(path, "duplicates an observation identity or path")
1658
+ observation_ids.add(artifact_id)
1659
+ observation_paths.add(relative_path)
1660
+ if observation["state"] == "located":
1661
+ _sha(observation["observed_sha256"], f"{path}.observed_sha256")
1662
+ size = observation["observed_size_bytes"]
1663
+ if isinstance(size, bool) or not isinstance(size, int) or not 0 <= size <= MAX_ARTIFACT_BYTES:
1664
+ _fail(f"{path}.observed_size_bytes", "has an invalid observed size")
1665
+ elif observation["state"] == "not_located":
1666
+ if observation["observed_sha256"] is not None or observation["observed_size_bytes"] is not None:
1667
+ _fail(path, "a not_located observation cannot carry observed values")
1668
+ else:
1669
+ _fail(f"{path}.state", "must be located or not_located")
1670
+ if observation["status"] not in {"DOCUMENTED", "CONFLICTING"}:
1671
+ _fail(f"{path}.status", "must be DOCUMENTED or CONFLICTING")
1672
+ observation_reasons = observation["reason_codes"]
1673
+ if (
1674
+ not isinstance(observation_reasons, list)
1675
+ or not observation_reasons
1676
+ or any(not isinstance(reason, str) for reason in observation_reasons)
1677
+ or len(set(observation_reasons)) != len(observation_reasons)
1678
+ or any(
1679
+ reason not in OBSERVATION_REASON_CODES
1680
+ for reason in observation_reasons
1681
+ )
1682
+ or observation_reasons != sorted(observation_reasons)
1683
+ ):
1684
+ _fail(f"{path}.reason_codes", "has invalid or non-canonical reasons")
1685
+ if observation["state"] == "not_located":
1686
+ if (
1687
+ observation["status"] != "CONFLICTING"
1688
+ or observation_reasons != ["DECLARED_ATTACHMENT_NOT_LOCATED"]
1689
+ ):
1690
+ _fail(path, "not_located must preserve its exact declared conflict")
1691
+ elif observation["status"] == "DOCUMENTED":
1692
+ if observation_reasons != ["STRUCTURE_DOCUMENTED"]:
1693
+ _fail(path, "DOCUMENTED must carry STRUCTURE_DOCUMENTED only")
1694
+ elif any(
1695
+ reason
1696
+ in {"STRUCTURE_DOCUMENTED", "DECLARED_ATTACHMENT_NOT_LOCATED"}
1697
+ for reason in observation_reasons
1698
+ ):
1699
+ _fail(path, "a located conflict has an invalid reason")
1700
+ if _digest(observations) != packet_pointer["observation_digest"]:
1701
+ _fail(
1702
+ "manifest.packet_pointer.observation_digest",
1703
+ "does not match the canonical packet observations",
1704
+ )
1705
+ if observations != sorted(
1706
+ observations, key=lambda row: (row["artifact_id"], row["relative_path"])
1707
+ ):
1708
+ _fail("manifest.packet_observations", "must use canonical artifact/path order")
1709
+
1710
+ envelope = _closed(
1711
+ root["capability_envelope"],
1712
+ "manifest.capability_envelope",
1713
+ {"state", "facts", "reason_codes"},
1714
+ )
1715
+ fact_rows = _array(
1716
+ envelope["facts"], "manifest.capability_envelope.facts", maximum=7, minimum=7
1717
+ )
1718
+ expected_values = dict(ENVELOPE_FACTS)
1719
+ fact_ids: set[str] = set()
1720
+ derived_envelope_reasons: list[str] = []
1721
+ for index, raw_fact in enumerate(fact_rows):
1722
+ path = f"manifest.capability_envelope.facts[{index}]"
1723
+ fact = _closed(
1724
+ raw_fact,
1725
+ path,
1726
+ {"fact_id", "required_value", "declared_state", "declared_value"},
1727
+ )
1728
+ fact_id = fact["fact_id"]
1729
+ if fact_id not in expected_values or fact_id in fact_ids:
1730
+ _fail(f"{path}.fact_id", "must identify one unique V1 envelope fact")
1731
+ fact_ids.add(fact_id)
1732
+ if fact["required_value"] is not expected_values[fact_id]:
1733
+ _fail(f"{path}.required_value", "does not match the V1 envelope")
1734
+ if fact["declared_state"] == "declared":
1735
+ if not isinstance(fact["declared_value"], bool):
1736
+ _fail(f"{path}.declared_value", "must be boolean when declared")
1737
+ if fact["declared_value"] != fact["required_value"]:
1738
+ reason = "V1_ENVELOPE_OUTSIDE"
1739
+ else:
1740
+ reason = None
1741
+ elif fact["declared_state"] == "unknown":
1742
+ if fact["declared_value"] is not None:
1743
+ _fail(f"{path}.declared_value", "must be null when unknown")
1744
+ reason = "V1_ENVELOPE_FACT_UNKNOWN"
1745
+ elif fact["declared_state"] == "missing":
1746
+ if fact["declared_value"] is not None:
1747
+ _fail(f"{path}.declared_value", "must be null when missing")
1748
+ reason = "V1_ENVELOPE_FACT_MISSING"
1749
+ else:
1750
+ _fail(f"{path}.declared_state", "has an invalid declaration state")
1751
+ if reason is not None and reason not in derived_envelope_reasons:
1752
+ derived_envelope_reasons.append(reason)
1753
+ if [row["fact_id"] for row in fact_rows] != [row[0] for row in ENVELOPE_FACTS]:
1754
+ _fail(
1755
+ "manifest.capability_envelope.facts",
1756
+ "must use the fixed V1 envelope fact order",
1757
+ )
1758
+ envelope_reasons = envelope["reason_codes"]
1759
+ if not isinstance(envelope_reasons, list) or envelope_reasons != derived_envelope_reasons:
1760
+ _fail(
1761
+ "manifest.capability_envelope.reason_codes",
1762
+ "must exactly summarize the ordered fact states",
1763
+ )
1764
+ expected_envelope_state = "within_v1" if not envelope_reasons else "unresolved"
1765
+ if envelope["state"] != expected_envelope_state:
1766
+ _fail("manifest.capability_envelope.state", "does not match its fact states")
1767
+
1768
+ overlay = root["overlay_selection_state"]
1769
+ if overlay is not None:
1770
+ overlay = _closed(
1771
+ overlay,
1772
+ "manifest.overlay_selection_state",
1773
+ {"institutional", "funder"},
1774
+ )
1775
+ for kind in ("institutional", "funder"):
1776
+ if overlay[kind] not in {
1777
+ "selected",
1778
+ "not_provided",
1779
+ "none_declared_by_author",
1780
+ }:
1781
+ _fail(f"manifest.overlay_selection_state.{kind}", "has an invalid state")
1782
+ if authority["state"] == "not_provided" and overlay is not None:
1783
+ _fail(
1784
+ "manifest.overlay_selection_state",
1785
+ "must be null when authority inputs are not provided",
1786
+ )
1787
+ if authority["state"] == "replay_validated" and overlay is None:
1788
+ _fail(
1789
+ "manifest.overlay_selection_state",
1790
+ "must be present when authority inputs are replay validated",
1791
+ )
1792
+
1793
+ administrative = _closed(
1794
+ root["administrative_status"],
1795
+ "manifest.administrative_status",
1796
+ {
1797
+ "review_pathway",
1798
+ "submission_readiness",
1799
+ "authorization_status",
1800
+ "review_timeline",
1801
+ },
1802
+ )
1803
+ if administrative["review_pathway"] != REVIEW_PATHWAY:
1804
+ _fail("manifest.administrative_status.review_pathway", "has changed")
1805
+ if administrative["submission_readiness"] not in READINESS_VALUES:
1806
+ _fail("manifest.administrative_status.submission_readiness", "has an invalid state")
1807
+ _validate_authorization_status(
1808
+ administrative["authorization_status"],
1809
+ "manifest.administrative_status.authorization_status",
1810
+ )
1811
+ if administrative["review_timeline"] != REVIEW_TIMELINE:
1812
+ _fail("manifest.administrative_status.review_timeline", "has changed")
1813
+
1814
+ acceptance = _closed(
1815
+ root["acceptance_boundary"],
1816
+ "manifest.acceptance_boundary",
1817
+ {"status", "reason_code", "affects_submission_readiness"},
1818
+ )
1819
+ if acceptance != {
1820
+ "status": "ACCEPTANCE_UNVERIFIED",
1821
+ "reason_code": ACCEPTANCE_REASON,
1822
+ "affects_submission_readiness": False,
1823
+ }:
1824
+ _fail("manifest.acceptance_boundary", "must preserve the fixed acceptance boundary")
1825
+
1826
+ entries = _array(root["entries"], "manifest.entries", maximum=4096)
1827
+ for index, raw_entry in enumerate(entries):
1828
+ path = f"manifest.entries[{index}]"
1829
+ entry = _closed(
1830
+ raw_entry,
1831
+ path,
1832
+ {
1833
+ "requirement_ref",
1834
+ "evidence_ref",
1835
+ "responsibility",
1836
+ "status",
1837
+ "readiness_effect",
1838
+ "reason_codes",
1839
+ "matched_artifact_ids",
1840
+ },
1841
+ )
1842
+ _validate_requirement_ref(entry["requirement_ref"], f"{path}.requirement_ref", "true")
1843
+ _validate_evidence_ref(
1844
+ entry["evidence_ref"],
1845
+ f"{path}.evidence_ref",
1846
+ entry["requirement_ref"]["requirement_pointer"],
1847
+ )
1848
+ if entry["responsibility"] not in {"packet_owned", "external_dependency"}:
1849
+ _fail(f"{path}.responsibility", "has an invalid responsibility")
1850
+ if entry["status"] not in STATUSES:
1851
+ _fail(f"{path}.status", "has an invalid status")
1852
+ if entry["readiness_effect"] not in {"none", "gap", "unresolved"}:
1853
+ _fail(f"{path}.readiness_effect", "has an invalid readiness effect")
1854
+ reason_codes = entry["reason_codes"]
1855
+ if not isinstance(reason_codes, list) or not 1 <= len(reason_codes) <= 16:
1856
+ _fail(f"{path}.reason_codes", "must be a non-empty bounded array")
1857
+ if any(not isinstance(code, str) for code in reason_codes) or len(
1858
+ set(reason_codes)
1859
+ ) != len(reason_codes) or any(
1860
+ code not in ENTRY_REASON_CODES for code in reason_codes
1861
+ ):
1862
+ _fail(f"{path}.reason_codes", "contains a duplicate or invalid reason")
1863
+ if reason_codes != sorted(reason_codes):
1864
+ _fail(f"{path}.reason_codes", "must use canonical reason-code order")
1865
+ _unique_identifiers(
1866
+ entry["matched_artifact_ids"], f"{path}.matched_artifact_ids"
1867
+ )
1868
+ if entry["matched_artifact_ids"] != sorted(entry["matched_artifact_ids"]):
1869
+ _fail(f"{path}.matched_artifact_ids", "must use canonical identifier order")
1870
+ if entry["status"] == "DOCUMENTED" and (
1871
+ entry["responsibility"], entry["readiness_effect"]
1872
+ ) != ("packet_owned", "none"):
1873
+ _fail(path, "DOCUMENTED must be packet-owned with no readiness effect")
1874
+ if entry["status"] in {"NOT_LOCATED", "CONFLICTING"} and (
1875
+ entry["responsibility"], entry["readiness_effect"]
1876
+ ) != ("packet_owned", "gap"):
1877
+ _fail(path, "a located packet gap must remain packet-owned")
1878
+ if entry["status"] == "APPLICABILITY_UNRESOLVED" and entry[
1879
+ "readiness_effect"
1880
+ ] != "unresolved":
1881
+ _fail(path, "unresolved applicability must leave readiness unresolved")
1882
+ if entry["status"] == "ACCEPTANCE_UNVERIFIED":
1883
+ expected = (
1884
+ "none" if entry["responsibility"] == "external_dependency" else "unresolved"
1885
+ )
1886
+ if entry["readiness_effect"] != expected:
1887
+ _fail(path, "acceptance state has an invalid readiness effect")
1888
+ entry_keys = [
1889
+ (
1890
+ row["requirement_ref"]["axis"],
1891
+ row["requirement_ref"]["authority_kind"],
1892
+ row["requirement_ref"]["authority_id"],
1893
+ row["requirement_ref"]["requirement_id"],
1894
+ row["evidence_ref"]["evidence_pointer"],
1895
+ )
1896
+ for row in entries
1897
+ ]
1898
+ if entry_keys != sorted(entry_keys) or len(set(entry_keys)) != len(entry_keys):
1899
+ _fail("manifest.entries", "must use unique canonical semantic order")
1900
+
1901
+ excluded = _array(
1902
+ root["excluded_requirements"], "manifest.excluded_requirements", maximum=4096
1903
+ )
1904
+ for index, raw_excluded in enumerate(excluded):
1905
+ path = f"manifest.excluded_requirements[{index}]"
1906
+ row = _closed(raw_excluded, path, {"requirement_ref", "exclusion_basis"})
1907
+ _validate_requirement_ref(row["requirement_ref"], f"{path}.requirement_ref", "false")
1908
+ if row["exclusion_basis"] != "resolved_predicate_false":
1909
+ _fail(f"{path}.exclusion_basis", "has changed")
1910
+ excluded_keys = [
1911
+ (
1912
+ row["requirement_ref"]["axis"],
1913
+ row["requirement_ref"]["authority_kind"],
1914
+ row["requirement_ref"]["authority_id"],
1915
+ row["requirement_ref"]["requirement_id"],
1916
+ )
1917
+ for row in excluded
1918
+ ]
1919
+ if excluded_keys != sorted(excluded_keys) or len(set(excluded_keys)) != len(
1920
+ excluded_keys
1921
+ ):
1922
+ _fail("manifest.excluded_requirements", "must use unique canonical order")
1923
+
1924
+ unresolved = _array(
1925
+ root["unresolved_reasons"], "manifest.unresolved_reasons", maximum=128
1926
+ )
1927
+ if len({_canonical_bytes(row) for row in unresolved}) != len(unresolved):
1928
+ _fail("manifest.unresolved_reasons", "contains a duplicate reason")
1929
+ for index, raw_reason in enumerate(unresolved):
1930
+ path = f"manifest.unresolved_reasons[{index}]"
1931
+ reason = _closed(raw_reason, path, {"status", "code", "fact_id", "overlay_kind"})
1932
+ if reason["status"] != "APPLICABILITY_UNRESOLVED":
1933
+ _fail(f"{path}.status", "must equal APPLICABILITY_UNRESOLVED")
1934
+ if reason["code"] not in GLOBAL_REASON_CODES:
1935
+ _fail(f"{path}.code", "has an invalid unresolved reason")
1936
+ if reason["fact_id"] is not None:
1937
+ _identifier(reason["fact_id"], f"{path}.fact_id")
1938
+ if reason["overlay_kind"] not in {None, "institutional", "funder"}:
1939
+ _fail(f"{path}.overlay_kind", "has an invalid overlay kind")
1940
+
1941
+ expected_unresolved: list[dict[str, Any]] = []
1942
+ gate_permitted = bool(
1943
+ authority["state"] == "replay_validated"
1944
+ and authority["resolution_state"] == "resolved"
1945
+ and authority["downstream_gate"]["profile_dependent_result_allowed"]
1946
+ )
1947
+ if authority["state"] == "not_provided":
1948
+ expected_unresolved.append(
1949
+ {
1950
+ "status": "APPLICABILITY_UNRESOLVED",
1951
+ "code": "AUTHORITY_INPUT_NOT_PROVIDED",
1952
+ "fact_id": None,
1953
+ "overlay_kind": None,
1954
+ }
1955
+ )
1956
+ elif not gate_permitted:
1957
+ expected_unresolved.append(
1958
+ {
1959
+ "status": "APPLICABILITY_UNRESOLVED",
1960
+ "code": "AUTHORITY_RESOLUTION_NOT_PERMITTED",
1961
+ "fact_id": None,
1962
+ "overlay_kind": None,
1963
+ }
1964
+ )
1965
+ for fact in fact_rows:
1966
+ if fact["declared_state"] == "missing":
1967
+ code = "V1_ENVELOPE_FACT_MISSING"
1968
+ elif fact["declared_state"] == "unknown":
1969
+ code = "V1_ENVELOPE_FACT_UNKNOWN"
1970
+ elif fact["declared_value"] != fact["required_value"]:
1971
+ code = "V1_ENVELOPE_OUTSIDE"
1972
+ else:
1973
+ code = None
1974
+ if code is not None:
1975
+ expected_unresolved.append(
1976
+ {
1977
+ "status": "APPLICABILITY_UNRESOLVED",
1978
+ "code": code,
1979
+ "fact_id": fact["fact_id"],
1980
+ "overlay_kind": None,
1981
+ }
1982
+ )
1983
+ if overlay is not None:
1984
+ for kind in ("institutional", "funder"):
1985
+ if overlay[kind] == "not_provided":
1986
+ expected_unresolved.append(
1987
+ {
1988
+ "status": "APPLICABILITY_UNRESOLVED",
1989
+ "code": "OVERLAY_SELECTION_NOT_PROVIDED",
1990
+ "fact_id": None,
1991
+ "overlay_kind": kind,
1992
+ }
1993
+ )
1994
+ expected_unresolved.sort(
1995
+ key=lambda row: (
1996
+ row["code"],
1997
+ row["fact_id"] or "",
1998
+ row["overlay_kind"] or "",
1999
+ )
2000
+ )
2001
+ if _canonical_bytes(unresolved) != _canonical_bytes(expected_unresolved):
2002
+ _fail(
2003
+ "manifest.unresolved_reasons",
2004
+ "does not exactly match authority, envelope, and overlay states",
2005
+ )
2006
+
2007
+ if not gate_permitted or envelope["state"] != "within_v1":
2008
+ if entries or excluded or observations:
2009
+ _fail(
2010
+ "manifest",
2011
+ "a closed authority or capability gate must have no entries, exclusions, or packet observations",
2012
+ )
2013
+ expected_readiness = _readiness(unresolved, entries)
2014
+ if administrative["submission_readiness"] != expected_readiness:
2015
+ _fail(
2016
+ "manifest.administrative_status.submission_readiness",
2017
+ "does not match unresolved and packet-owned readiness effects",
2018
+ )
2019
+
2020
+ if root["boundary_footer"] != BOUNDARY_FOOTER:
2021
+ _fail("manifest.boundary_footer", "has changed")
2022
+
2023
+
2024
+ def validate_submission_packet_manifest(
2025
+ manifest: dict[str, Any],
2026
+ inventory: dict[str, Any],
2027
+ packet_root: Path | str,
2028
+ *,
2029
+ context: dict[str, Any] | None = None,
2030
+ registry: dict[str, Any] | None = None,
2031
+ resolved: dict[str, Any] | None = None,
2032
+ ) -> None:
2033
+ """Validate self-digests and exact deterministic replay from all named inputs."""
2034
+ supplied = (context is not None, registry is not None, resolved is not None)
2035
+ if any(supplied) and not all(supplied):
2036
+ raise ContractError("context, registry, and resolved authority inputs are all-or-none")
2037
+ if all(supplied):
2038
+ assert context is not None and registry is not None and resolved is not None
2039
+ validate_resolved_context(resolved, context, registry)
2040
+ _validate_manifest_shape(manifest)
2041
+ expected = build_submission_packet_manifest(
2042
+ inventory,
2043
+ packet_root,
2044
+ context=context,
2045
+ registry=registry,
2046
+ resolved=resolved,
2047
+ )
2048
+ if _canonical_bytes(manifest) != _canonical_bytes(expected):
2049
+ _fail(
2050
+ "manifest",
2051
+ "does not exactly match deterministic replay from the named inputs",
2052
+ )
2053
+
2054
+
2055
+ def _md(value: Any) -> str:
2056
+ if value is None:
2057
+ return "not provided"
2058
+ if isinstance(value, bool):
2059
+ return "true" if value else "false"
2060
+ # Encode every ASCII punctuation mark as an entity. Markdown parses the
2061
+ # entity as text before browsers decode its visible character, so an
2062
+ # inventory path cannot create a link, image, HTML tag, code span, fence,
2063
+ # table delimiter, or extension-specific attribute syntax.
2064
+ return "".join(
2065
+ f"&#{ord(character)};"
2066
+ if (
2067
+ character.isascii()
2068
+ and not character.isalnum()
2069
+ and not character.isspace()
2070
+ )
2071
+ or unicodedata.category(character) in {"Zl", "Zp"}
2072
+ else character
2073
+ for character in str(value)
2074
+ )
2075
+
2076
+
2077
+ def render_submission_packet_manifest(manifest: dict[str, Any]) -> str:
2078
+ """Render a shape- and self-digest-validated trusted manifest carrier."""
2079
+ _validate_manifest_shape(manifest)
2080
+ administrative = manifest["administrative_status"]
2081
+ authorization = administrative["authorization_status"]
2082
+ lines = [
2083
+ "# Human-Subjects Submission-Packet Manifest",
2084
+ "",
2085
+ "## Human-Subjects Administrative Status",
2086
+ "",
2087
+ f"Review pathway: {administrative['review_pathway']}",
2088
+ f"Submission readiness: {administrative['submission_readiness']}",
2089
+ f"Authorization status: {authorization['value']}",
2090
+ f"Review timeline: {administrative['review_timeline']}",
2091
+ f"Authorization source reference: {_md(authorization['source_reference'])}",
2092
+ f"Authorization provenance: {_md(authorization['provenance'])}",
2093
+ "",
2094
+ "Readiness and caller-supplied authorization are independent. This manifest does not derive or update authorization.",
2095
+ "",
2096
+ "## Packet Observations",
2097
+ "",
2098
+ "| Artifact | Relative path | State | Status | Reason codes |",
2099
+ "|---|---|---|---|---|",
2100
+ ]
2101
+ if manifest["packet_observations"]:
2102
+ for observation in manifest["packet_observations"]:
2103
+ lines.append(
2104
+ "| "
2105
+ + " | ".join(
2106
+ [
2107
+ _md(observation["artifact_id"]),
2108
+ _md(observation["relative_path"]),
2109
+ _md(observation["state"]),
2110
+ _md(observation["status"]),
2111
+ _md(", ".join(observation["reason_codes"])),
2112
+ ]
2113
+ )
2114
+ + " |"
2115
+ )
2116
+ else:
2117
+ lines.append("| none | none | none | none | none |")
2118
+
2119
+ lines.extend(
2120
+ [
2121
+ "",
2122
+ "## Mechanical Evidence Entries",
2123
+ "",
2124
+ "| Requirement | Evidence | Responsibility | Status | Readiness effect | Matched artifact IDs | Reason codes |",
2125
+ "|---|---|---|---|---|---|---|",
2126
+ ]
2127
+ )
2128
+ if manifest["entries"]:
2129
+ for entry in manifest["entries"]:
2130
+ lines.append(
2131
+ "| "
2132
+ + " | ".join(
2133
+ [
2134
+ _md(entry["requirement_ref"]["requirement_id"]),
2135
+ _md(entry["evidence_ref"]["evidence_id"]),
2136
+ _md(entry["responsibility"]),
2137
+ _md(entry["status"]),
2138
+ _md(entry["readiness_effect"]),
2139
+ _md(", ".join(entry["matched_artifact_ids"]) or "none"),
2140
+ _md(", ".join(entry["reason_codes"])),
2141
+ ]
2142
+ )
2143
+ + " |"
2144
+ )
2145
+ else:
2146
+ lines.append("| none | none | none | none | none | none | none |")
2147
+
2148
+ lines.extend(
2149
+ [
2150
+ "",
2151
+ "## Excluded Requirements",
2152
+ "",
2153
+ "| Requirement | Requirement pointer | Exclusion basis |",
2154
+ "|---|---|---|",
2155
+ ]
2156
+ )
2157
+ if manifest["excluded_requirements"]:
2158
+ for excluded in manifest["excluded_requirements"]:
2159
+ lines.append(
2160
+ "| "
2161
+ + " | ".join(
2162
+ [
2163
+ _md(excluded["requirement_ref"]["requirement_id"]),
2164
+ _md(excluded["requirement_ref"]["requirement_pointer"]),
2165
+ _md(excluded["exclusion_basis"]),
2166
+ ]
2167
+ )
2168
+ + " |"
2169
+ )
2170
+ else:
2171
+ lines.append("| none | none | none |")
2172
+
2173
+ lines.extend(
2174
+ [
2175
+ "",
2176
+ "## Unresolved States",
2177
+ "",
2178
+ "| Status | Code | Fact ID | Overlay kind |",
2179
+ "|---|---|---|---|",
2180
+ ]
2181
+ )
2182
+ if manifest["unresolved_reasons"]:
2183
+ for reason in manifest["unresolved_reasons"]:
2184
+ lines.append(
2185
+ "| "
2186
+ + " | ".join(
2187
+ _md(reason[field])
2188
+ for field in ("status", "code", "fact_id", "overlay_kind")
2189
+ )
2190
+ + " |"
2191
+ )
2192
+ else:
2193
+ lines.append("| none | none | none | none |")
2194
+
2195
+ lines.extend(
2196
+ [
2197
+ "",
2198
+ "## Institutional Acceptance Boundary",
2199
+ "",
2200
+ "| Status | Reason | Affects submission readiness |",
2201
+ "|---|---|---|",
2202
+ "| "
2203
+ + " | ".join(
2204
+ [
2205
+ _md(manifest["acceptance_boundary"]["status"]),
2206
+ _md(manifest["acceptance_boundary"]["reason_code"]),
2207
+ _md(
2208
+ manifest["acceptance_boundary"][
2209
+ "affects_submission_readiness"
2210
+ ]
2211
+ ),
2212
+ ]
2213
+ )
2214
+ + " |",
2215
+ "",
2216
+ "> **Human-subjects boundary:** This output does not authorize recruitment, consent, access to identifiable data, intervention, or data collection.",
2217
+ "",
2218
+ ]
2219
+ )
2220
+ return "\n".join(lines)
2221
+
2222
+
2223
+ def _add_authority_arguments(parser: argparse.ArgumentParser) -> None:
2224
+ parser.add_argument("--context", type=Path)
2225
+ parser.add_argument("--registry", type=Path)
2226
+ parser.add_argument("--resolved", type=Path)
2227
+
2228
+
2229
+ def _load_authority_arguments(
2230
+ args: argparse.Namespace,
2231
+ ) -> tuple[dict[str, Any] | None, dict[str, Any] | None, dict[str, Any] | None]:
2232
+ paths = (args.context, args.registry, args.resolved)
2233
+ if any(path is not None for path in paths) and not all(
2234
+ path is not None for path in paths
2235
+ ):
2236
+ raise ContractError("--context, --registry, and --resolved are all-or-none")
2237
+ if not all(path is not None for path in paths):
2238
+ return None, None, None
2239
+ return load_json(args.context), load_json(args.registry), load_json(args.resolved)
2240
+
2241
+
2242
+ def _emit(text: str, output: Path | None) -> None:
2243
+ if output is None:
2244
+ sys.stdout.write(text)
2245
+ else:
2246
+ output.write_text(text, encoding="utf-8")
2247
+
2248
+
2249
+ def main(argv: list[str] | None = None) -> int:
2250
+ parser = argparse.ArgumentParser(allow_abbrev=False)
2251
+ subparsers = parser.add_subparsers(dest="command", required=True)
2252
+
2253
+ build_parser = subparsers.add_parser("build", allow_abbrev=False)
2254
+ build_parser.add_argument("--inventory", type=Path, required=True)
2255
+ build_parser.add_argument("--packet-root", type=Path, required=True)
2256
+ build_parser.add_argument("--output", type=Path)
2257
+ _add_authority_arguments(build_parser)
2258
+
2259
+ validate_parser = subparsers.add_parser("validate", allow_abbrev=False)
2260
+ validate_parser.add_argument("--manifest", type=Path, required=True)
2261
+ validate_parser.add_argument("--inventory", type=Path, required=True)
2262
+ validate_parser.add_argument("--packet-root", type=Path, required=True)
2263
+ _add_authority_arguments(validate_parser)
2264
+
2265
+ render_parser = subparsers.add_parser("render", allow_abbrev=False)
2266
+ render_parser.add_argument("--manifest", type=Path, required=True)
2267
+ render_parser.add_argument("--inventory", type=Path, required=True)
2268
+ render_parser.add_argument("--packet-root", type=Path, required=True)
2269
+ render_parser.add_argument("--output", type=Path)
2270
+ _add_authority_arguments(render_parser)
2271
+
2272
+ args = parser.parse_args(argv)
2273
+ try:
2274
+ context, registry, resolved = _load_authority_arguments(args)
2275
+ inventory = load_json(args.inventory)
2276
+ if args.command == "build":
2277
+ manifest = build_submission_packet_manifest(
2278
+ inventory,
2279
+ args.packet_root,
2280
+ context=context,
2281
+ registry=registry,
2282
+ resolved=resolved,
2283
+ )
2284
+ # Compact canonical output stays within the same 8 MiB limit used
2285
+ # by the strict JSON loader, preserving build -> validate/render
2286
+ # round trips even at the exact boundary.
2287
+ _emit(_canonical_bytes(manifest).decode("utf-8"), args.output)
2288
+ return 0
2289
+
2290
+ manifest = load_json(args.manifest)
2291
+ validate_submission_packet_manifest(
2292
+ manifest,
2293
+ inventory,
2294
+ args.packet_root,
2295
+ context=context,
2296
+ registry=registry,
2297
+ resolved=resolved,
2298
+ )
2299
+ if args.command == "validate":
2300
+ print("Submission-packet manifest validation passed (#667).")
2301
+ return 0
2302
+ _emit(render_submission_packet_manifest(manifest), args.output)
2303
+ return 0
2304
+ except (ContractError, OSError, ValueError, RecursionError, UnicodeError) as exc:
2305
+ print(f"ERROR: {exc}", file=sys.stderr)
2306
+ return 2
2307
+
2308
+
2309
+ if __name__ == "__main__":
2310
+ raise SystemExit(main())