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,460 @@
1
+ """Assessment target definitions and read-only projections."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import fnmatch
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+
11
+ ALLOWED_ASSESSMENT_TARGETS = {"auditable_brief", "delivery_brief"}
12
+ DEFAULT_ASSESSMENT_TARGET = "delivery_brief"
13
+ EXPERIMENT_080_CONDITION_PATH = Path("experiment/080/condition.json")
14
+ AUDIT_BINDING_SCHEMA = "mabw.auditable_audit_binding.v1"
15
+ AUDITABLE_TARGET_DOWNSTREAM_STAGES = ("analyst", "editor", "auditor")
16
+
17
+ AUDITABLE_TARGET_ARTIFACTS = {
18
+ "audited_brief": "output/intermediate/audited_brief.md",
19
+ "audit_report": "output/intermediate/audit_report.json",
20
+ "auditor_quality_gate_report": "output/intermediate/gates/auditor_quality_gate_report.json",
21
+ }
22
+
23
+ FINAL_ABSTRACT_QUALITY_WARNING_TYPES = {
24
+ "final_scope_title_mismatch",
25
+ "final_missing_comparison_basis",
26
+ "final_missing_limitation_section",
27
+ "final_incomplete_key_case_fields",
28
+ "final_unsupported_superlative",
29
+ }
30
+
31
+ ASSESSMENT_TARGET_CLAIM_SCOPE = {
32
+ "auditable_brief": [
33
+ "guidance_manifestation_in_audited_brief",
34
+ "evidence_use_under_frozen_fact_layer",
35
+ "auditor_gate_passage",
36
+ ],
37
+ "delivery_brief": [
38
+ "guidance_manifestation_in_reader_delivery",
39
+ "reader_clean_delivery",
40
+ "finalize_transform_included",
41
+ ],
42
+ }
43
+
44
+ ASSESSMENT_TARGET_EXCLUDED_CLAIM_SCOPE = {
45
+ "auditable_brief": [
46
+ "reader_clean_delivery",
47
+ "finalize_transform_correctness",
48
+ "management_ready_output",
49
+ "docx_pdf_delivery_quality",
50
+ ],
51
+ "delivery_brief": [],
52
+ }
53
+
54
+ ASSESSMENT_TARGET_INCLUDED_CONTROLS = {
55
+ "auditable_brief": [
56
+ "analyst complete",
57
+ "editor complete",
58
+ "auditor complete",
59
+ "auditor gates pass",
60
+ "audit report artifact present",
61
+ "auditor gate report artifact present",
62
+ "run integrity clean",
63
+ ],
64
+ "delivery_brief": [
65
+ "finalize complete",
66
+ "reader-clean delivery pass",
67
+ "delivery artifacts present",
68
+ "run archive present",
69
+ ],
70
+ }
71
+
72
+ AUDITABLE_TARGET_NEXT_ALLOWED = [
73
+ "multi-agent-brief experiments 080 register-run --case <case_dir> --condition <condition> --workspace <workspace> --output <run_record.json>",
74
+ "multi-agent-brief experiments 080 score-run --case <case_dir> --run-record <run_record.json> --output <scorecard.json>",
75
+ ]
76
+
77
+ AUDITABLE_TARGET_FORBIDDEN = [
78
+ "finalize",
79
+ "finalize-complete",
80
+ "deliver",
81
+ "DOCX/PDF delivery quality claims",
82
+ "reader-clean delivery claims",
83
+ ]
84
+
85
+
86
+ def assessment_target(container: dict[str, Any]) -> str:
87
+ value = container.get("assessment_target")
88
+ if isinstance(value, str) and value in ALLOWED_ASSESSMENT_TARGETS:
89
+ return value
90
+ return DEFAULT_ASSESSMENT_TARGET
91
+
92
+
93
+ def assessment_target_manifest(target: str) -> dict[str, Any]:
94
+ target = target if target in ALLOWED_ASSESSMENT_TARGETS else DEFAULT_ASSESSMENT_TARGET
95
+ if target == "auditable_brief":
96
+ return {
97
+ "assessment_target": target,
98
+ "target_status_semantics": "auditor_ready_internal_auditable_draft",
99
+ "target_artifact": "output/intermediate/audited_brief.md",
100
+ "target_complete_when": [
101
+ "analyst_complete",
102
+ "editor_complete",
103
+ "auditor_complete",
104
+ "analyst_editor_auditor_stage_completion_events_bound",
105
+ "auditor_quality_gates_pass",
106
+ "run_integrity_clean",
107
+ "no_active_repair",
108
+ ],
109
+ "included_control_scope": ASSESSMENT_TARGET_INCLUDED_CONTROLS[target],
110
+ "claim_scope": ASSESSMENT_TARGET_CLAIM_SCOPE[target],
111
+ "excluded_claim_scope": ASSESSMENT_TARGET_EXCLUDED_CLAIM_SCOPE[target],
112
+ "audit_binding_status": "required_python_owned",
113
+ "timing_semantics": "diagnostic_only",
114
+ "reader_clean_required": False,
115
+ "delivery_archive_required": False,
116
+ "next_allowed_commands": list(AUDITABLE_TARGET_NEXT_ALLOWED),
117
+ "forbidden_downstream_actions": list(AUDITABLE_TARGET_FORBIDDEN),
118
+ }
119
+ return {
120
+ "assessment_target": target,
121
+ "target_status_semantics": "reader_delivery_brief",
122
+ "target_artifact": "output/delivery/brief.md",
123
+ "target_complete_when": [
124
+ "finalize_complete",
125
+ "reader_clean_pass",
126
+ "delivery_artifacts_present",
127
+ "run_archive_present",
128
+ ],
129
+ "included_control_scope": ASSESSMENT_TARGET_INCLUDED_CONTROLS[target],
130
+ "claim_scope": ASSESSMENT_TARGET_CLAIM_SCOPE[target],
131
+ "excluded_claim_scope": ASSESSMENT_TARGET_EXCLUDED_CLAIM_SCOPE[target],
132
+ "audit_binding_status": "not_applicable",
133
+ "timing_semantics": "required_control_for_a_controlled",
134
+ "reader_clean_required": True,
135
+ "delivery_archive_required": True,
136
+ "next_allowed_commands": [],
137
+ "forbidden_downstream_actions": [],
138
+ }
139
+
140
+
141
+ def load_experiment_080_condition_metadata(workspace: str | Path) -> dict[str, Any] | None:
142
+ path = Path(workspace).expanduser().resolve() / EXPERIMENT_080_CONDITION_PATH
143
+ if not path.exists():
144
+ return None
145
+ try:
146
+ payload = json.loads(path.read_text(encoding="utf-8"))
147
+ except (OSError, json.JSONDecodeError):
148
+ return None
149
+ return payload if isinstance(payload, dict) else None
150
+
151
+
152
+ def project_assessment_target_status(
153
+ *,
154
+ condition_metadata: dict[str, Any] | None,
155
+ workflow_state: dict[str, Any] | None,
156
+ artifact_registry: dict[str, Any] | None,
157
+ auditor_gate_report: dict[str, Any] | None,
158
+ event_records: list[dict[str, Any]] | None = None,
159
+ ) -> dict[str, Any]:
160
+ if not isinstance(condition_metadata, dict):
161
+ return {"present": False}
162
+ target = assessment_target(condition_metadata)
163
+ manifest = assessment_target_manifest(target)
164
+ projection: dict[str, Any] = {
165
+ "present": True,
166
+ "experiment_id": condition_metadata.get("experiment_id"),
167
+ "case_id": condition_metadata.get("case_id"),
168
+ "condition": condition_metadata.get("condition"),
169
+ "assessment_target": target,
170
+ "assessment_target_manifest": manifest,
171
+ "target_complete": False,
172
+ "status": "not_applicable" if target != "auditable_brief" else "incomplete",
173
+ "audit_binding_status": manifest.get("audit_binding_status"),
174
+ "reasons": [],
175
+ }
176
+ if target != "auditable_brief":
177
+ return projection
178
+
179
+ reasons = _auditable_target_incomplete_reasons(
180
+ workflow_state=workflow_state,
181
+ artifact_registry=artifact_registry,
182
+ auditor_gate_report=auditor_gate_report,
183
+ event_records=event_records,
184
+ )
185
+ projection["target_complete"] = not reasons
186
+ projection["status"] = "complete" if not reasons else "incomplete"
187
+ projection["reasons"] = reasons
188
+ return projection
189
+
190
+
191
+ def _auditable_target_incomplete_reasons(
192
+ *,
193
+ workflow_state: dict[str, Any] | None,
194
+ artifact_registry: dict[str, Any] | None,
195
+ auditor_gate_report: dict[str, Any] | None,
196
+ event_records: list[dict[str, Any]] | None,
197
+ ) -> list[str]:
198
+ reasons: list[str] = []
199
+ workflow = workflow_state if isinstance(workflow_state, dict) else {}
200
+ registry = artifact_registry if isinstance(artifact_registry, dict) else {}
201
+ artifacts = registry.get("artifacts") if isinstance(registry.get("artifacts"), dict) else {}
202
+
203
+ current_stage = workflow.get("current_stage")
204
+ if current_stage not in {"finalize", None}:
205
+ reasons.append(f"workflow current_stage is {current_stage or '<missing>'}, expected finalize or terminal")
206
+ if isinstance(workflow.get("active_repair"), dict):
207
+ reasons.append("active repair is open")
208
+
209
+ statuses = workflow.get("stage_statuses") if isinstance(workflow.get("stage_statuses"), dict) else {}
210
+ for stage_id in AUDITABLE_TARGET_DOWNSTREAM_STAGES:
211
+ stage_status = statuses.get(stage_id) if isinstance(statuses.get(stage_id), dict) else {}
212
+ if stage_status.get("status") != "complete":
213
+ reasons.append(f"{stage_id} stage is not complete")
214
+ reasons.extend(
215
+ auditable_stage_completion_event_reasons(
216
+ workflow_state=workflow,
217
+ event_records=event_records,
218
+ )
219
+ )
220
+
221
+ integrity = workflow.get("run_integrity") if isinstance(workflow.get("run_integrity"), dict) else {}
222
+ if integrity.get("status") != "clean":
223
+ reasons.append("run_integrity is not clean")
224
+ if integrity.get("reference_eligible") is not True:
225
+ reasons.append("run_integrity.reference_eligible is not true")
226
+
227
+ for artifact_id, expected_path in AUDITABLE_TARGET_ARTIFACTS.items():
228
+ record = artifacts.get(artifact_id) if isinstance(artifacts.get(artifact_id), dict) else {}
229
+ if record.get("path") != expected_path:
230
+ reasons.append(f"{artifact_id} path is not {expected_path}")
231
+ if record.get("status") != "valid":
232
+ reasons.append(f"{artifact_id} is not valid in artifact_registry")
233
+ if not isinstance(record.get("sha256"), str) or len(str(record.get("sha256"))) != 64:
234
+ reasons.append(f"{artifact_id} has no frozen sha256")
235
+
236
+ _extend_audit_binding_reasons(
237
+ reasons,
238
+ workflow=workflow,
239
+ artifacts=artifacts,
240
+ event_records=event_records,
241
+ )
242
+
243
+ gate = auditor_gate_report if isinstance(auditor_gate_report, dict) else {}
244
+ if gate.get("status") != "pass" and not auditable_gate_has_only_final_abstract_advisory_warnings(gate):
245
+ reasons.append("auditor quality gate report status is not pass")
246
+ for result in gate.get("gate_results") or []:
247
+ if isinstance(result, dict) and (result.get("status") == "fail" or result.get("blocking") is True):
248
+ reasons.append("auditor quality gate report contains blocking gate_results")
249
+ break
250
+ for finding in gate.get("findings") or []:
251
+ if isinstance(finding, dict) and (
252
+ finding.get("blocking") is True or finding.get("blocking_level") == "blocking"
253
+ ):
254
+ reasons.append("auditor quality gate report contains blocking findings")
255
+ break
256
+
257
+ return reasons
258
+
259
+
260
+ def auditable_gate_has_only_final_abstract_advisory_warnings(payload: dict[str, Any]) -> bool:
261
+ if payload.get("status") != "warning":
262
+ return False
263
+ gate_results = payload.get("gate_results")
264
+ findings = payload.get("findings")
265
+ if not isinstance(gate_results, list) or not isinstance(findings, list):
266
+ return False
267
+ advisory_finding_ids: set[str] = set()
268
+ for finding in findings:
269
+ if not isinstance(finding, dict):
270
+ return False
271
+ if finding.get("gate_id") != "final_abstract_quality":
272
+ return False
273
+ if finding.get("finding_type") not in FINAL_ABSTRACT_QUALITY_WARNING_TYPES:
274
+ return False
275
+ if finding.get("blocking") is True or finding.get("blocking_level") == "blocking":
276
+ return False
277
+ finding_id = str(finding.get("finding_id") or "")
278
+ if finding_id:
279
+ advisory_finding_ids.add(finding_id)
280
+
281
+ saw_advisory_warning = False
282
+ for result in gate_results:
283
+ if not isinstance(result, dict):
284
+ return False
285
+ gate_id = result.get("gate_id")
286
+ status = result.get("status")
287
+ if status == "pass":
288
+ continue
289
+ if gate_id != "final_abstract_quality" or status != "warning" or result.get("blocking") is True:
290
+ return False
291
+ finding_ids = result.get("finding_ids")
292
+ if not isinstance(finding_ids, list):
293
+ return False
294
+ if any(str(finding_id) not in advisory_finding_ids for finding_id in finding_ids):
295
+ return False
296
+ saw_advisory_warning = True
297
+ return saw_advisory_warning
298
+
299
+
300
+ def auditable_gate_projection_has_only_final_abstract_advisory_warnings(gate: dict[str, Any]) -> bool:
301
+ if gate.get("report_status") != "warning" or gate.get("no_blocking") is not True:
302
+ return False
303
+ warning_types = gate.get("warning_finding_types")
304
+ if not isinstance(warning_types, list) or not warning_types:
305
+ return False
306
+ normalized = {str(item) for item in warning_types if isinstance(item, str) and item}
307
+ return bool(normalized) and normalized <= FINAL_ABSTRACT_QUALITY_WARNING_TYPES
308
+
309
+
310
+ def auditable_stage_completion_event_reasons(
311
+ *,
312
+ workflow_state: dict[str, Any] | None,
313
+ event_records: list[dict[str, Any]] | None,
314
+ ) -> list[str]:
315
+ workflow = workflow_state if isinstance(workflow_state, dict) else {}
316
+ if event_records is None:
317
+ return ["stage completion event log is unavailable"]
318
+ run_id = workflow.get("run_id")
319
+ run_id_text = run_id.strip() if isinstance(run_id, str) and run_id.strip() else ""
320
+ if not run_id_text:
321
+ return ["stage completion events cannot verify workflow run_id"]
322
+ reasons: list[str] = []
323
+ for stage_id in AUDITABLE_TARGET_DOWNSTREAM_STAGES:
324
+ if _auditable_stage_completion_event(event_records, run_id=run_id_text, stage_id=stage_id) is None:
325
+ reasons.append(f"{stage_id} stage completion decision_recorded event is missing")
326
+ return reasons
327
+
328
+
329
+ def _extend_audit_binding_reasons(
330
+ reasons: list[str],
331
+ *,
332
+ workflow: dict[str, Any],
333
+ artifacts: dict[str, Any],
334
+ event_records: list[dict[str, Any]] | None,
335
+ ) -> None:
336
+ statuses = workflow.get("stage_statuses") if isinstance(workflow.get("stage_statuses"), dict) else {}
337
+ auditor_status = statuses.get("auditor") if isinstance(statuses.get("auditor"), dict) else {}
338
+ auditor_metadata = (
339
+ auditor_status.get("metadata")
340
+ if isinstance(auditor_status.get("metadata"), dict)
341
+ else {}
342
+ )
343
+ binding = (
344
+ auditor_metadata.get("audit_binding")
345
+ if isinstance(auditor_metadata.get("audit_binding"), dict)
346
+ else {}
347
+ )
348
+ if binding.get("schema_version") != AUDIT_BINDING_SCHEMA:
349
+ reasons.append("auditor audit_binding is missing or has unsupported schema")
350
+ return
351
+ expected = {
352
+ "claim_ledger_sha256": _artifact_sha(artifacts, "claim_ledger"),
353
+ "audited_brief_sha256": _artifact_sha(artifacts, "audited_brief"),
354
+ "audit_report_sha256": _artifact_sha(artifacts, "audit_report"),
355
+ }
356
+ for field, expected_sha in expected.items():
357
+ actual = binding.get(field)
358
+ if not isinstance(expected_sha, str) or len(expected_sha) != 64:
359
+ reasons.append(f"audit binding cannot verify {field.removesuffix('_sha256')}")
360
+ elif actual != expected_sha:
361
+ reasons.append(f"audit binding {field} does not match artifact_registry")
362
+ run_id = workflow.get("run_id")
363
+ run_id_text = run_id.strip() if isinstance(run_id, str) and run_id.strip() else ""
364
+ repair_ids = binding.get("relevant_repair_transaction_ids")
365
+ if not isinstance(repair_ids, list) or any(not isinstance(item, str) or not item for item in repair_ids):
366
+ reasons.append("audit binding relevant_repair_transaction_ids is invalid")
367
+ elif event_records is not None:
368
+ if not run_id_text:
369
+ reasons.append("audit binding repair history cannot verify workflow run_id")
370
+ else:
371
+ expected_repair_ids = _auditable_brief_repair_transaction_ids(
372
+ event_records,
373
+ run_id=run_id_text,
374
+ )
375
+ if list(repair_ids) != expected_repair_ids:
376
+ reasons.append("audit binding relevant_repair_transaction_ids does not match event_log")
377
+ auditor_stage_transaction_id = binding.get("auditor_stage_transaction_id")
378
+ if not isinstance(auditor_stage_transaction_id, str) or not auditor_stage_transaction_id:
379
+ reasons.append("audit binding auditor_stage_transaction_id is missing")
380
+ elif event_records is not None:
381
+ if not run_id_text:
382
+ reasons.append("audit binding auditor_stage_transaction_id cannot verify workflow run_id")
383
+ elif _auditable_auditor_completion_event(
384
+ event_records,
385
+ run_id=run_id_text,
386
+ transaction_id=auditor_stage_transaction_id,
387
+ ) is None:
388
+ reasons.append("audit binding auditor_stage_transaction_id does not match event_log")
389
+
390
+
391
+ def _artifact_sha(artifacts: dict[str, Any], artifact_id: str) -> str | None:
392
+ record = artifacts.get(artifact_id) if isinstance(artifacts.get(artifact_id), dict) else {}
393
+ sha = record.get("sha256")
394
+ return sha if isinstance(sha, str) else None
395
+
396
+
397
+ def _auditable_brief_repair_transaction_ids(
398
+ records: list[dict[str, Any]],
399
+ *,
400
+ run_id: str,
401
+ ) -> list[str]:
402
+ ids: list[str] = []
403
+ artifact_path = "output/intermediate/audited_brief.md"
404
+ for event in records:
405
+ if event.get("run_id") != run_id:
406
+ continue
407
+ if event.get("event_type") != "repair_completed":
408
+ continue
409
+ metadata = event.get("metadata") if isinstance(event.get("metadata"), dict) else {}
410
+ allowed = [str(item) for item in metadata.get("allowed_artifacts") or []]
411
+ if not any(_artifact_path_matches(pattern, artifact_path) for pattern in allowed):
412
+ continue
413
+ transaction_id = metadata.get("transaction_id") or metadata.get("repair_transaction_id")
414
+ if isinstance(transaction_id, str) and transaction_id and transaction_id not in ids:
415
+ ids.append(transaction_id)
416
+ return ids
417
+
418
+
419
+ def _artifact_path_matches(pattern: str, path: str) -> bool:
420
+ candidate = pattern.strip()
421
+ return bool(candidate and (path == candidate or fnmatch.fnmatch(path, candidate)))
422
+
423
+
424
+ def _auditable_auditor_completion_event(
425
+ records: list[dict[str, Any]],
426
+ *,
427
+ run_id: str,
428
+ transaction_id: str,
429
+ ) -> dict[str, Any] | None:
430
+ for event in records:
431
+ if event.get("run_id") != run_id:
432
+ continue
433
+ if event.get("event_type") != "decision_recorded":
434
+ continue
435
+ if event.get("stage_id") != "auditor" or event.get("decision") != "continue":
436
+ continue
437
+ metadata = event.get("metadata") if isinstance(event.get("metadata"), dict) else {}
438
+ if metadata.get("transaction_id") == transaction_id:
439
+ return event
440
+ return None
441
+
442
+
443
+ def _auditable_stage_completion_event(
444
+ records: list[dict[str, Any]],
445
+ *,
446
+ run_id: str,
447
+ stage_id: str,
448
+ ) -> dict[str, Any] | None:
449
+ for event in records:
450
+ if event.get("run_id") != run_id:
451
+ continue
452
+ if event.get("event_type") != "decision_recorded":
453
+ continue
454
+ if event.get("stage_id") != stage_id or event.get("decision") != "continue":
455
+ continue
456
+ metadata = event.get("metadata") if isinstance(event.get("metadata"), dict) else {}
457
+ transaction_id = metadata.get("transaction_id")
458
+ if isinstance(transaction_id, str) and transaction_id:
459
+ return event
460
+ return None
@@ -0,0 +1,225 @@
1
+ """Validation helpers for MABW orchestration contract registries."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from multi_agent_brief.contracts.errors import FieldViolation
8
+ from multi_agent_brief.contracts.registry import ContractRegistry
9
+ from multi_agent_brief.contracts.role_topology import (
10
+ ROLE_TOPOLOGY_SATISFIER_VALUES,
11
+ ROLE_TOPOLOGY_VALUES,
12
+ resolve_role_topology,
13
+ )
14
+
15
+
16
+ PARITY_CONFIG_FILES = (
17
+ "stage_specs.yaml",
18
+ "artifact_contracts.yaml",
19
+ "orchestrator_contract.yaml",
20
+ "policy_packs/default.yaml",
21
+ )
22
+
23
+
24
+ def validate_contract_registry(registry: ContractRegistry) -> list[FieldViolation]:
25
+ """Validate stage/artifact/decision references in a registry."""
26
+ violations: list[FieldViolation] = []
27
+ stage_ids = [stage.stage_id for stage in registry.stages]
28
+ artifact_ids = [artifact.artifact_id for artifact in registry.artifacts]
29
+ stage_id_set = set(stage_ids)
30
+ artifact_id_set = set(artifact_ids)
31
+ decision_set = set(registry.decision_vocabulary)
32
+ producer_kind_set = set(registry.producer_kind_values)
33
+
34
+ violations.extend(_duplicate_violations("stages.stage_id", stage_ids))
35
+ violations.extend(_duplicate_violations("artifacts.artifact_id", artifact_ids))
36
+ violations.extend(_role_topology_violations(registry))
37
+
38
+ for stage in registry.stages:
39
+ if not stage.stage_id:
40
+ violations.append(FieldViolation("stages.stage_id", "stage_id is required"))
41
+ for artifact_id in stage.expected_artifacts:
42
+ if artifact_id not in artifact_id_set:
43
+ violations.append(
44
+ FieldViolation(
45
+ f"stages.{stage.stage_id}.expected_artifacts",
46
+ f"unknown artifact: {artifact_id}",
47
+ )
48
+ )
49
+ for decision in stage.allowed_decisions:
50
+ if decision not in decision_set:
51
+ violations.append(
52
+ FieldViolation(
53
+ f"stages.{stage.stage_id}.allowed_decisions",
54
+ f"unknown decision: {decision}",
55
+ )
56
+ )
57
+
58
+ for artifact in registry.artifacts:
59
+ if not artifact.artifact_id:
60
+ violations.append(
61
+ FieldViolation("artifacts.artifact_id", "artifact_id is required")
62
+ )
63
+ if producer_kind_set and artifact.producer_kind not in producer_kind_set:
64
+ violations.append(
65
+ FieldViolation(
66
+ f"artifacts.{artifact.artifact_id}.producer_kind",
67
+ f"unknown producer kind: {artifact.producer_kind}",
68
+ )
69
+ )
70
+ if artifact.producer_kind == "workflow_stage" and artifact.producer_stage not in stage_id_set:
71
+ violations.append(
72
+ FieldViolation(
73
+ f"artifacts.{artifact.artifact_id}.producer_stage",
74
+ f"unknown producer stage: {artifact.producer_stage}",
75
+ )
76
+ )
77
+ for stage_id in artifact.consumer_stages:
78
+ if stage_id not in stage_id_set:
79
+ violations.append(
80
+ FieldViolation(
81
+ f"artifacts.{artifact.artifact_id}.consumer_stages",
82
+ f"unknown consumer stage: {stage_id}",
83
+ )
84
+ )
85
+ for decision in artifact.allowed_decisions:
86
+ if decision not in decision_set:
87
+ violations.append(
88
+ FieldViolation(
89
+ f"artifacts.{artifact.artifact_id}.allowed_decisions",
90
+ f"unknown decision: {decision}",
91
+ )
92
+ )
93
+
94
+ return violations
95
+
96
+
97
+ def _role_topology_violations(registry: ContractRegistry) -> list[FieldViolation]:
98
+ violations: list[FieldViolation] = []
99
+ try:
100
+ resolve_role_topology(registry.policy_pack_data)
101
+ except ValueError:
102
+ violations.append(
103
+ FieldViolation(
104
+ "policy.role_topology",
105
+ "must be one of: " + ", ".join(sorted(ROLE_TOPOLOGY_VALUES)),
106
+ )
107
+ )
108
+
109
+ artifact_id_set = {artifact.artifact_id for artifact in registry.artifacts}
110
+ for stage in registry.stage_specs_data.get("workflow", {}).get("stages", []) or []:
111
+ if not isinstance(stage, dict):
112
+ continue
113
+ stage_id = str(stage.get("stage_id") or "")
114
+ topology_satisfaction = stage.get("topology_satisfaction")
115
+ if topology_satisfaction is None:
116
+ continue
117
+ if not isinstance(topology_satisfaction, dict):
118
+ violations.append(
119
+ FieldViolation(
120
+ f"stages.{stage_id}.topology_satisfaction",
121
+ "must be an object",
122
+ )
123
+ )
124
+ continue
125
+ for topology, rule in topology_satisfaction.items():
126
+ if str(topology) not in ROLE_TOPOLOGY_VALUES:
127
+ violations.append(
128
+ FieldViolation(
129
+ f"stages.{stage_id}.topology_satisfaction",
130
+ f"unknown role topology: {topology}",
131
+ )
132
+ )
133
+ continue
134
+ if not isinstance(rule, dict):
135
+ violations.append(
136
+ FieldViolation(
137
+ f"stages.{stage_id}.topology_satisfaction.{topology}",
138
+ "must be an object",
139
+ )
140
+ )
141
+ continue
142
+ satisfied_by = str(rule.get("satisfied_by") or "").strip()
143
+ if not satisfied_by:
144
+ continue
145
+ if satisfied_by not in ROLE_TOPOLOGY_SATISFIER_VALUES:
146
+ violations.append(
147
+ FieldViolation(
148
+ f"stages.{stage_id}.topology_satisfaction.{topology}.satisfied_by",
149
+ f"unknown topology satisfier: {satisfied_by}",
150
+ )
151
+ )
152
+ required_artifacts = rule.get("required_artifacts") or []
153
+ required_artifacts_field = (
154
+ f"stages.{stage_id}.topology_satisfaction.{topology}.required_artifacts"
155
+ )
156
+ if not isinstance(required_artifacts, list):
157
+ violations.append(
158
+ FieldViolation(
159
+ required_artifacts_field,
160
+ "must be a list of artifact ids",
161
+ )
162
+ )
163
+ continue
164
+ for artifact_id in required_artifacts:
165
+ if not isinstance(artifact_id, str) or not artifact_id.strip():
166
+ violations.append(
167
+ FieldViolation(
168
+ required_artifacts_field,
169
+ "must contain only non-empty artifact id strings",
170
+ )
171
+ )
172
+ continue
173
+ if artifact_id not in artifact_id_set:
174
+ violations.append(
175
+ FieldViolation(
176
+ required_artifacts_field,
177
+ f"unknown artifact: {artifact_id}",
178
+ )
179
+ )
180
+ return violations
181
+
182
+
183
+ def validate_config_parity(
184
+ *,
185
+ root_config_dir: str | Path,
186
+ package_config_dir: str | Path,
187
+ ) -> list[FieldViolation]:
188
+ """Validate that root and packaged config copies are byte-equivalent."""
189
+ root = Path(root_config_dir)
190
+ package = Path(package_config_dir)
191
+ violations: list[FieldViolation] = []
192
+ for rel_path in PARITY_CONFIG_FILES:
193
+ root_path = root / rel_path
194
+ package_path = package / rel_path
195
+ if not root_path.exists():
196
+ violations.append(
197
+ FieldViolation(f"configs.{rel_path}", "root config file is missing")
198
+ )
199
+ continue
200
+ if not package_path.exists():
201
+ violations.append(
202
+ FieldViolation(f"configs.{rel_path}", "packaged config file is missing")
203
+ )
204
+ continue
205
+ if root_path.read_text(encoding="utf-8") != package_path.read_text(encoding="utf-8"):
206
+ violations.append(
207
+ FieldViolation(
208
+ f"configs.{rel_path}",
209
+ "root and packaged config copies differ",
210
+ )
211
+ )
212
+ return violations
213
+
214
+
215
+ def _duplicate_violations(field: str, values: list[str]) -> list[FieldViolation]:
216
+ seen: set[str] = set()
217
+ duplicates: set[str] = set()
218
+ for value in values:
219
+ if value in seen:
220
+ duplicates.add(value)
221
+ seen.add(value)
222
+ return [
223
+ FieldViolation(field, f"duplicate value: {value}")
224
+ for value in sorted(duplicates)
225
+ ]