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,686 @@
1
+ """Product-layer delivery/audit bundle projection.
2
+
3
+ This module classifies already-finalized workspace artifacts. It does not move
4
+ files, render templates, deliver reports, or approve publication.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import json
11
+ import re
12
+ import zipfile
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from multi_agent_brief.outputs.reader_final_gate import (
17
+ detect_reader_residue,
18
+ detect_reader_residue_in_docx,
19
+ )
20
+ from multi_agent_brief.outputs.finalize import (
21
+ interpret_finalize_audit_binding,
22
+ require_finalize_audit_binding_pass,
23
+ )
24
+ from multi_agent_brief.product.citation_profile import (
25
+ DEFAULT_CITATION_PROFILE,
26
+ citation_profile_report,
27
+ normalize_citation_profile,
28
+ validate_citation_profile_report,
29
+ )
30
+ from multi_agent_brief.product.quality_panel import (
31
+ QualityPanelError,
32
+ render_quality_panel_html,
33
+ render_quality_summary,
34
+ validate_quality_panel_html,
35
+ validate_quality_panel_payload,
36
+ validate_quality_summary_markdown,
37
+ )
38
+ from multi_agent_brief.product.report_spec import ReportSpecLoadError, load_report_spec
39
+ from multi_agent_brief.product.template_registry import ReportTemplateRegistry
40
+
41
+ REPORT_BUNDLE_MANIFEST_SCHEMA_VERSION = "briefloop.report_bundle_manifest.v1"
42
+ _ASCII_SAFE_RE = re.compile(r"[^A-Za-z0-9._-]+")
43
+ _JUNK_SUFFIXES = {".tmp", ".temp", ".swp", ".swo"}
44
+ _DELIVERY_BUNDLE_README_MEMBER = "delivery/_BUNDLE_README.md"
45
+ _AUDIT_BUNDLE_README_MEMBER = "audit/_BUNDLE_README.md"
46
+ _DELIVERY_BUNDLE_README = """# BriefLoop Delivery Bundle
47
+
48
+ Open the files in this bundle for the reader-facing report.
49
+
50
+ - `brief.md` is the local Markdown delivery when present.
51
+ - DOCX or other configured delivery files are reader-facing copies of the same finalized report surface.
52
+ - Audit/control artifacts are intentionally excluded from this bundle.
53
+
54
+ This bundle does not prove semantic truth, approve publication, or replace human review before sending.
55
+ For claim, source, gate, event, and quality traces, open the separate audit bundle.
56
+ """
57
+ _AUDIT_BUNDLE_README = """# BriefLoop Audit Bundle
58
+
59
+ Open this bundle when a reviewer asks where a claim, warning, gate result, or delivery decision came from.
60
+
61
+ Useful starting points when present:
62
+
63
+ - `output/intermediate/quality_summary.md` for a compact quality summary.
64
+ - `output/intermediate/quality_panel.html` for a static inspection panel.
65
+ - `output/intermediate/claim_ledger.json` for recorded claims.
66
+ - `output/source_appendix.md` and `output/source_appendix_trace.md` for source trail review.
67
+ - `output/intermediate/audit_report.json`, gate reports, workflow state, runtime manifest, and event log for control records.
68
+
69
+ This bundle is not reader delivery, semantic proof, delivery approval, or release authority.
70
+ Do not edit these control files in place to change a run outcome.
71
+ """
72
+
73
+
74
+ class ReportBundleProjectionError(Exception):
75
+ """Raised when a bundle projection cannot be built safely."""
76
+
77
+
78
+ def build_report_bundle_manifest(
79
+ *,
80
+ workspace: str | Path,
81
+ template_registry: ReportTemplateRegistry | None = None,
82
+ ) -> dict[str, Any]:
83
+ ws = Path(workspace).expanduser().resolve()
84
+ finalize_report = _load_finalize_report(ws)
85
+ hygiene: dict[str, Any] = {"status": "clean", "excluded_artifacts": []}
86
+ delivery_records = _delivery_records(ws, finalize_report, hygiene=hygiene)
87
+ audit_records = _audit_records(ws, finalize_report, hygiene=hygiene)
88
+ if hygiene["excluded_artifacts"]:
89
+ hygiene["status"] = "excluded_packaging_junk"
90
+ template = _template_projection(
91
+ ws,
92
+ template_registry=template_registry or ReportTemplateRegistry.from_package(),
93
+ )
94
+ citation_profile = _citation_profile_projection(finalize_report)
95
+ return {
96
+ "schema_version": REPORT_BUNDLE_MANIFEST_SCHEMA_VERSION,
97
+ "workspace": ".",
98
+ "source": "finalize_report_projection",
99
+ "semantics": "delivery_and_audit_bundle_projection_only",
100
+ "template": template,
101
+ "citation_profile": citation_profile,
102
+ "packaging_hygiene": hygiene,
103
+ "supplemental_guidance": {
104
+ "status": "available_when_archives_are_written",
105
+ "semantics": "supplemental_guidance_non_authoritative_not_counted_as_artifacts",
106
+ "artifact_count_policy": "excluded_from_delivery_bundle_and_audit_bundle_artifact_count",
107
+ "delivery_archive_member": _DELIVERY_BUNDLE_README_MEMBER,
108
+ "audit_archive_member": _AUDIT_BUNDLE_README_MEMBER,
109
+ },
110
+ "bundle_archives": {"status": "not_requested"},
111
+ "delivery_bundle": {
112
+ "status": "available",
113
+ "semantics": "reader_facing_artifacts_only",
114
+ "artifact_count": len(delivery_records),
115
+ "artifacts": delivery_records,
116
+ },
117
+ "audit_bundle": {
118
+ "status": "available",
119
+ "semantics": "audit_control_artifacts_only_not_reader_delivery",
120
+ "artifact_count": len(audit_records),
121
+ "artifacts": audit_records,
122
+ },
123
+ "non_goals": [
124
+ "delivery_approval",
125
+ "gate_bypass",
126
+ "publication_authorization",
127
+ "semantic_support_assessment",
128
+ ],
129
+ }
130
+
131
+
132
+ def write_report_bundle_manifest(
133
+ *,
134
+ workspace: str | Path,
135
+ output_path: str | Path | None = None,
136
+ template_registry: ReportTemplateRegistry | None = None,
137
+ write_archives: bool = False,
138
+ ) -> dict[str, Any]:
139
+ ws = Path(workspace).expanduser().resolve()
140
+ target = _manifest_output_path(ws, output_path)
141
+ _raise_if_reserved_archive_output(ws, target)
142
+ manifest = build_report_bundle_manifest(workspace=ws, template_registry=template_registry)
143
+ if write_archives:
144
+ manifest["bundle_archives"] = _write_bundle_archives(ws, manifest)
145
+ manifest["manifest_path"] = _workspace_relative(ws, target)
146
+ target.parent.mkdir(parents=True, exist_ok=True)
147
+ target.write_text(json.dumps(manifest, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")
148
+ return manifest
149
+
150
+
151
+ def _manifest_output_path(workspace: Path, output_path: str | Path | None) -> Path:
152
+ target = Path(output_path).expanduser() if output_path else workspace / "output" / "report_bundle_manifest.json"
153
+ if not target.is_absolute():
154
+ target = workspace / target
155
+ target = target.resolve()
156
+ try:
157
+ _workspace_relative(workspace, target)
158
+ except ValueError as exc:
159
+ raise ReportBundleProjectionError("bundle manifest output must stay inside the workspace.") from exc
160
+ return target
161
+
162
+
163
+ def _raise_if_reserved_archive_output(workspace: Path, target: Path) -> None:
164
+ reserved = {
165
+ (workspace / "output" / "delivery_bundle.zip").resolve(),
166
+ (workspace / "output" / "audit_bundle.zip").resolve(),
167
+ }
168
+ if target in reserved:
169
+ rel = _workspace_relative(workspace, target)
170
+ raise ReportBundleProjectionError(
171
+ f"bundle manifest output path is reserved for clean bundle archives: {rel}"
172
+ )
173
+
174
+
175
+ def _write_bundle_archives(workspace: Path, manifest: dict[str, Any]) -> dict[str, Any]:
176
+ output_dir = workspace / "output"
177
+ output_dir.mkdir(parents=True, exist_ok=True)
178
+ delivery_path = output_dir / "delivery_bundle.zip"
179
+ audit_path = output_dir / "audit_bundle.zip"
180
+ delivery_records = _records_from_bundle(manifest, "delivery_bundle")
181
+ audit_records = _records_from_bundle(manifest, "audit_bundle")
182
+ _write_zip_from_records(
183
+ workspace=workspace,
184
+ archive_path=delivery_path,
185
+ records=delivery_records,
186
+ surface="delivery",
187
+ )
188
+ _write_zip_from_records(
189
+ workspace=workspace,
190
+ archive_path=audit_path,
191
+ records=audit_records,
192
+ surface="audit",
193
+ )
194
+ return {
195
+ "status": "generated",
196
+ "semantics": "clean_archives_from_report_bundle_manifest",
197
+ "delivery": _archive_record(workspace, delivery_path, artifact_count=len(delivery_records)),
198
+ "audit": _archive_record(workspace, audit_path, artifact_count=len(audit_records)),
199
+ }
200
+
201
+
202
+ def _records_from_bundle(manifest: dict[str, Any], key: str) -> list[dict[str, Any]]:
203
+ bundle = manifest.get(key)
204
+ artifacts = bundle.get("artifacts") if isinstance(bundle, dict) else None
205
+ if not isinstance(artifacts, list):
206
+ return []
207
+ return [item for item in artifacts if isinstance(item, dict)]
208
+
209
+
210
+ def _write_zip_from_records(
211
+ *,
212
+ workspace: Path,
213
+ archive_path: Path,
214
+ records: list[dict[str, Any]],
215
+ surface: str,
216
+ ) -> None:
217
+ archive_path.parent.mkdir(parents=True, exist_ok=True)
218
+ with zipfile.ZipFile(archive_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
219
+ _write_bundle_readme(zf, surface=surface)
220
+ for record in sorted(records, key=lambda item: str(item.get("path") or "")):
221
+ rel = str(record.get("path") or "").strip()
222
+ if not rel:
223
+ continue
224
+ source = _resolve_workspace_path(workspace, rel)
225
+ arcname = _archive_member_name(rel, surface=surface)
226
+ info = zipfile.ZipInfo(arcname)
227
+ info.date_time = (1980, 1, 1, 0, 0, 0)
228
+ info.compress_type = zipfile.ZIP_DEFLATED
229
+ info.external_attr = 0o644 << 16
230
+ zf.writestr(info, source.read_bytes())
231
+
232
+
233
+ def _write_bundle_readme(zf: zipfile.ZipFile, *, surface: str) -> None:
234
+ if surface == "delivery":
235
+ arcname = _DELIVERY_BUNDLE_README_MEMBER
236
+ text = _DELIVERY_BUNDLE_README
237
+ elif surface == "audit":
238
+ arcname = _AUDIT_BUNDLE_README_MEMBER
239
+ text = _AUDIT_BUNDLE_README
240
+ else:
241
+ return
242
+ info = zipfile.ZipInfo(arcname)
243
+ info.date_time = (1980, 1, 1, 0, 0, 0)
244
+ info.compress_type = zipfile.ZIP_DEFLATED
245
+ info.external_attr = 0o644 << 16
246
+ zf.writestr(info, text)
247
+
248
+
249
+ def _archive_member_name(rel_path: str, *, surface: str) -> str:
250
+ rel = Path(rel_path).as_posix()
251
+ if surface == "delivery" and rel.startswith("output/delivery/"):
252
+ rel = rel.removeprefix("output/delivery/")
253
+ return f"{surface}/{rel}".replace("//", "/")
254
+
255
+
256
+ def _archive_record(workspace: Path, path: Path, *, artifact_count: int) -> dict[str, Any]:
257
+ return {
258
+ "path": _workspace_relative(workspace, path),
259
+ "sha256": _sha256_file(path),
260
+ "size_bytes": path.stat().st_size,
261
+ "artifact_count": artifact_count,
262
+ }
263
+
264
+
265
+ def _load_finalize_report(workspace: Path) -> dict[str, Any]:
266
+ path = workspace / "output" / "intermediate" / "finalize_report.json"
267
+ if not path.exists():
268
+ raise ReportBundleProjectionError(
269
+ "finalize_report.json is required before building report bundles."
270
+ )
271
+ try:
272
+ payload = json.loads(path.read_text(encoding="utf-8"))
273
+ except (OSError, UnicodeDecodeError) as exc:
274
+ raise ReportBundleProjectionError(f"finalize_report.json is unreadable: {exc}") from exc
275
+ except json.JSONDecodeError as exc:
276
+ raise ReportBundleProjectionError(f"finalize_report.json is invalid JSON: {exc}") from exc
277
+ if not isinstance(payload, dict):
278
+ raise ReportBundleProjectionError("finalize_report.json must contain an object.")
279
+ if payload.get("status") != "pass":
280
+ raise ReportBundleProjectionError("finalize_report.json status must be pass.")
281
+ reader_clean = payload.get("reader_clean")
282
+ if not isinstance(reader_clean, dict) or reader_clean.get("status") != "pass":
283
+ raise ReportBundleProjectionError("finalize_report.json reader_clean.status must be pass.")
284
+ audit_binding_reasons = require_finalize_audit_binding_pass(
285
+ interpret_finalize_audit_binding(
286
+ workspace=workspace,
287
+ finalize_report=payload,
288
+ )
289
+ )
290
+ if audit_binding_reasons:
291
+ raise ReportBundleProjectionError(
292
+ "finalize_report.json audit_binding must pass before building report bundles: "
293
+ + "; ".join(audit_binding_reasons)
294
+ )
295
+ return payload
296
+
297
+
298
+ def _delivery_records(
299
+ workspace: Path,
300
+ finalize_report: dict[str, Any],
301
+ *,
302
+ hygiene: dict[str, Any],
303
+ ) -> list[dict[str, Any]]:
304
+ raw_artifacts = finalize_report.get("delivery_artifacts")
305
+ if not isinstance(raw_artifacts, list) or not raw_artifacts:
306
+ raise ReportBundleProjectionError("finalize_report.json delivery_artifacts must be non-empty.")
307
+ raw_hashes = finalize_report.get("delivery_artifact_sha256")
308
+ if not isinstance(raw_hashes, dict) or not raw_hashes:
309
+ raise ReportBundleProjectionError(
310
+ "finalize_report.json delivery_artifact_sha256 must be a non-empty object."
311
+ )
312
+ hashes = raw_hashes
313
+ records: list[dict[str, Any]] = []
314
+ delivery_root = (workspace / "output" / "delivery").resolve()
315
+ for raw in raw_artifacts:
316
+ if not isinstance(raw, str) or not raw.strip():
317
+ raise ReportBundleProjectionError("finalize_report.json contains an invalid delivery artifact path.")
318
+ path = _resolve_workspace_path(workspace, raw)
319
+ try:
320
+ path.relative_to(delivery_root)
321
+ except ValueError as exc:
322
+ raise ReportBundleProjectionError(
323
+ "delivery artifacts must be under output/delivery/."
324
+ ) from exc
325
+ if _is_packaging_junk(path):
326
+ _record_hygiene_exclusion(workspace, path, hygiene=hygiene, surface="delivery")
327
+ continue
328
+ expected_sha = _hash_for_path(hashes, raw=raw, workspace=workspace, path=path)
329
+ if not expected_sha:
330
+ raise ReportBundleProjectionError(
331
+ f"delivery artifact hash missing: {_workspace_relative(workspace, path)}"
332
+ )
333
+ actual_sha = _sha256_file(path)
334
+ if expected_sha != actual_sha:
335
+ raise ReportBundleProjectionError(
336
+ f"delivery artifact hash mismatch: {_workspace_relative(workspace, path)}"
337
+ )
338
+ _validate_reader_delivery_artifact(workspace, path)
339
+ records.append(_artifact_record(workspace, path, role="reader_delivery"))
340
+ if not records:
341
+ raise ReportBundleProjectionError(
342
+ "finalize_report.json delivery_artifacts did not include packageable reader artifacts."
343
+ )
344
+ return records
345
+
346
+
347
+ def _validate_reader_delivery_artifact(workspace: Path, path: Path) -> None:
348
+ rel = _workspace_relative(workspace, path)
349
+ suffix = path.suffix.lower()
350
+ if suffix == ".docx":
351
+ result = detect_reader_residue_in_docx(path, artifact=rel)
352
+ else:
353
+ try:
354
+ text = path.read_text(encoding="utf-8")
355
+ except (OSError, UnicodeDecodeError) as exc:
356
+ raise ReportBundleProjectionError(f"reader delivery artifact is unreadable: {rel}: {exc}") from exc
357
+ result = detect_reader_residue(text, artifact=rel)
358
+ if result.status != "pass":
359
+ finding_kinds = sorted({finding.kind for finding in result.findings})
360
+ detail = ", ".join(finding_kinds) or "reader_residue"
361
+ raise ReportBundleProjectionError(
362
+ f"reader delivery artifact failed reader-clean residue scan: {rel}: {detail}"
363
+ )
364
+
365
+
366
+ def _audit_records(
367
+ workspace: Path,
368
+ finalize_report: dict[str, Any],
369
+ *,
370
+ hygiene: dict[str, Any],
371
+ ) -> list[dict[str, Any]]:
372
+ _validate_present_quality_artifacts(workspace)
373
+ candidates = [
374
+ ("finalize_report", workspace / "output" / "intermediate" / "finalize_report.json"),
375
+ ("claim_ledger", workspace / "output" / "intermediate" / "claim_ledger.json"),
376
+ ("audited_brief", workspace / "output" / "intermediate" / "audited_brief.md"),
377
+ ("audit_report", workspace / "output" / "intermediate" / "audit_report.json"),
378
+ ("artifact_registry", workspace / "output" / "intermediate" / "artifact_registry.json"),
379
+ ("runtime_manifest", workspace / "output" / "intermediate" / "runtime_manifest.json"),
380
+ ("workflow_state", workspace / "output" / "intermediate" / "workflow_state.json"),
381
+ ("event_log", workspace / "output" / "intermediate" / "event_log.jsonl"),
382
+ ("auditor_gate_report", workspace / "output" / "intermediate" / "gates" / "auditor_quality_gate_report.json"),
383
+ (
384
+ "finalize_gate_report",
385
+ workspace / "output" / "intermediate" / "gates" / "finalize_quality_gate_report.json",
386
+ ),
387
+ ("source_appendix", workspace / "output" / "source_appendix.md"),
388
+ ("source_appendix_trace", _optional_report_path(workspace, finalize_report, "source_appendix_trace")),
389
+ ("atomic_claim_graph", workspace / "output" / "intermediate" / "atomic_claim_graph.json"),
390
+ ("evidence_span_registry", workspace / "output" / "intermediate" / "evidence_span_registry.json"),
391
+ ("claim_support_matrix", workspace / "output" / "intermediate" / "claim_support_matrix.json"),
392
+ ("semantic_assessment_report", workspace / "output" / "intermediate" / "semantic_assessment_report.json"),
393
+ ("quality_panel", workspace / "output" / "intermediate" / "quality_panel.json"),
394
+ ("quality_summary", workspace / "output" / "intermediate" / "quality_summary.md"),
395
+ ("quality_panel_html", workspace / "output" / "intermediate" / "quality_panel.html"),
396
+ ]
397
+ records: list[dict[str, Any]] = []
398
+ seen: set[str] = set()
399
+ delivery_root = (workspace / "output" / "delivery").resolve()
400
+ for role, path in candidates:
401
+ if path is None or not path.exists() or not path.is_file():
402
+ continue
403
+ resolved = path.resolve()
404
+ try:
405
+ resolved.relative_to(delivery_root)
406
+ continue
407
+ except ValueError:
408
+ pass
409
+ rel = _workspace_relative(workspace, resolved)
410
+ if rel in seen:
411
+ continue
412
+ if _is_packaging_junk(resolved):
413
+ _record_hygiene_exclusion(workspace, resolved, hygiene=hygiene, surface="audit")
414
+ continue
415
+ seen.add(rel)
416
+ records.append(_artifact_record(workspace, resolved, role=role))
417
+ return records
418
+
419
+
420
+ def _validate_present_quality_artifacts(workspace: Path) -> None:
421
+ quality_paths = {
422
+ "quality_panel": workspace / "output" / "intermediate" / "quality_panel.json",
423
+ "quality_summary": workspace / "output" / "intermediate" / "quality_summary.md",
424
+ "quality_panel_html": workspace / "output" / "intermediate" / "quality_panel.html",
425
+ }
426
+ if not any(path.exists() for path in quality_paths.values()):
427
+ return
428
+
429
+ panel_path = quality_paths["quality_panel"]
430
+ panel_payload = _load_valid_quality_panel_payload(workspace, panel_path)
431
+ if quality_paths["quality_summary"].exists():
432
+ _validate_quality_summary_binding(workspace, quality_paths["quality_summary"], panel_path, panel_payload)
433
+ if quality_paths["quality_panel_html"].exists():
434
+ _validate_quality_panel_html_binding(workspace, quality_paths["quality_panel_html"], panel_path, panel_payload)
435
+
436
+
437
+ def _load_valid_quality_panel_payload(workspace: Path, panel_path: Path) -> dict[str, Any]:
438
+ if not panel_path.exists():
439
+ _raise_quality_artifact_error(
440
+ workspace,
441
+ panel_path,
442
+ "quality_panel_missing",
443
+ )
444
+ try:
445
+ payload = json.loads(panel_path.read_text(encoding="utf-8"))
446
+ except (OSError, UnicodeDecodeError):
447
+ _raise_quality_artifact_error(workspace, panel_path, "quality_panel_unreadable")
448
+ except json.JSONDecodeError:
449
+ _raise_quality_artifact_error(workspace, panel_path, "quality_panel_parse_error")
450
+ if not isinstance(payload, dict):
451
+ _raise_quality_artifact_error(workspace, panel_path, "quality_panel_invalid:not_object")
452
+ reason = validate_quality_panel_payload(payload)
453
+ if reason:
454
+ _raise_quality_artifact_error(workspace, panel_path, f"quality_panel_invalid:{reason}")
455
+ return payload
456
+
457
+
458
+ def _validate_quality_summary_binding(
459
+ workspace: Path,
460
+ summary_path: Path,
461
+ panel_path: Path,
462
+ panel_payload: dict[str, Any],
463
+ ) -> None:
464
+ try:
465
+ text = summary_path.read_text(encoding="utf-8")
466
+ except (OSError, UnicodeDecodeError):
467
+ _raise_quality_artifact_error(workspace, summary_path, "quality_summary_unreadable")
468
+ reason = validate_quality_summary_markdown(text)
469
+ if reason:
470
+ _raise_quality_artifact_error(workspace, summary_path, reason)
471
+ try:
472
+ expected = render_quality_summary(panel_payload, quality_panel_sha256=_sha256_file(panel_path))
473
+ except QualityPanelError as exc:
474
+ _raise_quality_artifact_error(workspace, summary_path, f"quality_summary_render:{exc}")
475
+ if text != expected:
476
+ _raise_quality_artifact_error(workspace, summary_path, "quality_summary_stale_or_hand_edited")
477
+
478
+
479
+ def _validate_quality_panel_html_binding(
480
+ workspace: Path,
481
+ html_path: Path,
482
+ panel_path: Path,
483
+ panel_payload: dict[str, Any],
484
+ ) -> None:
485
+ try:
486
+ text = html_path.read_text(encoding="utf-8")
487
+ except (OSError, UnicodeDecodeError):
488
+ _raise_quality_artifact_error(workspace, html_path, "quality_panel_html_unreadable")
489
+ reason = validate_quality_panel_html(text)
490
+ if reason:
491
+ _raise_quality_artifact_error(workspace, html_path, reason)
492
+ try:
493
+ expected = render_quality_panel_html(panel_payload, quality_panel_sha256=_sha256_file(panel_path))
494
+ except QualityPanelError as exc:
495
+ _raise_quality_artifact_error(workspace, html_path, f"quality_panel_html_render:{exc}")
496
+ if text != expected:
497
+ _raise_quality_artifact_error(workspace, html_path, "quality_panel_html_stale_or_hand_edited")
498
+
499
+
500
+ def _raise_quality_artifact_error(workspace: Path, path: Path, reason: str) -> None:
501
+ try:
502
+ rel = _workspace_relative(workspace, path)
503
+ except ValueError:
504
+ rel = path.as_posix()
505
+ raise ReportBundleProjectionError(
506
+ f"quality projection artifact invalid: {rel}: {reason}; rerun briefloop quality summarize"
507
+ )
508
+
509
+
510
+ def _template_projection(
511
+ workspace: Path,
512
+ *,
513
+ template_registry: ReportTemplateRegistry,
514
+ ) -> dict[str, Any]:
515
+ spec_path = workspace / "report_spec.yaml"
516
+ if not spec_path.exists():
517
+ return {"status": "not_available", "reason": "report_spec_missing"}
518
+ try:
519
+ spec = load_report_spec(spec_path)
520
+ except (OSError, ReportSpecLoadError) as exc:
521
+ return {"status": "invalid_report_spec", "reason": str(exc)}
522
+ report_type = str(spec.get("report_type") or "").strip()
523
+ template = template_registry.get_by_report_type(report_type)
524
+ if template is None:
525
+ return {"status": "not_available", "report_type": report_type, "reason": "template_missing"}
526
+ return {
527
+ "status": "available",
528
+ "template_id": template.template_id,
529
+ "report_type": template.report_type,
530
+ "section_order": list(template.section_order),
531
+ "semantics": "stable_section_order_only_not_renderer",
532
+ }
533
+
534
+
535
+ def _citation_profile_projection(finalize_report: dict[str, Any]) -> dict[str, Any]:
536
+ if "citation_profile" not in finalize_report:
537
+ report = citation_profile_report(
538
+ profile=DEFAULT_CITATION_PROFILE,
539
+ source="legacy_finalize_report_default",
540
+ )
541
+ report["status"] = "legacy_default"
542
+ report["semantics"] = "reader_delivery_citation_projection_and_audit_trace_split"
543
+ return report
544
+
545
+ raw_profile = finalize_report.get("citation_profile")
546
+ profile = normalize_citation_profile(raw_profile)
547
+ if not profile:
548
+ raise ReportBundleProjectionError("finalize_report citation profile invalid: citation_profile")
549
+ report = citation_profile_report(
550
+ profile=profile,
551
+ source=str(finalize_report.get("citation_profile_source") or "finalize_report"),
552
+ warnings=[
553
+ str(item)
554
+ for item in finalize_report.get("citation_profile_warnings", [])
555
+ if isinstance(item, str)
556
+ ],
557
+ )
558
+ for source_field, target_field in (
559
+ ("citation_profile_runtime_effect", "runtime_effect"),
560
+ ("citation_profile_reader_citation_style", "reader_citation_style"),
561
+ ("citation_profile_reader_metadata_level", "reader_metadata_level"),
562
+ ("citation_profile_audit_trace_level", "audit_trace_level"),
563
+ ):
564
+ value = finalize_report.get(source_field)
565
+ if value is not None and str(value).strip() != str(report.get(target_field) or ""):
566
+ raise ReportBundleProjectionError(
567
+ f"finalize_report citation profile invalid: {source_field}"
568
+ )
569
+ for source_field, target_field in (
570
+ ("citation_profile_delivery_exposes_internal_ids", "delivery_exposes_internal_ids"),
571
+ ("citation_profile_delivery_exposes_local_paths", "delivery_exposes_local_paths"),
572
+ ("citation_profile_audit_bundle_keeps_trace", "audit_bundle_keeps_trace"),
573
+ ):
574
+ if source_field in finalize_report and finalize_report[source_field] is not report[target_field]:
575
+ raise ReportBundleProjectionError(
576
+ f"finalize_report citation profile invalid: {source_field}"
577
+ )
578
+ reason = validate_citation_profile_report(report)
579
+ if reason:
580
+ raise ReportBundleProjectionError(f"finalize_report citation profile invalid: {reason}")
581
+ report["status"] = "available"
582
+ report["semantics"] = "reader_delivery_citation_projection_and_audit_trace_split"
583
+ return report
584
+
585
+
586
+ def _optional_report_path(workspace: Path, report: dict[str, Any], field: str) -> Path | None:
587
+ raw = report.get(field)
588
+ if not isinstance(raw, str) or not raw.strip():
589
+ return None
590
+ return _resolve_workspace_path(workspace, raw)
591
+
592
+
593
+ def _resolve_workspace_path(workspace: Path, raw: str) -> Path:
594
+ path = Path(raw).expanduser()
595
+ resolved = path.resolve() if path.is_absolute() else (workspace / path).resolve()
596
+ try:
597
+ resolved.relative_to(workspace)
598
+ except ValueError as exc:
599
+ raise ReportBundleProjectionError(f"artifact path escapes workspace: {raw}") from exc
600
+ if not resolved.exists() or not resolved.is_file():
601
+ raise ReportBundleProjectionError(f"artifact path is missing: {raw}")
602
+ return resolved
603
+
604
+
605
+ def _hash_for_path(
606
+ hashes: dict[str, Any],
607
+ *,
608
+ raw: str,
609
+ workspace: Path,
610
+ path: Path,
611
+ ) -> str:
612
+ rel = _workspace_relative(workspace, path)
613
+ for key in (raw, rel, path.as_posix(), str(path)):
614
+ value = hashes.get(key)
615
+ if isinstance(value, str) and value.strip():
616
+ return value.strip()
617
+ return ""
618
+
619
+
620
+ def _artifact_record(workspace: Path, path: Path, *, role: str) -> dict[str, Any]:
621
+ record = {
622
+ "path": _workspace_relative(workspace, path),
623
+ "role": role,
624
+ "sha256": _sha256_file(path),
625
+ "size_bytes": path.stat().st_size,
626
+ }
627
+ fallback = _ascii_fallback_name(path.name)
628
+ if fallback != path.name:
629
+ record["ascii_fallback_name"] = fallback
630
+ return record
631
+
632
+
633
+ def _is_packaging_junk(path: Path) -> bool:
634
+ parts = set(path.parts)
635
+ name = path.name
636
+ lower = name.lower()
637
+ return (
638
+ "__MACOSX" in parts
639
+ or name == ".DS_Store"
640
+ or name.startswith("~$")
641
+ or name.startswith(".~lock.")
642
+ or name.endswith("~")
643
+ or name.endswith("#")
644
+ or lower in {"thumbs.db", "desktop.ini"}
645
+ or lower.endswith(tuple(_JUNK_SUFFIXES))
646
+ )
647
+
648
+
649
+ def _record_hygiene_exclusion(
650
+ workspace: Path,
651
+ path: Path,
652
+ *,
653
+ hygiene: dict[str, Any],
654
+ surface: str,
655
+ ) -> None:
656
+ exclusions = hygiene.setdefault("excluded_artifacts", [])
657
+ exclusions.append({
658
+ "path": _workspace_relative(workspace, path),
659
+ "surface": surface,
660
+ "reason": "packaging_junk",
661
+ })
662
+
663
+
664
+ def _ascii_fallback_name(filename: str) -> str:
665
+ path = Path(filename)
666
+ suffix = path.suffix
667
+ raw_stem = path.stem or filename
668
+ encoded_stem = raw_stem.encode("ascii", "ignore").decode("ascii")
669
+ fallback_stem = _ASCII_SAFE_RE.sub("-", encoded_stem).strip(".-")
670
+ safe_suffix = suffix if suffix and suffix.encode("ascii", "ignore").decode("ascii") == suffix else ""
671
+ digest = hashlib.sha256(filename.encode("utf-8")).hexdigest()[:12]
672
+ if fallback_stem:
673
+ return f"{fallback_stem}-{digest}{safe_suffix}"
674
+ return f"artifact-{digest}{safe_suffix}"
675
+
676
+
677
+ def _workspace_relative(workspace: Path, path: Path) -> str:
678
+ return path.resolve().relative_to(workspace).as_posix()
679
+
680
+
681
+ def _sha256_file(path: Path) -> str:
682
+ h = hashlib.sha256()
683
+ with path.open("rb") as f:
684
+ for chunk in iter(lambda: f.read(1024 * 1024), b""):
685
+ h.update(chunk)
686
+ return h.hexdigest()