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,358 @@
1
+ """Audience profile runtime surface helpers.
2
+
3
+ This module owns workspace-local taste context files. It deliberately does not
4
+ import CLI profile types so runtime code can use it without a CLI dependency.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import os
11
+ import uuid
12
+ from dataclasses import dataclass
13
+ from datetime import datetime, timezone
14
+ from pathlib import Path
15
+ from typing import Any, Mapping
16
+
17
+ import yaml
18
+
19
+
20
+ AUDIENCE_MEMORY_FILES = {
21
+ "audience_profile": "audience_profile.md",
22
+ "audience_profile_snapshot": "output/intermediate/audience_profile_snapshot.md",
23
+ }
24
+
25
+ _SNAPSHOT_MARKER = "<!-- mabw:audience-profile-snapshot"
26
+ _SNAPSHOT_END = "-->"
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class AudienceProfileResult:
31
+ path: Path
32
+ relative_path: str
33
+ created: bool
34
+ missing: bool
35
+ sha256: str
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class AudienceSnapshotResult:
40
+ path: Path
41
+ relative_path: str
42
+ created: bool
43
+ stale_rebuilt: bool
44
+ run_id: str
45
+ source_sha256: str
46
+ captured_body_sha256: str
47
+ snapshot_sha256: str
48
+ profile_created: bool
49
+ profile_missing: bool
50
+
51
+
52
+ def utc_now() -> str:
53
+ return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
54
+
55
+
56
+ def sha256_text(text: str) -> str:
57
+ return hashlib.sha256(text.encode("utf-8")).hexdigest()
58
+
59
+
60
+ def sha256_file(path: Path) -> str:
61
+ digest = hashlib.sha256()
62
+ with path.open("rb") as fh:
63
+ for chunk in iter(lambda: fh.read(1024 * 1024), b""):
64
+ digest.update(chunk)
65
+ return digest.hexdigest()
66
+
67
+
68
+ def _workspace_path(workspace: str | Path, rel_path: str) -> Path:
69
+ ws = Path(workspace).expanduser().resolve()
70
+ path = (ws / rel_path).resolve()
71
+ try:
72
+ path.relative_to(ws)
73
+ except ValueError as exc:
74
+ raise ValueError(f"Audience memory path escapes workspace: {rel_path}") from exc
75
+ return path
76
+
77
+
78
+ def _write_text_atomic(path: Path, text: str) -> None:
79
+ path.parent.mkdir(parents=True, exist_ok=True)
80
+ tmp = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp")
81
+ try:
82
+ tmp.write_text(text, encoding="utf-8")
83
+ os.replace(tmp, path)
84
+ finally:
85
+ try:
86
+ tmp.unlink(missing_ok=True)
87
+ except OSError:
88
+ pass
89
+
90
+
91
+ def _as_text(value: Any, default: str = "") -> str:
92
+ if value is None:
93
+ return default
94
+ text = str(value).strip()
95
+ return text if text else default
96
+
97
+
98
+ def _as_list(value: Any) -> list[str]:
99
+ if value is None:
100
+ return []
101
+ if isinstance(value, (list, tuple, set)):
102
+ return [str(item).strip() for item in value if str(item).strip()]
103
+ text = str(value).strip()
104
+ if not text:
105
+ return []
106
+ return [part.strip() for part in text.replace(";", ",").split(",") if part.strip()]
107
+
108
+
109
+ def profile_data_from_object(profile: Any) -> dict[str, Any]:
110
+ """Convert an arbitrary profile-like object into plain profile data."""
111
+ fields = [
112
+ "company",
113
+ "industry",
114
+ "industry_text",
115
+ "role",
116
+ "audience",
117
+ "audience_profile",
118
+ "brief_title",
119
+ "task_objective",
120
+ "interface_language",
121
+ "output_language",
122
+ "cadence",
123
+ "source_profile",
124
+ "focus_areas",
125
+ "forbidden_sources",
126
+ "output_formats",
127
+ ]
128
+ return {field: getattr(profile, field, "") for field in fields}
129
+
130
+
131
+ def profile_data_from_workspace_config(workspace: str | Path) -> dict[str, Any]:
132
+ """Extract audience profile defaults from an existing workspace config."""
133
+ config_path = Path(workspace).expanduser().resolve() / "config.yaml"
134
+ if not config_path.exists():
135
+ return {}
136
+ data = yaml.safe_load(config_path.read_text(encoding="utf-8")) or {}
137
+ if not isinstance(data, dict):
138
+ return {}
139
+
140
+ project = data.get("project") or {}
141
+ audience_profile = data.get("audience_profile") or {}
142
+ language = data.get("language") or {}
143
+ report = data.get("report") or {}
144
+ source = data.get("source") or {}
145
+ source_strategy = data.get("source_strategy") or {}
146
+ output = data.get("output") or {}
147
+ task = data.get("task") or {}
148
+
149
+ formats = output.get("formats") if isinstance(output, dict) else []
150
+ if not isinstance(formats, list):
151
+ formats = []
152
+
153
+ return {
154
+ "company": project.get("company") or project.get("organization") or project.get("name") or "",
155
+ "industry": project.get("industry") or "",
156
+ "industry_text": project.get("industry") or "",
157
+ "role": project.get("role") or "",
158
+ "audience": project.get("audience") or "",
159
+ "audience_profile": audience_profile.get("id") or "",
160
+ "brief_title": project.get("name") or "",
161
+ "task_objective": task.get("objective") or "",
162
+ "interface_language": language.get("interface") or "",
163
+ "output_language": language.get("output") or "",
164
+ "cadence": report.get("cadence") or "",
165
+ "source_profile": source.get("mode") or source_strategy.get("profile") or "",
166
+ "output_formats": formats,
167
+ }
168
+
169
+
170
+ def build_default_audience_profile(profile_data: Mapping[str, Any] | None = None) -> str:
171
+ """Build a human-editable audience profile Markdown template."""
172
+ data = dict(profile_data or {})
173
+ company = _as_text(data.get("company"), "Unknown organization")
174
+ industry = _as_text(data.get("industry_text") or data.get("industry"), "Unknown industry/theme")
175
+ audience = _as_text(data.get("audience"), "management")
176
+ profile_id = _as_text(data.get("audience_profile"), "default")
177
+ title = _as_text(data.get("brief_title"), "Workspace brief")
178
+ objective = _as_text(data.get("task_objective"), "Summarize material updates for the target reader.")
179
+ language = _as_text(data.get("output_language") or data.get("interface_language"), "en-US")
180
+ cadence = _as_text(data.get("cadence"), "weekly")
181
+ role = _as_text(data.get("role"), "strategy_office")
182
+ source_profile = _as_text(data.get("source_profile"), "llm_decide")
183
+ focus_areas = _as_list(data.get("focus_areas"))
184
+ forbidden_sources = _as_list(data.get("forbidden_sources"))
185
+ output_formats = _as_list(data.get("output_formats"))
186
+
187
+ focus_lines = "\n".join(f"- {item}" for item in focus_areas) or "- Add durable audience priorities here."
188
+ avoid_lines = "\n".join(f"- {item}" for item in forbidden_sources) or "- Do not let taste preferences override evidence or audit constraints."
189
+ output_lines = "\n".join(f"- {item}" for item in output_formats) or "- Markdown\n- DOCX"
190
+
191
+ return (
192
+ "# Audience Profile\n\n"
193
+ "This workspace-local file records reader taste, department preferences, and recurring editorial guidance.\n"
194
+ "It is not source evidence, not a correctness contract, and not a stage gate.\n"
195
+ "Human edits are allowed. Mid-run edits apply to the next run after a new snapshot is created.\n\n"
196
+ "## Reader Context\n\n"
197
+ f"- Organization: {company}\n"
198
+ f"- Industry/theme: {industry}\n"
199
+ f"- Reader/audience: {audience}\n"
200
+ f"- Audience profile id: {profile_id}\n"
201
+ f"- Internal role/use context: {role}\n"
202
+ f"- Brief title: {title}\n"
203
+ f"- Cadence: {cadence}\n"
204
+ f"- Output language: {language}\n"
205
+ f"- Source mode: {source_profile}\n\n"
206
+ "## Decision Use\n\n"
207
+ f"{objective}\n\n"
208
+ "## Preferred Brief Style\n\n"
209
+ "- Start with the decision-useful conclusion when evidence supports it.\n"
210
+ "- Keep claims bounded by source quality, freshness, and uncertainty.\n"
211
+ "- Prefer concise, concrete implications over generic narrative.\n\n"
212
+ "## Tone And Structure Preferences\n\n"
213
+ "- Write for the stated reader, not for a generic public audience.\n"
214
+ "- Preserve auditability during drafting; reader-facing cleanup happens after audit.\n"
215
+ "- Surface risks, caveats, and missing evidence clearly when relevant.\n\n"
216
+ "## Focus Areas\n\n"
217
+ f"{focus_lines}\n\n"
218
+ "## Output Expectations\n\n"
219
+ f"{output_lines}\n\n"
220
+ "## Recurring Feedback\n\n"
221
+ "- Add durable feedback patterns here after human review.\n\n"
222
+ "## Avoid\n\n"
223
+ f"{avoid_lines}\n\n"
224
+ "## Human Notes\n\n"
225
+ "- Add workspace-specific taste notes here.\n"
226
+ )
227
+
228
+
229
+ def ensure_audience_profile(
230
+ workspace: str | Path,
231
+ profile_data: Mapping[str, Any] | None = None,
232
+ ) -> AudienceProfileResult:
233
+ """Ensure the workspace has an audience profile."""
234
+ path = _workspace_path(workspace, AUDIENCE_MEMORY_FILES["audience_profile"])
235
+ missing = not path.exists()
236
+ created = False
237
+ if missing:
238
+ _write_text_atomic(path, build_default_audience_profile(profile_data))
239
+ created = True
240
+ return AudienceProfileResult(
241
+ path=path,
242
+ relative_path=AUDIENCE_MEMORY_FILES["audience_profile"],
243
+ created=created,
244
+ missing=missing,
245
+ sha256=sha256_file(path),
246
+ )
247
+
248
+
249
+ def _parse_snapshot_metadata(text: str) -> dict[str, str] | None:
250
+ """Parse only the strict top-of-file audience snapshot metadata block."""
251
+ if not text.startswith(_SNAPSHOT_MARKER):
252
+ return None
253
+ end = text.find(_SNAPSHOT_END)
254
+ if end == -1:
255
+ return None
256
+ block = text[len(_SNAPSHOT_MARKER):end].strip()
257
+ metadata: dict[str, str] = {}
258
+ for line in block.splitlines():
259
+ line = line.strip()
260
+ if not line:
261
+ continue
262
+ if ":" not in line:
263
+ return None
264
+ key, value = line.split(":", 1)
265
+ key = key.strip()
266
+ value = value.strip()
267
+ if not key or not value:
268
+ return None
269
+ metadata[key] = value
270
+ required = {"run_id", "source", "created_at", "source_sha256", "captured_body_sha256"}
271
+ if not required.issubset(metadata):
272
+ return None
273
+ if metadata.get("source") != AUDIENCE_MEMORY_FILES["audience_profile"]:
274
+ return None
275
+ return metadata
276
+
277
+
278
+ def _snapshot_text(
279
+ *,
280
+ run_id: str,
281
+ created_at: str,
282
+ source_sha256: str,
283
+ captured_body_sha256: str,
284
+ profile_body: str,
285
+ ) -> str:
286
+ body = profile_body.rstrip() + "\n"
287
+ return (
288
+ "<!-- mabw:audience-profile-snapshot\n"
289
+ f"run_id: {run_id}\n"
290
+ f"source: {AUDIENCE_MEMORY_FILES['audience_profile']}\n"
291
+ f"created_at: {created_at}\n"
292
+ f"source_sha256: {source_sha256}\n"
293
+ f"captured_body_sha256: {captured_body_sha256}\n"
294
+ "-->\n\n"
295
+ "# Audience Profile Snapshot\n\n"
296
+ f"- Run ID: {run_id}\n"
297
+ f"- Source: {AUDIENCE_MEMORY_FILES['audience_profile']}\n"
298
+ f"- Created at: {created_at}\n"
299
+ f"- Source SHA256: {source_sha256}\n"
300
+ f"- Captured Body SHA256: {captured_body_sha256}\n"
301
+ "- Run behavior: use this snapshot for the current run.\n"
302
+ f"- Mid-run edits to {AUDIENCE_MEMORY_FILES['audience_profile']} apply to later runs only.\n\n"
303
+ "## Captured Audience Profile\n\n"
304
+ f"{body}"
305
+ )
306
+
307
+
308
+ def create_audience_profile_snapshot(
309
+ workspace: str | Path,
310
+ *,
311
+ run_id: str,
312
+ profile_data: Mapping[str, Any] | None = None,
313
+ ) -> AudienceSnapshotResult:
314
+ """Create or reuse the active run audience profile snapshot."""
315
+ profile = ensure_audience_profile(workspace, profile_data)
316
+ snapshot_path = _workspace_path(workspace, AUDIENCE_MEMORY_FILES["audience_profile_snapshot"])
317
+
318
+ if snapshot_path.exists():
319
+ existing_text = snapshot_path.read_text(encoding="utf-8")
320
+ metadata = _parse_snapshot_metadata(existing_text)
321
+ if metadata and metadata.get("run_id") == run_id:
322
+ return AudienceSnapshotResult(
323
+ path=snapshot_path,
324
+ relative_path=AUDIENCE_MEMORY_FILES["audience_profile_snapshot"],
325
+ created=False,
326
+ stale_rebuilt=False,
327
+ run_id=run_id,
328
+ source_sha256=metadata["source_sha256"],
329
+ captured_body_sha256=metadata["captured_body_sha256"],
330
+ snapshot_sha256=sha256_file(snapshot_path),
331
+ profile_created=profile.created,
332
+ profile_missing=profile.missing,
333
+ )
334
+
335
+ profile_body = profile.path.read_text(encoding="utf-8")
336
+ source_sha256 = sha256_text(profile_body)
337
+ captured_body_sha256 = source_sha256
338
+ text = _snapshot_text(
339
+ run_id=run_id,
340
+ created_at=utc_now(),
341
+ source_sha256=source_sha256,
342
+ captured_body_sha256=captured_body_sha256,
343
+ profile_body=profile_body,
344
+ )
345
+ stale_rebuilt = snapshot_path.exists()
346
+ _write_text_atomic(snapshot_path, text)
347
+ return AudienceSnapshotResult(
348
+ path=snapshot_path,
349
+ relative_path=AUDIENCE_MEMORY_FILES["audience_profile_snapshot"],
350
+ created=True,
351
+ stale_rebuilt=stale_rebuilt,
352
+ run_id=run_id,
353
+ source_sha256=source_sha256,
354
+ captured_body_sha256=captured_body_sha256,
355
+ snapshot_sha256=sha256_file(snapshot_path),
356
+ profile_created=profile.created,
357
+ profile_missing=profile.missing,
358
+ )
@@ -0,0 +1,36 @@
1
+ """Audit utilities."""
2
+
3
+ from multi_agent_brief.audit.deterministic import DeterministicAuditAgent, run_deterministic_audit
4
+ from multi_agent_brief.audit.final_quality import FinalQualityAuditAgent, FinalQualityConfig
5
+ from multi_agent_brief.audit.harness import QualityHarnessAuditAgent
6
+ from multi_agent_brief.audit.interfaces import AuditAgentInterface, CompositeAuditAgent
7
+ from multi_agent_brief.audit.semantic import (
8
+ SEMANTIC_SUPPORT_CALIBRATION_METADATA_KEY,
9
+ SEMANTIC_SUPPORT_INVALID_CALIBRATION_LABEL,
10
+ SEMANTIC_SUPPORT_PROPOSAL_FINDING_TYPE,
11
+ SEMANTIC_SUPPORT_PROPOSAL_LABELS,
12
+ NoOpSemanticAuditAgent,
13
+ SemanticAuditPromptBuilder,
14
+ findings_from_semantic_proposal_rows,
15
+ normalize_calibration_label,
16
+ semantic_support_proposal_finding,
17
+ )
18
+
19
+ __all__ = [
20
+ "SEMANTIC_SUPPORT_CALIBRATION_METADATA_KEY",
21
+ "SEMANTIC_SUPPORT_INVALID_CALIBRATION_LABEL",
22
+ "SEMANTIC_SUPPORT_PROPOSAL_FINDING_TYPE",
23
+ "SEMANTIC_SUPPORT_PROPOSAL_LABELS",
24
+ "normalize_calibration_label",
25
+ "AuditAgentInterface",
26
+ "CompositeAuditAgent",
27
+ "DeterministicAuditAgent",
28
+ "FinalQualityAuditAgent",
29
+ "FinalQualityConfig",
30
+ "NoOpSemanticAuditAgent",
31
+ "QualityHarnessAuditAgent",
32
+ "SemanticAuditPromptBuilder",
33
+ "findings_from_semantic_proposal_rows",
34
+ "run_deterministic_audit",
35
+ "semantic_support_proposal_finding",
36
+ ]
@@ -0,0 +1,181 @@
1
+ """Comparable Case Applicability audit (v0.5.3 PR 3).
2
+
3
+ Enforces boundaries on analogous/comparable evidence:
4
+ 1. evidence_relation=analogous MUST have applicability_reason.
5
+ 2. Single comparable case can only support hypothesis/to_verify, NOT strong action.
6
+ 3. No local direct evidence → must generate verification_path.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+
12
+ from multi_agent_brief.analysis_blocks.schemas import AnalysisBlock
13
+ from multi_agent_brief.core.claim_ledger import ClaimLedger
14
+ from multi_agent_brief.core.schemas import Claim
15
+
16
+
17
+ @dataclass
18
+ class CaseApplicabilityFinding:
19
+ """One audit finding for case applicability."""
20
+
21
+ finding_type: str
22
+ severity: str # "warning" or "fail"
23
+ block_id: str
24
+ claim_id: str
25
+ description: str
26
+ recommendation: str
27
+
28
+
29
+ def audit_case_applicability(
30
+ blocks: list[AnalysisBlock],
31
+ ledger: ClaimLedger,
32
+ ) -> list[CaseApplicabilityFinding]:
33
+ """Audit case applicability across all analysis blocks.
34
+
35
+ Returns findings for:
36
+ - analogous claims without applicability_reason
37
+ - single comparable case supporting strong action
38
+ - missing verification_path when no local direct evidence
39
+ """
40
+ findings: list[CaseApplicabilityFinding] = []
41
+
42
+ for block in blocks:
43
+ _check_analogous_applicability(block, ledger, findings)
44
+ _check_single_case_action(block, ledger, findings)
45
+ _check_verification_path(block, findings)
46
+
47
+ return findings
48
+
49
+
50
+ def _check_analogous_applicability(
51
+ block: AnalysisBlock,
52
+ ledger: ClaimLedger,
53
+ findings: list[CaseApplicabilityFinding],
54
+ ) -> None:
55
+ """Rule 1: analogous claims MUST have applicability_reason."""
56
+ for cid in block.case_claim_ids:
57
+ claim = ledger.get_claim(cid)
58
+ if not claim:
59
+ continue
60
+ if claim.evidence_relation == "analogous" and not claim.applicability_reason.strip():
61
+ findings.append(CaseApplicabilityFinding(
62
+ finding_type="missing_applicability_reason",
63
+ severity="warning",
64
+ block_id=block.block_id,
65
+ claim_id=cid,
66
+ description=(
67
+ f"Claim {cid} uses analogous evidence but has no applicability_reason. "
68
+ "Readers cannot judge when this comparison applies."
69
+ ),
70
+ recommendation=(
71
+ "Add applicability_reason explaining why this comparable case is relevant "
72
+ "and what dimensions are not comparable."
73
+ ),
74
+ ))
75
+
76
+
77
+ def _check_single_case_action(
78
+ block: AnalysisBlock,
79
+ ledger: ClaimLedger,
80
+ findings: list[CaseApplicabilityFinding],
81
+ ) -> None:
82
+ """Rule 2: single comparable case cannot support strong action."""
83
+ # Check two scenarios:
84
+ # A) action_claim_ids contain analogous evidence (direct action on analogy)
85
+ # B) case_claim_ids contain epistemic_type=action (builder classified by evidence_relation first)
86
+ if not block.case_claim_ids:
87
+ return
88
+ if block.fact_claim_ids:
89
+ # There are direct facts, so analogous evidence is supplementary
90
+ return
91
+
92
+ # Scenario A: explicit action claims with analogous evidence
93
+ for action_cid in block.action_claim_ids:
94
+ action_claim = ledger.get_claim(action_cid)
95
+ if not action_claim:
96
+ continue
97
+ if action_claim.evidence_relation == "analogous":
98
+ findings.append(CaseApplicabilityFinding(
99
+ finding_type="analogous_evidence_supports_action",
100
+ severity="fail",
101
+ block_id=block.block_id,
102
+ claim_id=action_cid,
103
+ description=(
104
+ f"Action claim {action_cid} is supported only by comparable/analogous evidence "
105
+ "with no direct fact backing. Single comparable cases cannot support strong actions."
106
+ ),
107
+ recommendation=(
108
+ "Downgrade to 'to_verify' or add direct evidence. "
109
+ "Comparable cases can support hypothesis or trend judgment, not actionable recommendations."
110
+ ),
111
+ ))
112
+
113
+ # Scenario B: case claims that were originally action-type (builder put them in case due to analogous)
114
+ for case_cid in block.case_claim_ids:
115
+ case_claim = ledger.get_claim(case_cid)
116
+ if not case_claim:
117
+ continue
118
+ if case_claim.epistemic_type == "action":
119
+ findings.append(CaseApplicabilityFinding(
120
+ finding_type="analogous_evidence_supports_action",
121
+ severity="fail",
122
+ block_id=block.block_id,
123
+ claim_id=case_cid,
124
+ description=(
125
+ f"Claim {case_cid} has epistemic_type='action' but evidence_relation='analogous'. "
126
+ "It was classified as a case (correct), but an action claim based solely on "
127
+ "comparable evidence cannot support a strong recommendation."
128
+ ),
129
+ recommendation=(
130
+ "Downgrade epistemic_type to 'hypothesis' or 'interpreted', "
131
+ "or add direct local evidence to support the action."
132
+ ),
133
+ ))
134
+
135
+
136
+ def _check_verification_path(
137
+ block: AnalysisBlock,
138
+ findings: list[CaseApplicabilityFinding],
139
+ ) -> None:
140
+ """Rule 3: no local direct evidence → must have verification_path."""
141
+ has_direct_fact = bool(block.fact_claim_ids)
142
+ has_case = bool(block.case_claim_ids)
143
+ has_verification = bool(block.verification_path.strip())
144
+
145
+ if has_case and not has_direct_fact and not has_verification:
146
+ findings.append(CaseApplicabilityFinding(
147
+ finding_type="missing_verification_path",
148
+ severity="warning",
149
+ block_id=block.block_id,
150
+ claim_id="",
151
+ description=(
152
+ f"Block '{block.title}' uses comparable cases but has no direct local facts "
153
+ "and no verification_path. Readers don't know how to validate the comparison."
154
+ ),
155
+ recommendation=(
156
+ "Add verification_path describing what data or action would confirm "
157
+ "whether the comparable case applies locally."
158
+ ),
159
+ ))
160
+
161
+
162
+ def format_case_applicability_report(findings: list[CaseApplicabilityFinding]) -> str:
163
+ """Format findings into a readable report."""
164
+ if not findings:
165
+ return "Case applicability audit: all checks passed.\n"
166
+
167
+ lines = ["Case Applicability Audit Report", "=" * 34, ""]
168
+ for f in findings:
169
+ icon = "❌" if f.severity == "fail" else "⚠️"
170
+ lines.append(f"{icon} [{f.severity.upper()}] {f.finding_type}")
171
+ lines.append(f" Block: {f.block_id}")
172
+ if f.claim_id:
173
+ lines.append(f" Claim: {f.claim_id}")
174
+ lines.append(f" {f.description}")
175
+ lines.append(f" → {f.recommendation}")
176
+ lines.append("")
177
+
178
+ fails = sum(1 for f in findings if f.severity == "fail")
179
+ warns = sum(1 for f in findings if f.severity == "warning")
180
+ lines.append(f"Result: {fails} fail(s), {warns} warning(s)")
181
+ return "\n".join(lines)