briefloop 0.11.12__py3-none-any.whl

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 (424) hide show
  1. briefloop-0.11.12.dist-info/METADATA +473 -0
  2. briefloop-0.11.12.dist-info/RECORD +424 -0
  3. briefloop-0.11.12.dist-info/WHEEL +5 -0
  4. briefloop-0.11.12.dist-info/entry_points.txt +3 -0
  5. briefloop-0.11.12.dist-info/licenses/LICENSE +22 -0
  6. briefloop-0.11.12.dist-info/top_level.txt +1 -0
  7. multi_agent_brief/__init__.py +36 -0
  8. multi_agent_brief/analysis_blocks/__init__.py +11 -0
  9. multi_agent_brief/analysis_blocks/builder.py +206 -0
  10. multi_agent_brief/analysis_blocks/renderer.py +237 -0
  11. multi_agent_brief/analysis_blocks/schemas.py +52 -0
  12. multi_agent_brief/analysis_modules/__init__.py +8 -0
  13. multi_agent_brief/analysis_modules/base.py +87 -0
  14. multi_agent_brief/analysis_modules/market_competitor/__init__.py +68 -0
  15. multi_agent_brief/analysis_modules/market_competitor/auditor.py +290 -0
  16. multi_agent_brief/analysis_modules/market_competitor/config.py +187 -0
  17. multi_agent_brief/analysis_modules/market_competitor/event_builder.py +226 -0
  18. multi_agent_brief/analysis_modules/market_competitor/renderer.py +199 -0
  19. multi_agent_brief/analysis_modules/market_competitor/schemas.py +518 -0
  20. multi_agent_brief/analysis_modules/policy_regulatory/__init__.py +7 -0
  21. multi_agent_brief/analysis_modules/policy_regulatory/audit.py +195 -0
  22. multi_agent_brief/analysis_modules/policy_regulatory/module.py +387 -0
  23. multi_agent_brief/analysis_modules/policy_regulatory/schemas.py +129 -0
  24. multi_agent_brief/analysis_modules/registry.py +73 -0
  25. multi_agent_brief/audience/__init__.py +19 -0
  26. multi_agent_brief/audience/profiles.py +246 -0
  27. multi_agent_brief/audience_memory/__init__.py +23 -0
  28. multi_agent_brief/audience_memory/profile.py +358 -0
  29. multi_agent_brief/audit/__init__.py +36 -0
  30. multi_agent_brief/audit/case_applicability.py +181 -0
  31. multi_agent_brief/audit/deterministic.py +471 -0
  32. multi_agent_brief/audit/editorial_governance.py +404 -0
  33. multi_agent_brief/audit/final_quality.py +764 -0
  34. multi_agent_brief/audit/harness.py +242 -0
  35. multi_agent_brief/audit/interfaces.py +96 -0
  36. multi_agent_brief/audit/limitation_hygiene.py +238 -0
  37. multi_agent_brief/audit/proposal_boundary.py +34 -0
  38. multi_agent_brief/audit/redaction.py +27 -0
  39. multi_agent_brief/audit/rule_packs.py +96 -0
  40. multi_agent_brief/audit/semantic.py +435 -0
  41. multi_agent_brief/capabilities/__init__.py +22 -0
  42. multi_agent_brief/capabilities/catalog.py +215 -0
  43. multi_agent_brief/capabilities/detect.py +199 -0
  44. multi_agent_brief/capabilities/models.py +60 -0
  45. multi_agent_brief/capabilities/recommend.py +206 -0
  46. multi_agent_brief/cli/__init__.py +2 -0
  47. multi_agent_brief/cli/analysis_commands.py +140 -0
  48. multi_agent_brief/cli/approval_commands.py +107 -0
  49. multi_agent_brief/cli/audit_commands.py +59 -0
  50. multi_agent_brief/cli/capability_commands.py +548 -0
  51. multi_agent_brief/cli/claude_commands.py +159 -0
  52. multi_agent_brief/cli/competitors_commands.py +182 -0
  53. multi_agent_brief/cli/controls_commands.py +137 -0
  54. multi_agent_brief/cli/deliver_commands.py +942 -0
  55. multi_agent_brief/cli/eval_cases_commands.py +99 -0
  56. multi_agent_brief/cli/experiments_commands.py +395 -0
  57. multi_agent_brief/cli/feedback_commands.py +192 -0
  58. multi_agent_brief/cli/finalize_commands.py +153 -0
  59. multi_agent_brief/cli/gates_commands.py +174 -0
  60. multi_agent_brief/cli/hermes_commands.py +356 -0
  61. multi_agent_brief/cli/improve_commands.py +224 -0
  62. multi_agent_brief/cli/init_commands.py +564 -0
  63. multi_agent_brief/cli/init_wizard.py +1473 -0
  64. multi_agent_brief/cli/input_commands.py +237 -0
  65. multi_agent_brief/cli/main.py +288 -0
  66. multi_agent_brief/cli/onboard_commands.py +193 -0
  67. multi_agent_brief/cli/product_commands.py +1449 -0
  68. multi_agent_brief/cli/provenance_commands.py +100 -0
  69. multi_agent_brief/cli/release_commands.py +68 -0
  70. multi_agent_brief/cli/repair_commands.py +169 -0
  71. multi_agent_brief/cli/run_commands.py +292 -0
  72. multi_agent_brief/cli/runtime_commands.py +81 -0
  73. multi_agent_brief/cli/secrets_commands.py +232 -0
  74. multi_agent_brief/cli/semantic_support_commands.py +80 -0
  75. multi_agent_brief/cli/sources_commands.py +647 -0
  76. multi_agent_brief/cli/start_commands.py +8 -0
  77. multi_agent_brief/cli/state_commands.py +446 -0
  78. multi_agent_brief/cli/status_commands.py +26 -0
  79. multi_agent_brief/cli/workbuddy_commands.py +103 -0
  80. multi_agent_brief/configs/artifact_contracts.yaml +514 -0
  81. multi_agent_brief/configs/orchestrator_contract.yaml +181 -0
  82. multi_agent_brief/configs/policy_packs/default.yaml +65 -0
  83. multi_agent_brief/configs/policy_profiles/evidence_extract_default.yaml +58 -0
  84. multi_agent_brief/configs/policy_profiles/finance_default.yaml +44 -0
  85. multi_agent_brief/configs/policy_profiles/internet_default.yaml +44 -0
  86. multi_agent_brief/configs/policy_profiles/manufacturing_default.yaml +33 -0
  87. multi_agent_brief/configs/policy_profiles/solar_manufacturing_default.yaml +64 -0
  88. multi_agent_brief/configs/report_packs/evidence_extract.yaml +48 -0
  89. multi_agent_brief/configs/report_packs/management_monthly.yaml +36 -0
  90. multi_agent_brief/configs/report_packs/market_weekly.yaml +36 -0
  91. multi_agent_brief/configs/report_packs/solar_industry_periodic.yaml +50 -0
  92. multi_agent_brief/configs/report_templates/evidence_extract.yaml +55 -0
  93. multi_agent_brief/configs/report_templates/management_monthly.yaml +48 -0
  94. multi_agent_brief/configs/report_templates/market_weekly.yaml +49 -0
  95. multi_agent_brief/configs/report_templates/solar_industry_periodic.yaml +75 -0
  96. multi_agent_brief/configs/stage_specs.yaml +180 -0
  97. multi_agent_brief/contracts/__init__.py +41 -0
  98. multi_agent_brief/contracts/base.py +99 -0
  99. multi_agent_brief/contracts/errors.py +41 -0
  100. multi_agent_brief/contracts/migrations/__init__.py +5 -0
  101. multi_agent_brief/contracts/migrations/claim_v1_to_v2.py +33 -0
  102. multi_agent_brief/contracts/registry.py +159 -0
  103. multi_agent_brief/contracts/role_topology.py +25 -0
  104. multi_agent_brief/contracts/schemas/__init__.py +35 -0
  105. multi_agent_brief/contracts/schemas/analysis_pack.py +135 -0
  106. multi_agent_brief/contracts/schemas/atomic_claim_graph.py +304 -0
  107. multi_agent_brief/contracts/schemas/audit_report.py +80 -0
  108. multi_agent_brief/contracts/schemas/candidate_item.py +51 -0
  109. multi_agent_brief/contracts/schemas/claim.py +197 -0
  110. multi_agent_brief/contracts/schemas/claim_draft.py +386 -0
  111. multi_agent_brief/contracts/schemas/claim_support_matrix.py +334 -0
  112. multi_agent_brief/contracts/schemas/evidence_span_registry.py +270 -0
  113. multi_agent_brief/contracts/schemas/policy_profile.py +247 -0
  114. multi_agent_brief/contracts/schemas/report_spec.py +257 -0
  115. multi_agent_brief/contracts/schemas/semantic_assessment_report.py +480 -0
  116. multi_agent_brief/contracts/schemas/source_evidence_pack_manifest.py +233 -0
  117. multi_agent_brief/contracts/schemas/source_item.py +96 -0
  118. multi_agent_brief/contracts/semantic_assessment_status.py +12 -0
  119. multi_agent_brief/contracts/source_metadata.py +252 -0
  120. multi_agent_brief/contracts/target_contract.py +460 -0
  121. multi_agent_brief/contracts/validator.py +225 -0
  122. multi_agent_brief/controls/__init__.py +23 -0
  123. multi_agent_brief/controls/contract.py +106 -0
  124. multi_agent_brief/controls/switchboard.py +1076 -0
  125. multi_agent_brief/core/__init__.py +2 -0
  126. multi_agent_brief/core/citations.py +14 -0
  127. multi_agent_brief/core/claim_ledger.py +94 -0
  128. multi_agent_brief/core/config.py +191 -0
  129. multi_agent_brief/core/env.py +69 -0
  130. multi_agent_brief/core/previous.py +83 -0
  131. multi_agent_brief/core/schemas.py +181 -0
  132. multi_agent_brief/core/selection.py +189 -0
  133. multi_agent_brief/delivery/__init__.py +6 -0
  134. multi_agent_brief/delivery/base.py +36 -0
  135. multi_agent_brief/delivery/feishu.py +204 -0
  136. multi_agent_brief/delivery/gws.py +237 -0
  137. multi_agent_brief/evaluation_cases/__init__.py +15 -0
  138. multi_agent_brief/evaluation_cases/contract.py +429 -0
  139. multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/config.yaml +18 -0
  140. multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/improvement/ledger.jsonl +2 -0
  141. multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/sources.yaml +2 -0
  142. multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/user.md +4 -0
  143. multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/config.yaml +13 -0
  144. multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/sources.yaml +9 -0
  145. multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/user.md +4 -0
  146. multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/config.yaml +12 -0
  147. multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/sources.yaml +6 -0
  148. multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/user.md +4 -0
  149. multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/config.yaml +6 -0
  150. multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/input/human_feedback.md +1 -0
  151. multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/sources.yaml +2 -0
  152. multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/user.md +3 -0
  153. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/config.yaml +8 -0
  154. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/brief.md +14 -0
  155. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audit_report.json +1 -0
  156. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audited_brief.md +5 -0
  157. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/candidate_claims.json +1 -0
  158. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/claim_ledger.json +1 -0
  159. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/screened_candidates.json +1 -0
  160. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/sources.yaml +2 -0
  161. multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/user.md +3 -0
  162. multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/config.yaml +13 -0
  163. multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/sources.yaml +9 -0
  164. multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/user.md +4 -0
  165. multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/config.yaml +4 -0
  166. multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/sources.yaml +2 -0
  167. multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/user.md +3 -0
  168. multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/config.yaml +13 -0
  169. multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/sources.yaml +9 -0
  170. multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/user.md +4 -0
  171. multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/config.yaml +13 -0
  172. multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/sources.yaml +9 -0
  173. multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/user.md +5 -0
  174. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/config.yaml +6 -0
  175. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/audited_brief.md +3 -0
  176. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/candidate_claims.json +1 -0
  177. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/claim_ledger.json +1 -0
  178. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/feedback_issues.json +25 -0
  179. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/repair_plan.json +27 -0
  180. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/screened_candidates.json +1 -0
  181. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/sources.yaml +2 -0
  182. multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/user.md +3 -0
  183. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/config.yaml +8 -0
  184. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audit_report.json +1 -0
  185. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audited_brief.md +3 -0
  186. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/candidate_claims.json +1 -0
  187. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/claim_ledger.json +11 -0
  188. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/quality_gate_report.json +22 -0
  189. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/screened_candidates.json +1 -0
  190. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/sources.yaml +2 -0
  191. multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/user.md +3 -0
  192. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/config.yaml +13 -0
  193. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audit_report.json +1 -0
  194. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audited_brief.md +5 -0
  195. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/candidate_claims.json +1 -0
  196. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/claim_ledger.json +40 -0
  197. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/screened_candidates.json +1 -0
  198. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/sources.yaml +3 -0
  199. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/user.md +3 -0
  200. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/config.yaml +8 -0
  201. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/brief.md +3 -0
  202. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audit_report.json +1 -0
  203. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audited_brief.md +3 -0
  204. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/candidate_claims.json +1 -0
  205. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/claim_ledger.json +13 -0
  206. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/screened_candidates.json +1 -0
  207. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/sources.yaml +2 -0
  208. multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/user.md +3 -0
  209. multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/config.yaml +13 -0
  210. multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/output/intermediate/release_readiness_report.json +31 -0
  211. multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/sources.yaml +9 -0
  212. multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/user.md +4 -0
  213. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/config.yaml +18 -0
  214. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/ledger.jsonl +3 -0
  215. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/memory.md +3 -0
  216. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/output/intermediate/improvement_memory_snapshot.md +3 -0
  217. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/sources.yaml +2 -0
  218. multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/user.md +4 -0
  219. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/config.yaml +12 -0
  220. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/input/sources/source-001.txt +1 -0
  221. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/brief.md +37 -0
  222. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/delivery/brief.md +37 -0
  223. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/atomic_claim_graph.json +17 -0
  224. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audit_report.json +5 -0
  225. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audited_brief.md +37 -0
  226. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/claim_ledger.json +20 -0
  227. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/evidence_span_registry.json +20 -0
  228. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/finalize_report.json +40 -0
  229. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/auditor_quality_gate_report.json +42 -0
  230. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/finalize_quality_gate_report.json +42 -0
  231. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/screened_candidates.json +33 -0
  232. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix.md +12 -0
  233. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix_trace.md +10 -0
  234. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/report_spec.yaml +24 -0
  235. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/sources.yaml +4 -0
  236. multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/user.md +3 -0
  237. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/config.yaml +13 -0
  238. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/input/sources/README.md +4 -0
  239. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/output/intermediate/source_evidence_pack_manifest.json +26 -0
  240. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/sources.yaml +9 -0
  241. multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/user.md +4 -0
  242. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/config.yaml +9 -0
  243. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audit_report.json +1 -0
  244. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audited_brief.md +3 -0
  245. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/candidate_claims.json +1 -0
  246. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/claim_ledger.json +13 -0
  247. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/screened_candidates.json +1 -0
  248. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/sources.yaml +2 -0
  249. multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/user.md +3 -0
  250. multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/config.yaml +13 -0
  251. multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/sources.yaml +9 -0
  252. multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/user.md +4 -0
  253. multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/config.yaml +4 -0
  254. multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/sources.yaml +2 -0
  255. multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/user.md +3 -0
  256. multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/config.yaml +18 -0
  257. multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/improvement/ledger.jsonl +1 -0
  258. multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/sources.yaml +2 -0
  259. multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/user.md +4 -0
  260. multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/config.yaml +18 -0
  261. multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/sources.yaml +9 -0
  262. multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/user.md +5 -0
  263. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/config.yaml +8 -0
  264. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audit_report.json +1 -0
  265. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audited_brief.md +8 -0
  266. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/candidate_claims.json +1 -0
  267. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/claim_ledger.json +1 -0
  268. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/screened_candidates.json +1 -0
  269. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/sources.yaml +2 -0
  270. multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/user.md +3 -0
  271. multi_agent_brief/evaluation_cases/fixtures/manifest.yaml +861 -0
  272. multi_agent_brief/evaluation_cases/fixtures.py +20 -0
  273. multi_agent_brief/evaluation_cases/runner.py +1603 -0
  274. multi_agent_brief/experiments/__init__.py +45 -0
  275. multi_agent_brief/experiments/experiment_080.py +5835 -0
  276. multi_agent_brief/experiments/schemas.py +63 -0
  277. multi_agent_brief/experiments/target_contract.py +10 -0
  278. multi_agent_brief/feedback/__init__.py +17 -0
  279. multi_agent_brief/feedback/feedback_contract.py +482 -0
  280. multi_agent_brief/feedback/feedback_state.py +898 -0
  281. multi_agent_brief/hermes/__init__.py +25 -0
  282. multi_agent_brief/hermes/adapter.py +832 -0
  283. multi_agent_brief/improvement/__init__.py +83 -0
  284. multi_agent_brief/improvement/contract.py +1114 -0
  285. multi_agent_brief/improvement/memory.py +451 -0
  286. multi_agent_brief/improvement/product_definition.py +228 -0
  287. multi_agent_brief/improvement/state.py +961 -0
  288. multi_agent_brief/inputs/__init__.py +2 -0
  289. multi_agent_brief/inputs/classifier.py +322 -0
  290. multi_agent_brief/inputs/contracts.py +59 -0
  291. multi_agent_brief/inputs/extractor.py +310 -0
  292. multi_agent_brief/install/__init__.py +2 -0
  293. multi_agent_brief/install/writer.py +97 -0
  294. multi_agent_brief/onboarding/__init__.py +11 -0
  295. multi_agent_brief/onboarding/io.py +103 -0
  296. multi_agent_brief/onboarding/mapper.py +484 -0
  297. multi_agent_brief/onboarding/schema.py +46 -0
  298. multi_agent_brief/orchestrator/__init__.py +2 -0
  299. multi_agent_brief/orchestrator/fact_layer_import.py +352 -0
  300. multi_agent_brief/orchestrator/handoff.py +2209 -0
  301. multi_agent_brief/orchestrator/role_topology.py +42 -0
  302. multi_agent_brief/orchestrator/run_archive.py +888 -0
  303. multi_agent_brief/orchestrator/run_integrity.py +346 -0
  304. multi_agent_brief/orchestrator/runtime_state/__init__.py +70 -0
  305. multi_agent_brief/orchestrator/runtime_state/_io.py +167 -0
  306. multi_agent_brief/orchestrator/runtime_state/_transactions.py +298 -0
  307. multi_agent_brief/orchestrator/runtime_state/artifact_registry.py +1825 -0
  308. multi_agent_brief/orchestrator/runtime_state/atomic_claim_graph.py +96 -0
  309. multi_agent_brief/orchestrator/runtime_state/claim_ledger_freeze.py +510 -0
  310. multi_agent_brief/orchestrator/runtime_state/claim_metadata_enrichment.py +836 -0
  311. multi_agent_brief/orchestrator/runtime_state/claim_support_matrix.py +582 -0
  312. multi_agent_brief/orchestrator/runtime_state/completion_gates.py +672 -0
  313. multi_agent_brief/orchestrator/runtime_state/contracts_loader.py +66 -0
  314. multi_agent_brief/orchestrator/runtime_state/decisions.py +239 -0
  315. multi_agent_brief/orchestrator/runtime_state/errors.py +61 -0
  316. multi_agent_brief/orchestrator/runtime_state/event_log.py +256 -0
  317. multi_agent_brief/orchestrator/runtime_state/evidence_span_registry.py +233 -0
  318. multi_agent_brief/orchestrator/runtime_state/fact_layer.py +755 -0
  319. multi_agent_brief/orchestrator/runtime_state/identity.py +90 -0
  320. multi_agent_brief/orchestrator/runtime_state/lifecycle.py +675 -0
  321. multi_agent_brief/orchestrator/runtime_state/manifest.py +82 -0
  322. multi_agent_brief/orchestrator/runtime_state/operations.py +224 -0
  323. multi_agent_brief/orchestrator/runtime_state/paths.py +38 -0
  324. multi_agent_brief/orchestrator/runtime_state/repair.py +984 -0
  325. multi_agent_brief/orchestrator/runtime_state/safety_surfaces.py +147 -0
  326. multi_agent_brief/orchestrator/runtime_state/semantic_assessment_report.py +692 -0
  327. multi_agent_brief/orchestrator/runtime_state/semantic_support_acceptance.py +683 -0
  328. multi_agent_brief/orchestrator/runtime_state/source_evidence_pack.py +131 -0
  329. multi_agent_brief/orchestrator/runtime_state/stage_completion.py +1320 -0
  330. multi_agent_brief/orchestrator/runtime_state/trajectory.py +200 -0
  331. multi_agent_brief/orchestrator/runtime_state/workflow.py +350 -0
  332. multi_agent_brief/orchestrator/source_evidence.py +43 -0
  333. multi_agent_brief/orchestrator/timing.py +410 -0
  334. multi_agent_brief/orchestrator_contract.py +96 -0
  335. multi_agent_brief/outputs/__init__.py +2 -0
  336. multi_agent_brief/outputs/atomic_reader_projection.py +288 -0
  337. multi_agent_brief/outputs/docx.py +32 -0
  338. multi_agent_brief/outputs/finalize.py +1036 -0
  339. multi_agent_brief/outputs/ib_docx.py +983 -0
  340. multi_agent_brief/outputs/naming.py +38 -0
  341. multi_agent_brief/outputs/pdf.py +20 -0
  342. multi_agent_brief/outputs/reader_final_gate.py +509 -0
  343. multi_agent_brief/outputs/reader_projection.py +397 -0
  344. multi_agent_brief/outputs/source_appendix.py +753 -0
  345. multi_agent_brief/outputs/templates/__init__.py +112 -0
  346. multi_agent_brief/product/__init__.py +49 -0
  347. multi_agent_brief/product/bundle_projection.py +686 -0
  348. multi_agent_brief/product/citation_profile.py +145 -0
  349. multi_agent_brief/product/guidance_manifestation.py +421 -0
  350. multi_agent_brief/product/materiality_selection.py +472 -0
  351. multi_agent_brief/product/policy_gate_adapter.py +116 -0
  352. multi_agent_brief/product/policy_profile.py +39 -0
  353. multi_agent_brief/product/policy_projection.py +161 -0
  354. multi_agent_brief/product/policy_registry.py +83 -0
  355. multi_agent_brief/product/policy_resolver.py +225 -0
  356. multi_agent_brief/product/quality_closeout.py +188 -0
  357. multi_agent_brief/product/quality_panel.py +2217 -0
  358. multi_agent_brief/product/release_approval.py +1034 -0
  359. multi_agent_brief/product/report_pack.py +130 -0
  360. multi_agent_brief/product/report_pack_aliases.py +68 -0
  361. multi_agent_brief/product/report_registry.py +101 -0
  362. multi_agent_brief/product/report_spec.py +181 -0
  363. multi_agent_brief/product/support_wording.py +525 -0
  364. multi_agent_brief/product/template_conformance.py +516 -0
  365. multi_agent_brief/product/template_projection.py +123 -0
  366. multi_agent_brief/product/template_registry.py +244 -0
  367. multi_agent_brief/product/template_render_plan.py +315 -0
  368. multi_agent_brief/product/template_renderer.py +224 -0
  369. multi_agent_brief/product/trajectory_regulation.py +441 -0
  370. multi_agent_brief/provenance/__init__.py +5 -0
  371. multi_agent_brief/provenance/builder.py +841 -0
  372. multi_agent_brief/provenance/contract.py +21 -0
  373. multi_agent_brief/provenance/io.py +144 -0
  374. multi_agent_brief/provenance/model.py +110 -0
  375. multi_agent_brief/provenance/references.py +43 -0
  376. multi_agent_brief/provenance/validator.py +135 -0
  377. multi_agent_brief/quality_gates/__init__.py +19 -0
  378. multi_agent_brief/quality_gates/contract.py +581 -0
  379. multi_agent_brief/quality_gates/state.py +2498 -0
  380. multi_agent_brief/repair/__init__.py +5 -0
  381. multi_agent_brief/repair/router.py +842 -0
  382. multi_agent_brief/runtime_assets.py +431 -0
  383. multi_agent_brief/sources/__init__.py +16 -0
  384. multi_agent_brief/sources/api_filings.py +236 -0
  385. multi_agent_brief/sources/api_news.py +174 -0
  386. multi_agent_brief/sources/base.py +142 -0
  387. multi_agent_brief/sources/cached_package.py +119 -0
  388. multi_agent_brief/sources/cli_provider.py +263 -0
  389. multi_agent_brief/sources/coverage.py +336 -0
  390. multi_agent_brief/sources/decider.py +756 -0
  391. multi_agent_brief/sources/doctor.py +373 -0
  392. multi_agent_brief/sources/evidence_pack.py +364 -0
  393. multi_agent_brief/sources/feishu_provider.py +326 -0
  394. multi_agent_brief/sources/filing_resolver.py +400 -0
  395. multi_agent_brief/sources/industry_packs.py +130 -0
  396. multi_agent_brief/sources/join.py +206 -0
  397. multi_agent_brief/sources/local_signal.py +81 -0
  398. multi_agent_brief/sources/local_signal_planner.py +636 -0
  399. multi_agent_brief/sources/manual.py +259 -0
  400. multi_agent_brief/sources/mcp_provider.py +309 -0
  401. multi_agent_brief/sources/mineru_provider.py +601 -0
  402. multi_agent_brief/sources/normalizer.py +95 -0
  403. multi_agent_brief/sources/opencli_provider.py +282 -0
  404. multi_agent_brief/sources/planner.py +178 -0
  405. multi_agent_brief/sources/registry.py +367 -0
  406. multi_agent_brief/sources/rss.py +190 -0
  407. multi_agent_brief/sources/search_backends/__init__.py +31 -0
  408. multi_agent_brief/sources/search_backends/base.py +51 -0
  409. multi_agent_brief/sources/search_backends/brave.py +192 -0
  410. multi_agent_brief/sources/search_backends/capabilities.py +94 -0
  411. multi_agent_brief/sources/search_backends/exa.py +180 -0
  412. multi_agent_brief/sources/search_backends/firecrawl.py +164 -0
  413. multi_agent_brief/sources/search_backends/serper.py +241 -0
  414. multi_agent_brief/sources/search_backends/tavily.py +124 -0
  415. multi_agent_brief/sources/sourcehub.py +505 -0
  416. multi_agent_brief/sources/web_search.py +267 -0
  417. multi_agent_brief/status.py +966 -0
  418. multi_agent_brief/tools/__init__.py +3 -0
  419. multi_agent_brief/tools/draft_cleanup.py +198 -0
  420. multi_agent_brief/workbuddy/__init__.py +1 -0
  421. multi_agent_brief/workbuddy/diagnose.py +569 -0
  422. multi_agent_brief/workbuddy/skill_pack.py +309 -0
  423. multi_agent_brief/workspace/__init__.py +1 -0
  424. multi_agent_brief/workspace/init_profile.py +45 -0
@@ -0,0 +1,692 @@
1
+ """Runtime validation helpers for Semantic Assessment Report artifacts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ from collections.abc import Iterable, Mapping
8
+ from copy import deepcopy
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from multi_agent_brief.audit.semantic import (
13
+ SEMANTIC_SUPPORT_CALIBRATION_METADATA_KEY,
14
+ SEMANTIC_SUPPORT_INVALID_CALIBRATION_LABEL,
15
+ findings_from_semantic_proposal_rows,
16
+ normalize_calibration_label,
17
+ )
18
+ from multi_agent_brief.contracts.schemas.semantic_assessment_report import SemanticAssessmentReportContract
19
+ from multi_agent_brief.contracts.semantic_assessment_status import SEMANTIC_ASSESSMENT_REPORT_STATUSES
20
+ from multi_agent_brief.orchestrator.runtime_state.claim_support_matrix import (
21
+ _read_json_mapping,
22
+ _schema_error_reason,
23
+ _workspace_atomic_graph_payload,
24
+ _workspace_evidence_span_registry_payload,
25
+ _workspace_ledger_claims,
26
+ )
27
+
28
+
29
+ SEMANTIC_ASSESSMENT_PROPOSAL_PROJECTION_SCHEMA_VERSION = (
30
+ "mabw.semantic_assessment_report.proposal_projection.v1"
31
+ )
32
+ SEMANTIC_ASSESSMENT_WORKSPACE_PROJECTION_SCHEMA_VERSION = (
33
+ "mabw.semantic_assessment_report.workspace_projection.v1"
34
+ )
35
+ SEMANTIC_ASSESSMENT_REPORT_VALIDATION_PREFIX = "semantic_assessment_report_validation_error"
36
+ UNRESOLVED_SEMANTIC_ASSESSMENT_LEVELS = {"high", "unknown"}
37
+ SEMANTIC_ASSESSMENT_CHECKED_INPUTS = {
38
+ "audited_brief": "output/intermediate/audited_brief.md",
39
+ "claim_ledger": "output/intermediate/claim_ledger.json",
40
+ "atomic_claim_graph": "output/intermediate/atomic_claim_graph.json",
41
+ "evidence_span_registry": "output/intermediate/evidence_span_registry.json",
42
+ }
43
+ SEMANTIC_ASSESSMENT_CHECKED_INPUT_STATUSES = {
44
+ "fresh",
45
+ "stale",
46
+ "missing_checked_inputs",
47
+ "missing_input",
48
+ }
49
+
50
+
51
+ def project_semantic_assessment_report_from_workspace(workspace: str | Path) -> dict[str, Any]:
52
+ """Read and project a present, valid Semantic Assessment Report.
53
+
54
+ This is a read-only status surface. It validates machine-checkable sibling
55
+ artifact bindings before projecting proposal rows, but it does not accept
56
+ support truth, mutate the Claim-Support Matrix, create adjudication queue
57
+ items, write workspace state, gate delivery, or decide release eligibility.
58
+ """
59
+
60
+ ws = Path(workspace).expanduser().resolve()
61
+ intermediate = ws / "output" / "intermediate"
62
+ report_path = intermediate / "semantic_assessment_report.json"
63
+ base = _workspace_projection_base(workspace=ws, report_path=report_path)
64
+ if not report_path.exists():
65
+ return {
66
+ **base,
67
+ "status": "not_available",
68
+ "report_present": False,
69
+ "reason": "semantic_assessment_report_missing",
70
+ "proposal_projection": _empty_proposal_projection(),
71
+ "summary_counts": {},
72
+ "proposed_claim_support_rows": [],
73
+ }
74
+
75
+ report_payload, reason = _read_json_mapping(report_path, label="semantic_assessment_report")
76
+ if reason:
77
+ return _invalid_workspace_projection(base, reason=reason)
78
+ assert report_payload is not None
79
+
80
+ reason = _schema_error_reason(
81
+ SemanticAssessmentReportContract.validate(report_payload),
82
+ prefix="semantic_assessment_report_schema_error",
83
+ )
84
+ if reason:
85
+ return _invalid_workspace_projection(base, reason=reason)
86
+
87
+ ledger_claims, reason = _workspace_ledger_claims(intermediate / "claim_ledger.json")
88
+ if reason:
89
+ return _invalid_workspace_projection(base, reason=f"{SEMANTIC_ASSESSMENT_REPORT_VALIDATION_PREFIX}:{reason}")
90
+ graph_payload, reason = _workspace_atomic_graph_payload(
91
+ intermediate / "atomic_claim_graph.json",
92
+ ledger_claims=ledger_claims or [],
93
+ )
94
+ if reason:
95
+ return _invalid_workspace_projection(base, reason=f"{SEMANTIC_ASSESSMENT_REPORT_VALIDATION_PREFIX}:{reason}")
96
+ evidence_payload, reason = _workspace_evidence_span_registry_payload(
97
+ intermediate / "evidence_span_registry.json",
98
+ workspace=ws,
99
+ )
100
+ if reason:
101
+ return _invalid_workspace_projection(base, reason=f"{SEMANTIC_ASSESSMENT_REPORT_VALIDATION_PREFIX}:{reason}")
102
+
103
+ reason = validate_semantic_assessment_report_against_artifacts(
104
+ report_payload=report_payload,
105
+ ledger_claims=ledger_claims or [],
106
+ graph_payload=graph_payload or {},
107
+ evidence_span_registry_payload=evidence_payload or {},
108
+ )
109
+ if reason:
110
+ return _invalid_workspace_projection(
111
+ base,
112
+ reason=f"{SEMANTIC_ASSESSMENT_REPORT_VALIDATION_PREFIX}:{reason}",
113
+ )
114
+
115
+ proposal_projection = project_semantic_assessment_proposals(report_payload)
116
+ checked_projection = project_semantic_assessment_checked_inputs(
117
+ report_payload=report_payload,
118
+ workspace=ws,
119
+ )
120
+ checked_status = checked_projection["status"]
121
+ status = "valid" if checked_status in {"fresh", "missing_checked_inputs"} else checked_status
122
+ return {
123
+ **base,
124
+ "status": status,
125
+ "report_present": True,
126
+ "reason": None if checked_status in {"fresh", "missing_checked_inputs"} else checked_projection["reason"],
127
+ "report_sha256": _file_sha256(report_path),
128
+ "checked_inputs_status": checked_status,
129
+ "checked_inputs": checked_projection["checked_inputs"],
130
+ "checked_inputs_digest": checked_projection["checked_inputs_digest"],
131
+ "proposal_projection": proposal_projection,
132
+ "summary_counts": proposal_projection.get("summary_counts") or {},
133
+ "proposed_claim_support_rows": proposal_projection.get("proposed_claim_support_rows")
134
+ if isinstance(proposal_projection.get("proposed_claim_support_rows"), list)
135
+ else [],
136
+ }
137
+
138
+
139
+ def build_semantic_assessment_checked_inputs(workspace: str | Path) -> dict[str, dict[str, Any]]:
140
+ """Return current checked-input bindings for a workspace.
141
+
142
+ This helper is deterministic metadata only. It does not create, validate, or
143
+ accept semantic support truth; it records the exact artifacts a Semantic
144
+ Assessment Report reviewed so later readers can detect stale reports.
145
+ """
146
+
147
+ ws = Path(workspace).expanduser().resolve()
148
+ checked: dict[str, dict[str, Any]] = {}
149
+ for key, rel_path in SEMANTIC_ASSESSMENT_CHECKED_INPUTS.items():
150
+ path = ws / rel_path
151
+ if not path.exists():
152
+ raise FileNotFoundError(rel_path)
153
+ checked[key] = {
154
+ "path": rel_path,
155
+ "sha256": _file_sha256(path),
156
+ "size_bytes": path.stat().st_size,
157
+ }
158
+ return checked
159
+
160
+
161
+ def semantic_assessment_checked_inputs_digest(checked_inputs: Mapping[str, Any]) -> str:
162
+ """Return a stable digest for checked-input metadata."""
163
+
164
+ normalized = _normalized_checked_inputs(checked_inputs)
165
+ encoded = json.dumps(normalized, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
166
+ return hashlib.sha256(encoded).hexdigest()
167
+
168
+
169
+ def project_semantic_assessment_checked_inputs(
170
+ *,
171
+ report_payload: Mapping[str, Any],
172
+ workspace: str | Path,
173
+ ) -> dict[str, Any]:
174
+ """Project whether a report's declared checked inputs match workspace files."""
175
+
176
+ declared = report_payload.get("checked_inputs")
177
+ if declared is None:
178
+ return {
179
+ "status": "missing_checked_inputs",
180
+ "reason": "checked_inputs_missing",
181
+ "checked_inputs": {},
182
+ "checked_inputs_digest": "",
183
+ }
184
+ if not isinstance(declared, Mapping):
185
+ return {
186
+ "status": "missing_checked_inputs",
187
+ "reason": "checked_inputs_not_object",
188
+ "checked_inputs": {},
189
+ "checked_inputs_digest": "",
190
+ }
191
+ normalized = _normalized_checked_inputs(declared)
192
+ if set(normalized) != set(SEMANTIC_ASSESSMENT_CHECKED_INPUTS):
193
+ return {
194
+ "status": "missing_checked_inputs",
195
+ "reason": "checked_inputs_incomplete",
196
+ "checked_inputs": normalized,
197
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized) if normalized else "",
198
+ }
199
+
200
+ ws = Path(workspace).expanduser().resolve()
201
+ for key in SEMANTIC_ASSESSMENT_CHECKED_INPUTS:
202
+ entry = normalized[key]
203
+ rel_path = _text(entry.get("path"))
204
+ if not rel_path:
205
+ return {
206
+ "status": "missing_checked_inputs",
207
+ "reason": f"checked_input_path_missing:{key}",
208
+ "checked_inputs": normalized,
209
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized),
210
+ }
211
+ if rel_path != SEMANTIC_ASSESSMENT_CHECKED_INPUTS[key]:
212
+ return {
213
+ "status": "missing_input",
214
+ "reason": f"checked_input_path_mismatch:{key}",
215
+ "checked_inputs": normalized,
216
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized),
217
+ }
218
+ path = (ws / rel_path).resolve()
219
+ try:
220
+ path.relative_to(ws)
221
+ except ValueError:
222
+ return {
223
+ "status": "missing_input",
224
+ "reason": f"checked_input_path_outside_workspace:{key}",
225
+ "checked_inputs": normalized,
226
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized),
227
+ }
228
+ if not path.exists():
229
+ return {
230
+ "status": "missing_input",
231
+ "reason": f"checked_input_missing:{key}",
232
+ "checked_inputs": normalized,
233
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized),
234
+ }
235
+ if not path.is_file():
236
+ return {
237
+ "status": "missing_input",
238
+ "reason": f"checked_input_not_file:{key}",
239
+ "checked_inputs": normalized,
240
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized),
241
+ }
242
+ try:
243
+ current_size = path.stat().st_size
244
+ current_sha256 = _file_sha256(path)
245
+ except OSError:
246
+ return {
247
+ "status": "missing_input",
248
+ "reason": f"checked_input_unreadable:{key}",
249
+ "checked_inputs": normalized,
250
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized),
251
+ }
252
+ if current_size != entry.get("size_bytes"):
253
+ return {
254
+ "status": "stale",
255
+ "reason": f"checked_input_stale:{key}",
256
+ "checked_inputs": normalized,
257
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized),
258
+ }
259
+ if current_sha256 != entry.get("sha256"):
260
+ return {
261
+ "status": "stale",
262
+ "reason": f"checked_input_stale:{key}",
263
+ "checked_inputs": normalized,
264
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized),
265
+ }
266
+
267
+ return {
268
+ "status": "fresh",
269
+ "reason": None,
270
+ "checked_inputs": normalized,
271
+ "checked_inputs_digest": semantic_assessment_checked_inputs_digest(normalized),
272
+ }
273
+
274
+
275
+ def validate_semantic_assessment_checked_inputs_for_workspace(
276
+ *,
277
+ report_payload: Mapping[str, Any],
278
+ workspace: str | Path,
279
+ ) -> str | None:
280
+ """Return checked-input validation reason for authoritative consumers.
281
+
282
+ Legacy reports without ``checked_inputs`` remain optional/advisory and do
283
+ not invalidate the artifact registry. Declared checked inputs must match.
284
+ """
285
+
286
+ projection = project_semantic_assessment_checked_inputs(report_payload=report_payload, workspace=workspace)
287
+ status = projection["status"]
288
+ reason = _text(projection.get("reason"))
289
+ if status == "fresh" or (status == "missing_checked_inputs" and reason == "checked_inputs_missing"):
290
+ return None
291
+ return reason or status
292
+
293
+
294
+ def project_semantic_assessment_proposals(report_payload: Mapping[str, Any]) -> dict[str, Any]:
295
+ """Project report rows into proposal-only Claim-Support Matrix deltas.
296
+
297
+ This helper is intentionally read-only and non-authoritative. It does not
298
+ create accepted Claim-Support Matrix rows, write workspace state, create
299
+ adjudication records, judge semantic support, or decide release eligibility.
300
+ """
301
+
302
+ rows = [row for row in report_payload.get("rows", []) if isinstance(row, Mapping)]
303
+ assessor_methods = _assessor_method_index(report_payload)
304
+ assessor_labels = _assessor_label_index(report_payload)
305
+ proposed_rows = [
306
+ _project_report_row(row, assessor_methods=assessor_methods, assessor_labels=assessor_labels)
307
+ for row in rows
308
+ ]
309
+ proposed_rows.sort(key=lambda item: str(item.get("proposal_id") or ""))
310
+ summary_counts = _proposal_summary_counts(proposed_rows)
311
+ return {
312
+ "schema_version": SEMANTIC_ASSESSMENT_PROPOSAL_PROJECTION_SCHEMA_VERSION,
313
+ "status": "projected" if proposed_rows else "not_available",
314
+ "semantic_boundary": "proposal_projection_only_not_accepted_support_truth",
315
+ "source_report_schema_version": _text(report_payload.get("schema_version")),
316
+ "proposal_count": len(proposed_rows),
317
+ "summary_counts": summary_counts,
318
+ "proposed_claim_support_rows": proposed_rows,
319
+ "proposed_csm_delta": {
320
+ "status": "proposal_only" if proposed_rows else "not_available",
321
+ "accepted_csm_rows": [],
322
+ "candidate_rows": deepcopy(proposed_rows),
323
+ },
324
+ }
325
+
326
+
327
+ def semantic_support_findings_from_schema_valid_report(report_payload: Any) -> list[Any]:
328
+ """Adapt a schema-valid Semantic Assessment Report into advisory findings.
329
+
330
+ Returns ``list[AuditFinding]`` with ``finding_type="semantic_support_proposal"``.
331
+ An invalid or non-mapping report yields no findings, keeping its invalid
332
+ status visible through the existing registry/status path.
333
+
334
+ This validates report SHAPE only (``SemanticAssessmentReportContract``). It
335
+ does NOT validate cross-artifact bindings: a schema-valid report that
336
+ references a claim_id/atom_id/evidence_span that does not exist in the
337
+ workspace artifacts still produces findings here. Callers that need binding
338
+ validation must use ``project_semantic_assessment_report_from_workspace``,
339
+ which reads the Claim Ledger / Atomic Claim Graph / Evidence Span Registry
340
+ and rejects unknown references before projecting.
341
+
342
+ This is pure conversion: it never writes ``audit_report.json``, the
343
+ Claim-Support Matrix, workflow state, gate reports, or delivery files, and
344
+ the findings carry no gate or release authority.
345
+ """
346
+
347
+ if not isinstance(report_payload, Mapping):
348
+ return []
349
+ violations = SemanticAssessmentReportContract.validate(dict(report_payload))
350
+ if any(violation.severity == "error" for violation in violations):
351
+ return []
352
+ projection = project_semantic_assessment_proposals(report_payload)
353
+ proposal_rows = projection.get("proposed_claim_support_rows")
354
+ if not isinstance(proposal_rows, list):
355
+ return []
356
+ return findings_from_semantic_proposal_rows(proposal_rows)
357
+
358
+
359
+ def _workspace_projection_base(*, workspace: Path, report_path: Path) -> dict[str, Any]:
360
+ try:
361
+ rendered_path = report_path.relative_to(workspace).as_posix()
362
+ except ValueError:
363
+ rendered_path = report_path.name
364
+ return {
365
+ "schema_version": SEMANTIC_ASSESSMENT_WORKSPACE_PROJECTION_SCHEMA_VERSION,
366
+ "semantic_boundary": "proposal_projection_only_not_accepted_support_truth",
367
+ "report_path": rendered_path,
368
+ }
369
+
370
+
371
+ def _invalid_workspace_projection(base: Mapping[str, Any], *, reason: str) -> dict[str, Any]:
372
+ return {
373
+ **dict(base),
374
+ "status": "invalid_report",
375
+ "report_present": True,
376
+ "reason": reason,
377
+ "checked_inputs_status": "missing_checked_inputs",
378
+ "checked_inputs": {},
379
+ "checked_inputs_digest": "",
380
+ "proposal_projection": _empty_proposal_projection(),
381
+ "summary_counts": {},
382
+ "proposed_claim_support_rows": [],
383
+ }
384
+
385
+
386
+ def _empty_proposal_projection() -> dict[str, Any]:
387
+ return project_semantic_assessment_proposals(
388
+ {
389
+ "schema_version": "",
390
+ "assessors": [],
391
+ "rows": [],
392
+ }
393
+ )
394
+
395
+
396
+ def validate_semantic_assessment_report_against_artifacts(
397
+ *,
398
+ report_payload: Mapping[str, Any],
399
+ ledger_claims: Iterable[Mapping[str, Any]],
400
+ graph_payload: Mapping[str, Any],
401
+ evidence_span_registry_payload: Mapping[str, Any],
402
+ ) -> str | None:
403
+ """Return the first deterministic report cross-artifact validation reason.
404
+
405
+ This validates only machine-checkable references and proposal authority
406
+ flags. It does not judge semantic support, mutate the Claim-Support Matrix,
407
+ create adjudication records, or decide release eligibility.
408
+ """
409
+
410
+ rows = [row for row in report_payload.get("rows", []) if isinstance(row, Mapping)]
411
+ assessor_methods = _assessor_method_index(report_payload)
412
+ ledger_claim_ids = _ledger_claim_ids(ledger_claims)
413
+ atom_index = _graph_atom_index(graph_payload)
414
+ evidence_span_ids = _evidence_span_ids(evidence_span_registry_payload)
415
+
416
+ unknown_claim_ids = sorted(
417
+ {
418
+ claim_id
419
+ for row in rows
420
+ if (claim_id := _text(row.get("claim_id"))) and claim_id not in ledger_claim_ids
421
+ }
422
+ )
423
+ if unknown_claim_ids:
424
+ return f"unknown_claim_reference:{unknown_claim_ids[0]}"
425
+
426
+ unknown_atom_ids = sorted(
427
+ {
428
+ atom_id
429
+ for row in rows
430
+ if (atom_id := _text(row.get("atom_id"))) and atom_id not in atom_index
431
+ }
432
+ )
433
+ if unknown_atom_ids:
434
+ return f"unknown_atom_reference:{unknown_atom_ids[0]}"
435
+
436
+ atom_claim_mismatches = sorted(
437
+ (
438
+ atom_id,
439
+ _text(row.get("claim_id")),
440
+ str(atom_index[atom_id].get("claim_id") or ""),
441
+ )
442
+ for row in rows
443
+ if (atom_id := _text(row.get("atom_id"))) in atom_index
444
+ and _text(row.get("claim_id"))
445
+ and _text(row.get("claim_id")) != atom_index[atom_id].get("claim_id")
446
+ )
447
+ if atom_claim_mismatches:
448
+ atom_id, row_claim_id, graph_claim_id = atom_claim_mismatches[0]
449
+ return f"atom_claim_mismatch:{atom_id}:{row_claim_id}:{graph_claim_id}"
450
+
451
+ unknown_span_ids = sorted(_unknown_span_ids(rows=rows, evidence_span_ids=evidence_span_ids))
452
+ if unknown_span_ids:
453
+ return f"unknown_evidence_span_reference:{unknown_span_ids[0]}"
454
+
455
+ method_mismatches = sorted(
456
+ (
457
+ _text(row.get("row_id")) or _text(row.get("atom_id")) or "<unknown_row>",
458
+ _text(row.get("assessor_id")),
459
+ _text(row.get("assessment_method")),
460
+ assessor_methods[_text(row.get("assessor_id"))],
461
+ )
462
+ for row in rows
463
+ if (assessor_id := _text(row.get("assessor_id"))) in assessor_methods
464
+ and _text(row.get("assessment_method"))
465
+ and _text(row.get("assessment_method")) != assessor_methods[assessor_id]
466
+ )
467
+ if method_mismatches:
468
+ row_id, assessor_id, row_method, declared_method = method_mismatches[0]
469
+ return f"assessment_method_mismatch:{row_id}:{assessor_id}:{row_method}:{declared_method}"
470
+
471
+ missing_adjudication_flags = sorted(
472
+ _text(row.get("row_id")) or _text(row.get("atom_id")) or "<unknown_row>"
473
+ for row in rows
474
+ if _requires_llm_only_high_materiality_adjudication(
475
+ row=row,
476
+ assessor_methods=assessor_methods,
477
+ atom_index=atom_index,
478
+ )
479
+ )
480
+ if missing_adjudication_flags:
481
+ return f"llm_only_high_materiality_requires_human_adjudication:{missing_adjudication_flags[0]}"
482
+
483
+ return None
484
+
485
+
486
+ def _project_report_row(
487
+ row: Mapping[str, Any],
488
+ *,
489
+ assessor_methods: Mapping[str, str],
490
+ assessor_labels: Mapping[str, str],
491
+ ) -> dict[str, Any]:
492
+ assessor_id = _text(row.get("assessor_id"))
493
+ evidence_span_id = _text(row.get("evidence_span_id")) or None
494
+ candidate_span_ids = _candidate_span_ids(row)
495
+ relation_status = "single_span" if evidence_span_id else "candidate_spans"
496
+ assessment_method = assessor_methods.get(assessor_id) or _text(row.get("assessment_method"))
497
+ return {
498
+ "proposal_id": _text(row.get("row_id")),
499
+ "source_row_id": _text(row.get("row_id")),
500
+ "claim_id": _text(row.get("claim_id")),
501
+ "atom_id": _text(row.get("atom_id")),
502
+ "evidence_span_id": evidence_span_id,
503
+ "candidate_evidence_span_ids": candidate_span_ids,
504
+ "relation_status": relation_status,
505
+ "proposed_support_label": _text(row.get("proposed_support_label")),
506
+ "proposed_support_reason": _text(row.get("rationale")),
507
+ "confidence": row.get("confidence") if isinstance(row.get("confidence"), (int, float)) else None,
508
+ "uncertainty": _text(row.get("uncertainty")),
509
+ "disagreement": _text(row.get("disagreement")),
510
+ "requires_human_adjudication": row.get("requires_human_adjudication") is True,
511
+ "assessor_id": assessor_id,
512
+ "assessor_label": assessor_labels.get(assessor_id, ""),
513
+ "assessment_method": assessment_method,
514
+ "accepted_support_truth": False,
515
+ "writes_claim_support_matrix": False,
516
+ "calibration_label": normalize_calibration_label(
517
+ row.get("metadata", {}).get(SEMANTIC_SUPPORT_CALIBRATION_METADATA_KEY)
518
+ if isinstance(row.get("metadata"), Mapping)
519
+ else None
520
+ ),
521
+ "metadata": deepcopy(row.get("metadata")) if isinstance(row.get("metadata"), Mapping) else {},
522
+ }
523
+
524
+
525
+ def _calibration_label_counts(rows: list[Mapping[str, Any]]) -> dict[str, int]:
526
+ counts: dict[str, int] = {}
527
+ for row in rows:
528
+ label = row.get("calibration_label")
529
+ if isinstance(label, str) and label:
530
+ counts[label] = counts.get(label, 0) + 1
531
+ return dict(sorted(counts.items()))
532
+
533
+
534
+ def _proposal_summary_counts(rows: list[Mapping[str, Any]]) -> dict[str, Any]:
535
+ return {
536
+ "proposal_row_count": len(rows),
537
+ "single_span_proposal_count": sum(1 for row in rows if row.get("relation_status") == "single_span"),
538
+ "candidate_span_proposal_count": sum(1 for row in rows if row.get("relation_status") == "candidate_spans"),
539
+ "requires_human_adjudication_count": sum(
540
+ 1
541
+ for row in rows
542
+ if row.get("requires_human_adjudication") is True
543
+ or row.get("calibration_label") == SEMANTIC_SUPPORT_INVALID_CALIBRATION_LABEL
544
+ ),
545
+ "llm_only_count": sum(1 for row in rows if row.get("assessment_method") == "llm_only"),
546
+ "high_uncertainty_count": sum(1 for row in rows if row.get("uncertainty") == "high"),
547
+ "high_disagreement_count": sum(1 for row in rows if row.get("disagreement") == "high"),
548
+ "calibration_label_counts": _calibration_label_counts(rows),
549
+ }
550
+
551
+
552
+ def _unknown_span_ids(*, rows: Iterable[Mapping[str, Any]], evidence_span_ids: set[str]) -> set[str]:
553
+ unknown: set[str] = set()
554
+ for row in rows:
555
+ evidence_span_id = _text(row.get("evidence_span_id"))
556
+ if evidence_span_id and evidence_span_id not in evidence_span_ids:
557
+ unknown.add(evidence_span_id)
558
+ candidates = row.get("candidate_evidence_span_ids")
559
+ for candidate in candidates if isinstance(candidates, list) else []:
560
+ candidate_id = _text(candidate)
561
+ if candidate_id and candidate_id not in evidence_span_ids:
562
+ unknown.add(candidate_id)
563
+ return unknown
564
+
565
+
566
+ def _requires_llm_only_high_materiality_adjudication(
567
+ *,
568
+ row: Mapping[str, Any],
569
+ assessor_methods: Mapping[str, str],
570
+ atom_index: Mapping[str, Mapping[str, str]],
571
+ ) -> bool:
572
+ assessor_id = _text(row.get("assessor_id"))
573
+ effective_method = assessor_methods.get(assessor_id) or _text(row.get("assessment_method"))
574
+ if effective_method != "llm_only":
575
+ return False
576
+ atom_id = _text(row.get("atom_id"))
577
+ if not atom_id or atom_index.get(atom_id, {}).get("materiality") != "high":
578
+ return False
579
+ if row.get("requires_human_adjudication") is True:
580
+ return False
581
+ return (
582
+ _text(row.get("uncertainty")) in UNRESOLVED_SEMANTIC_ASSESSMENT_LEVELS
583
+ or _text(row.get("disagreement")) in UNRESOLVED_SEMANTIC_ASSESSMENT_LEVELS
584
+ )
585
+
586
+
587
+ def _candidate_span_ids(row: Mapping[str, Any]) -> list[str]:
588
+ candidates = row.get("candidate_evidence_span_ids")
589
+ if not isinstance(candidates, list):
590
+ return []
591
+ return [candidate_id for candidate in candidates if (candidate_id := _text(candidate))]
592
+
593
+
594
+ def _assessor_method_index(report_payload: Mapping[str, Any]) -> dict[str, str]:
595
+ assessor_methods: dict[str, str] = {}
596
+ assessors = report_payload.get("assessors") if isinstance(report_payload, Mapping) else None
597
+ for assessor in assessors if isinstance(assessors, list) else []:
598
+ if not isinstance(assessor, Mapping):
599
+ continue
600
+ assessor_id = _text(assessor.get("assessor_id"))
601
+ assessment_method = _text(assessor.get("assessment_method"))
602
+ if assessor_id and assessment_method and assessor_id not in assessor_methods:
603
+ assessor_methods[assessor_id] = assessment_method
604
+ return assessor_methods
605
+
606
+
607
+ def _assessor_label_index(report_payload: Mapping[str, Any]) -> dict[str, str]:
608
+ assessor_labels: dict[str, str] = {}
609
+ assessors = report_payload.get("assessors") if isinstance(report_payload, Mapping) else None
610
+ for assessor in assessors if isinstance(assessors, list) else []:
611
+ if not isinstance(assessor, Mapping):
612
+ continue
613
+ assessor_id = _text(assessor.get("assessor_id"))
614
+ label = _text(assessor.get("label"))
615
+ if assessor_id and label and assessor_id not in assessor_labels:
616
+ assessor_labels[assessor_id] = label
617
+ return assessor_labels
618
+
619
+
620
+ def _ledger_claim_ids(ledger_claims: Iterable[Mapping[str, Any]]) -> set[str]:
621
+ return {
622
+ claim_id
623
+ for claim in ledger_claims
624
+ if (claim_id := _text(claim.get("claim_id")))
625
+ }
626
+
627
+
628
+ def _graph_atom_index(graph_payload: Mapping[str, Any]) -> dict[str, dict[str, str]]:
629
+ atom_index: dict[str, dict[str, str]] = {}
630
+ claims = graph_payload.get("claims") if isinstance(graph_payload, Mapping) else None
631
+ for claim in claims if isinstance(claims, list) else []:
632
+ if not isinstance(claim, Mapping):
633
+ continue
634
+ claim_id = _text(claim.get("claim_id"))
635
+ atoms = claim.get("atoms")
636
+ for atom in atoms if isinstance(atoms, list) else []:
637
+ if not isinstance(atom, Mapping):
638
+ continue
639
+ atom_id = _text(atom.get("atom_id"))
640
+ if not atom_id:
641
+ continue
642
+ atom_index[atom_id] = {
643
+ "claim_id": claim_id,
644
+ "materiality": _text(atom.get("materiality")),
645
+ }
646
+ return atom_index
647
+
648
+
649
+ def _evidence_span_ids(evidence_span_registry_payload: Mapping[str, Any]) -> set[str]:
650
+ span_ids: set[str] = set()
651
+ sources = (
652
+ evidence_span_registry_payload.get("sources")
653
+ if isinstance(evidence_span_registry_payload, Mapping)
654
+ else None
655
+ )
656
+ for source in sources if isinstance(sources, list) else []:
657
+ if not isinstance(source, Mapping):
658
+ continue
659
+ spans = source.get("spans")
660
+ for span in spans if isinstance(spans, list) else []:
661
+ if not isinstance(span, Mapping):
662
+ continue
663
+ span_id = _text(span.get("span_id"))
664
+ if span_id:
665
+ span_ids.add(span_id)
666
+ return span_ids
667
+
668
+
669
+ def _normalized_checked_inputs(checked_inputs: Mapping[str, Any]) -> dict[str, dict[str, Any]]:
670
+ normalized: dict[str, dict[str, Any]] = {}
671
+ for key, value in checked_inputs.items():
672
+ clean_key = _text(key)
673
+ if not clean_key or not isinstance(value, Mapping):
674
+ continue
675
+ normalized[clean_key] = {
676
+ "path": _text(value.get("path")),
677
+ "sha256": _text(value.get("sha256")),
678
+ "size_bytes": value.get("size_bytes"),
679
+ }
680
+ return dict(sorted(normalized.items()))
681
+
682
+
683
+ def _file_sha256(path: Path) -> str:
684
+ digest = hashlib.sha256()
685
+ with path.open("rb") as handle:
686
+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
687
+ digest.update(chunk)
688
+ return digest.hexdigest()
689
+
690
+
691
+ def _text(value: Any) -> str:
692
+ return value.strip() if isinstance(value, str) else ""