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.
- briefloop-0.11.12.dist-info/METADATA +473 -0
- briefloop-0.11.12.dist-info/RECORD +424 -0
- briefloop-0.11.12.dist-info/WHEEL +5 -0
- briefloop-0.11.12.dist-info/entry_points.txt +3 -0
- briefloop-0.11.12.dist-info/licenses/LICENSE +22 -0
- briefloop-0.11.12.dist-info/top_level.txt +1 -0
- multi_agent_brief/__init__.py +36 -0
- multi_agent_brief/analysis_blocks/__init__.py +11 -0
- multi_agent_brief/analysis_blocks/builder.py +206 -0
- multi_agent_brief/analysis_blocks/renderer.py +237 -0
- multi_agent_brief/analysis_blocks/schemas.py +52 -0
- multi_agent_brief/analysis_modules/__init__.py +8 -0
- multi_agent_brief/analysis_modules/base.py +87 -0
- multi_agent_brief/analysis_modules/market_competitor/__init__.py +68 -0
- multi_agent_brief/analysis_modules/market_competitor/auditor.py +290 -0
- multi_agent_brief/analysis_modules/market_competitor/config.py +187 -0
- multi_agent_brief/analysis_modules/market_competitor/event_builder.py +226 -0
- multi_agent_brief/analysis_modules/market_competitor/renderer.py +199 -0
- multi_agent_brief/analysis_modules/market_competitor/schemas.py +518 -0
- multi_agent_brief/analysis_modules/policy_regulatory/__init__.py +7 -0
- multi_agent_brief/analysis_modules/policy_regulatory/audit.py +195 -0
- multi_agent_brief/analysis_modules/policy_regulatory/module.py +387 -0
- multi_agent_brief/analysis_modules/policy_regulatory/schemas.py +129 -0
- multi_agent_brief/analysis_modules/registry.py +73 -0
- multi_agent_brief/audience/__init__.py +19 -0
- multi_agent_brief/audience/profiles.py +246 -0
- multi_agent_brief/audience_memory/__init__.py +23 -0
- multi_agent_brief/audience_memory/profile.py +358 -0
- multi_agent_brief/audit/__init__.py +36 -0
- multi_agent_brief/audit/case_applicability.py +181 -0
- multi_agent_brief/audit/deterministic.py +471 -0
- multi_agent_brief/audit/editorial_governance.py +404 -0
- multi_agent_brief/audit/final_quality.py +764 -0
- multi_agent_brief/audit/harness.py +242 -0
- multi_agent_brief/audit/interfaces.py +96 -0
- multi_agent_brief/audit/limitation_hygiene.py +238 -0
- multi_agent_brief/audit/proposal_boundary.py +34 -0
- multi_agent_brief/audit/redaction.py +27 -0
- multi_agent_brief/audit/rule_packs.py +96 -0
- multi_agent_brief/audit/semantic.py +435 -0
- multi_agent_brief/capabilities/__init__.py +22 -0
- multi_agent_brief/capabilities/catalog.py +215 -0
- multi_agent_brief/capabilities/detect.py +199 -0
- multi_agent_brief/capabilities/models.py +60 -0
- multi_agent_brief/capabilities/recommend.py +206 -0
- multi_agent_brief/cli/__init__.py +2 -0
- multi_agent_brief/cli/analysis_commands.py +140 -0
- multi_agent_brief/cli/approval_commands.py +107 -0
- multi_agent_brief/cli/audit_commands.py +59 -0
- multi_agent_brief/cli/capability_commands.py +548 -0
- multi_agent_brief/cli/claude_commands.py +159 -0
- multi_agent_brief/cli/competitors_commands.py +182 -0
- multi_agent_brief/cli/controls_commands.py +137 -0
- multi_agent_brief/cli/deliver_commands.py +942 -0
- multi_agent_brief/cli/eval_cases_commands.py +99 -0
- multi_agent_brief/cli/experiments_commands.py +395 -0
- multi_agent_brief/cli/feedback_commands.py +192 -0
- multi_agent_brief/cli/finalize_commands.py +153 -0
- multi_agent_brief/cli/gates_commands.py +174 -0
- multi_agent_brief/cli/hermes_commands.py +356 -0
- multi_agent_brief/cli/improve_commands.py +224 -0
- multi_agent_brief/cli/init_commands.py +564 -0
- multi_agent_brief/cli/init_wizard.py +1473 -0
- multi_agent_brief/cli/input_commands.py +237 -0
- multi_agent_brief/cli/main.py +288 -0
- multi_agent_brief/cli/onboard_commands.py +193 -0
- multi_agent_brief/cli/product_commands.py +1449 -0
- multi_agent_brief/cli/provenance_commands.py +100 -0
- multi_agent_brief/cli/release_commands.py +68 -0
- multi_agent_brief/cli/repair_commands.py +169 -0
- multi_agent_brief/cli/run_commands.py +292 -0
- multi_agent_brief/cli/runtime_commands.py +81 -0
- multi_agent_brief/cli/secrets_commands.py +232 -0
- multi_agent_brief/cli/semantic_support_commands.py +80 -0
- multi_agent_brief/cli/sources_commands.py +647 -0
- multi_agent_brief/cli/start_commands.py +8 -0
- multi_agent_brief/cli/state_commands.py +446 -0
- multi_agent_brief/cli/status_commands.py +26 -0
- multi_agent_brief/cli/workbuddy_commands.py +103 -0
- multi_agent_brief/configs/artifact_contracts.yaml +514 -0
- multi_agent_brief/configs/orchestrator_contract.yaml +181 -0
- multi_agent_brief/configs/policy_packs/default.yaml +65 -0
- multi_agent_brief/configs/policy_profiles/evidence_extract_default.yaml +58 -0
- multi_agent_brief/configs/policy_profiles/finance_default.yaml +44 -0
- multi_agent_brief/configs/policy_profiles/internet_default.yaml +44 -0
- multi_agent_brief/configs/policy_profiles/manufacturing_default.yaml +33 -0
- multi_agent_brief/configs/policy_profiles/solar_manufacturing_default.yaml +64 -0
- multi_agent_brief/configs/report_packs/evidence_extract.yaml +48 -0
- multi_agent_brief/configs/report_packs/management_monthly.yaml +36 -0
- multi_agent_brief/configs/report_packs/market_weekly.yaml +36 -0
- multi_agent_brief/configs/report_packs/solar_industry_periodic.yaml +50 -0
- multi_agent_brief/configs/report_templates/evidence_extract.yaml +55 -0
- multi_agent_brief/configs/report_templates/management_monthly.yaml +48 -0
- multi_agent_brief/configs/report_templates/market_weekly.yaml +49 -0
- multi_agent_brief/configs/report_templates/solar_industry_periodic.yaml +75 -0
- multi_agent_brief/configs/stage_specs.yaml +180 -0
- multi_agent_brief/contracts/__init__.py +41 -0
- multi_agent_brief/contracts/base.py +99 -0
- multi_agent_brief/contracts/errors.py +41 -0
- multi_agent_brief/contracts/migrations/__init__.py +5 -0
- multi_agent_brief/contracts/migrations/claim_v1_to_v2.py +33 -0
- multi_agent_brief/contracts/registry.py +159 -0
- multi_agent_brief/contracts/role_topology.py +25 -0
- multi_agent_brief/contracts/schemas/__init__.py +35 -0
- multi_agent_brief/contracts/schemas/analysis_pack.py +135 -0
- multi_agent_brief/contracts/schemas/atomic_claim_graph.py +304 -0
- multi_agent_brief/contracts/schemas/audit_report.py +80 -0
- multi_agent_brief/contracts/schemas/candidate_item.py +51 -0
- multi_agent_brief/contracts/schemas/claim.py +197 -0
- multi_agent_brief/contracts/schemas/claim_draft.py +386 -0
- multi_agent_brief/contracts/schemas/claim_support_matrix.py +334 -0
- multi_agent_brief/contracts/schemas/evidence_span_registry.py +270 -0
- multi_agent_brief/contracts/schemas/policy_profile.py +247 -0
- multi_agent_brief/contracts/schemas/report_spec.py +257 -0
- multi_agent_brief/contracts/schemas/semantic_assessment_report.py +480 -0
- multi_agent_brief/contracts/schemas/source_evidence_pack_manifest.py +233 -0
- multi_agent_brief/contracts/schemas/source_item.py +96 -0
- multi_agent_brief/contracts/semantic_assessment_status.py +12 -0
- multi_agent_brief/contracts/source_metadata.py +252 -0
- multi_agent_brief/contracts/target_contract.py +460 -0
- multi_agent_brief/contracts/validator.py +225 -0
- multi_agent_brief/controls/__init__.py +23 -0
- multi_agent_brief/controls/contract.py +106 -0
- multi_agent_brief/controls/switchboard.py +1076 -0
- multi_agent_brief/core/__init__.py +2 -0
- multi_agent_brief/core/citations.py +14 -0
- multi_agent_brief/core/claim_ledger.py +94 -0
- multi_agent_brief/core/config.py +191 -0
- multi_agent_brief/core/env.py +69 -0
- multi_agent_brief/core/previous.py +83 -0
- multi_agent_brief/core/schemas.py +181 -0
- multi_agent_brief/core/selection.py +189 -0
- multi_agent_brief/delivery/__init__.py +6 -0
- multi_agent_brief/delivery/base.py +36 -0
- multi_agent_brief/delivery/feishu.py +204 -0
- multi_agent_brief/delivery/gws.py +237 -0
- multi_agent_brief/evaluation_cases/__init__.py +15 -0
- multi_agent_brief/evaluation_cases/contract.py +429 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/improvement/ledger.jsonl +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/config.yaml +12 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/sources.yaml +6 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/config.yaml +6 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/input/human_feedback.md +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/brief.md +14 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audited_brief.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/claim_ledger.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/config.yaml +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/user.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/config.yaml +6 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/claim_ledger.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/feedback_issues.json +25 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/repair_plan.json +27 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/claim_ledger.json +11 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/quality_gate_report.json +22 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audited_brief.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/claim_ledger.json +40 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/sources.yaml +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/claim_ledger.json +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/output/intermediate/release_readiness_report.json +31 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/ledger.jsonl +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/memory.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/output/intermediate/improvement_memory_snapshot.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/config.yaml +12 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/input/sources/source-001.txt +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/brief.md +37 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/delivery/brief.md +37 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/atomic_claim_graph.json +17 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audit_report.json +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audited_brief.md +37 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/claim_ledger.json +20 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/evidence_span_registry.json +20 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/finalize_report.json +40 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/auditor_quality_gate_report.json +42 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/finalize_quality_gate_report.json +42 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/screened_candidates.json +33 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix.md +12 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix_trace.md +10 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/report_spec.yaml +24 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/sources.yaml +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/input/sources/README.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/output/intermediate/source_evidence_pack_manifest.json +26 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/config.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/claim_ledger.json +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/config.yaml +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/improvement/ledger.jsonl +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/user.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audited_brief.md +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/claim_ledger.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/manifest.yaml +861 -0
- multi_agent_brief/evaluation_cases/fixtures.py +20 -0
- multi_agent_brief/evaluation_cases/runner.py +1603 -0
- multi_agent_brief/experiments/__init__.py +45 -0
- multi_agent_brief/experiments/experiment_080.py +5835 -0
- multi_agent_brief/experiments/schemas.py +63 -0
- multi_agent_brief/experiments/target_contract.py +10 -0
- multi_agent_brief/feedback/__init__.py +17 -0
- multi_agent_brief/feedback/feedback_contract.py +482 -0
- multi_agent_brief/feedback/feedback_state.py +898 -0
- multi_agent_brief/hermes/__init__.py +25 -0
- multi_agent_brief/hermes/adapter.py +832 -0
- multi_agent_brief/improvement/__init__.py +83 -0
- multi_agent_brief/improvement/contract.py +1114 -0
- multi_agent_brief/improvement/memory.py +451 -0
- multi_agent_brief/improvement/product_definition.py +228 -0
- multi_agent_brief/improvement/state.py +961 -0
- multi_agent_brief/inputs/__init__.py +2 -0
- multi_agent_brief/inputs/classifier.py +322 -0
- multi_agent_brief/inputs/contracts.py +59 -0
- multi_agent_brief/inputs/extractor.py +310 -0
- multi_agent_brief/install/__init__.py +2 -0
- multi_agent_brief/install/writer.py +97 -0
- multi_agent_brief/onboarding/__init__.py +11 -0
- multi_agent_brief/onboarding/io.py +103 -0
- multi_agent_brief/onboarding/mapper.py +484 -0
- multi_agent_brief/onboarding/schema.py +46 -0
- multi_agent_brief/orchestrator/__init__.py +2 -0
- multi_agent_brief/orchestrator/fact_layer_import.py +352 -0
- multi_agent_brief/orchestrator/handoff.py +2209 -0
- multi_agent_brief/orchestrator/role_topology.py +42 -0
- multi_agent_brief/orchestrator/run_archive.py +888 -0
- multi_agent_brief/orchestrator/run_integrity.py +346 -0
- multi_agent_brief/orchestrator/runtime_state/__init__.py +70 -0
- multi_agent_brief/orchestrator/runtime_state/_io.py +167 -0
- multi_agent_brief/orchestrator/runtime_state/_transactions.py +298 -0
- multi_agent_brief/orchestrator/runtime_state/artifact_registry.py +1825 -0
- multi_agent_brief/orchestrator/runtime_state/atomic_claim_graph.py +96 -0
- multi_agent_brief/orchestrator/runtime_state/claim_ledger_freeze.py +510 -0
- multi_agent_brief/orchestrator/runtime_state/claim_metadata_enrichment.py +836 -0
- multi_agent_brief/orchestrator/runtime_state/claim_support_matrix.py +582 -0
- multi_agent_brief/orchestrator/runtime_state/completion_gates.py +672 -0
- multi_agent_brief/orchestrator/runtime_state/contracts_loader.py +66 -0
- multi_agent_brief/orchestrator/runtime_state/decisions.py +239 -0
- multi_agent_brief/orchestrator/runtime_state/errors.py +61 -0
- multi_agent_brief/orchestrator/runtime_state/event_log.py +256 -0
- multi_agent_brief/orchestrator/runtime_state/evidence_span_registry.py +233 -0
- multi_agent_brief/orchestrator/runtime_state/fact_layer.py +755 -0
- multi_agent_brief/orchestrator/runtime_state/identity.py +90 -0
- multi_agent_brief/orchestrator/runtime_state/lifecycle.py +675 -0
- multi_agent_brief/orchestrator/runtime_state/manifest.py +82 -0
- multi_agent_brief/orchestrator/runtime_state/operations.py +224 -0
- multi_agent_brief/orchestrator/runtime_state/paths.py +38 -0
- multi_agent_brief/orchestrator/runtime_state/repair.py +984 -0
- multi_agent_brief/orchestrator/runtime_state/safety_surfaces.py +147 -0
- multi_agent_brief/orchestrator/runtime_state/semantic_assessment_report.py +692 -0
- multi_agent_brief/orchestrator/runtime_state/semantic_support_acceptance.py +683 -0
- multi_agent_brief/orchestrator/runtime_state/source_evidence_pack.py +131 -0
- multi_agent_brief/orchestrator/runtime_state/stage_completion.py +1320 -0
- multi_agent_brief/orchestrator/runtime_state/trajectory.py +200 -0
- multi_agent_brief/orchestrator/runtime_state/workflow.py +350 -0
- multi_agent_brief/orchestrator/source_evidence.py +43 -0
- multi_agent_brief/orchestrator/timing.py +410 -0
- multi_agent_brief/orchestrator_contract.py +96 -0
- multi_agent_brief/outputs/__init__.py +2 -0
- multi_agent_brief/outputs/atomic_reader_projection.py +288 -0
- multi_agent_brief/outputs/docx.py +32 -0
- multi_agent_brief/outputs/finalize.py +1036 -0
- multi_agent_brief/outputs/ib_docx.py +983 -0
- multi_agent_brief/outputs/naming.py +38 -0
- multi_agent_brief/outputs/pdf.py +20 -0
- multi_agent_brief/outputs/reader_final_gate.py +509 -0
- multi_agent_brief/outputs/reader_projection.py +397 -0
- multi_agent_brief/outputs/source_appendix.py +753 -0
- multi_agent_brief/outputs/templates/__init__.py +112 -0
- multi_agent_brief/product/__init__.py +49 -0
- multi_agent_brief/product/bundle_projection.py +686 -0
- multi_agent_brief/product/citation_profile.py +145 -0
- multi_agent_brief/product/guidance_manifestation.py +421 -0
- multi_agent_brief/product/materiality_selection.py +472 -0
- multi_agent_brief/product/policy_gate_adapter.py +116 -0
- multi_agent_brief/product/policy_profile.py +39 -0
- multi_agent_brief/product/policy_projection.py +161 -0
- multi_agent_brief/product/policy_registry.py +83 -0
- multi_agent_brief/product/policy_resolver.py +225 -0
- multi_agent_brief/product/quality_closeout.py +188 -0
- multi_agent_brief/product/quality_panel.py +2217 -0
- multi_agent_brief/product/release_approval.py +1034 -0
- multi_agent_brief/product/report_pack.py +130 -0
- multi_agent_brief/product/report_pack_aliases.py +68 -0
- multi_agent_brief/product/report_registry.py +101 -0
- multi_agent_brief/product/report_spec.py +181 -0
- multi_agent_brief/product/support_wording.py +525 -0
- multi_agent_brief/product/template_conformance.py +516 -0
- multi_agent_brief/product/template_projection.py +123 -0
- multi_agent_brief/product/template_registry.py +244 -0
- multi_agent_brief/product/template_render_plan.py +315 -0
- multi_agent_brief/product/template_renderer.py +224 -0
- multi_agent_brief/product/trajectory_regulation.py +441 -0
- multi_agent_brief/provenance/__init__.py +5 -0
- multi_agent_brief/provenance/builder.py +841 -0
- multi_agent_brief/provenance/contract.py +21 -0
- multi_agent_brief/provenance/io.py +144 -0
- multi_agent_brief/provenance/model.py +110 -0
- multi_agent_brief/provenance/references.py +43 -0
- multi_agent_brief/provenance/validator.py +135 -0
- multi_agent_brief/quality_gates/__init__.py +19 -0
- multi_agent_brief/quality_gates/contract.py +581 -0
- multi_agent_brief/quality_gates/state.py +2498 -0
- multi_agent_brief/repair/__init__.py +5 -0
- multi_agent_brief/repair/router.py +842 -0
- multi_agent_brief/runtime_assets.py +431 -0
- multi_agent_brief/sources/__init__.py +16 -0
- multi_agent_brief/sources/api_filings.py +236 -0
- multi_agent_brief/sources/api_news.py +174 -0
- multi_agent_brief/sources/base.py +142 -0
- multi_agent_brief/sources/cached_package.py +119 -0
- multi_agent_brief/sources/cli_provider.py +263 -0
- multi_agent_brief/sources/coverage.py +336 -0
- multi_agent_brief/sources/decider.py +756 -0
- multi_agent_brief/sources/doctor.py +373 -0
- multi_agent_brief/sources/evidence_pack.py +364 -0
- multi_agent_brief/sources/feishu_provider.py +326 -0
- multi_agent_brief/sources/filing_resolver.py +400 -0
- multi_agent_brief/sources/industry_packs.py +130 -0
- multi_agent_brief/sources/join.py +206 -0
- multi_agent_brief/sources/local_signal.py +81 -0
- multi_agent_brief/sources/local_signal_planner.py +636 -0
- multi_agent_brief/sources/manual.py +259 -0
- multi_agent_brief/sources/mcp_provider.py +309 -0
- multi_agent_brief/sources/mineru_provider.py +601 -0
- multi_agent_brief/sources/normalizer.py +95 -0
- multi_agent_brief/sources/opencli_provider.py +282 -0
- multi_agent_brief/sources/planner.py +178 -0
- multi_agent_brief/sources/registry.py +367 -0
- multi_agent_brief/sources/rss.py +190 -0
- multi_agent_brief/sources/search_backends/__init__.py +31 -0
- multi_agent_brief/sources/search_backends/base.py +51 -0
- multi_agent_brief/sources/search_backends/brave.py +192 -0
- multi_agent_brief/sources/search_backends/capabilities.py +94 -0
- multi_agent_brief/sources/search_backends/exa.py +180 -0
- multi_agent_brief/sources/search_backends/firecrawl.py +164 -0
- multi_agent_brief/sources/search_backends/serper.py +241 -0
- multi_agent_brief/sources/search_backends/tavily.py +124 -0
- multi_agent_brief/sources/sourcehub.py +505 -0
- multi_agent_brief/sources/web_search.py +267 -0
- multi_agent_brief/status.py +966 -0
- multi_agent_brief/tools/__init__.py +3 -0
- multi_agent_brief/tools/draft_cleanup.py +198 -0
- multi_agent_brief/workbuddy/__init__.py +1 -0
- multi_agent_brief/workbuddy/diagnose.py +569 -0
- multi_agent_brief/workbuddy/skill_pack.py +309 -0
- multi_agent_brief/workspace/__init__.py +1 -0
- multi_agent_brief/workspace/init_profile.py +45 -0
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
"""Fact-layer import transaction for fast-rerun workspaces.
|
|
2
|
+
|
|
3
|
+
Validates, archives, and atomically imports prior-run fact-layer artifacts
|
|
4
|
+
into a new run. Owns the mabw.fact_layer_import.v1 plan schema checks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
import shutil
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from multi_agent_brief.orchestrator_contract import resolve_repo_workdir
|
|
17
|
+
from multi_agent_brief.orchestrator.runtime_state._io import (
|
|
18
|
+
_read_json,
|
|
19
|
+
_restore_state_files,
|
|
20
|
+
_sha256_file,
|
|
21
|
+
_snapshot_state_files,
|
|
22
|
+
_write_json_atomic,
|
|
23
|
+
)
|
|
24
|
+
from multi_agent_brief.orchestrator.runtime_state._transactions import (
|
|
25
|
+
_load_manifest_and_workflow,
|
|
26
|
+
_restore_file_paths,
|
|
27
|
+
_snapshot_file_paths,
|
|
28
|
+
)
|
|
29
|
+
from multi_agent_brief.orchestrator.runtime_state.artifact_registry import (
|
|
30
|
+
ARTIFACT_VALID,
|
|
31
|
+
_build_artifact_registry,
|
|
32
|
+
)
|
|
33
|
+
from multi_agent_brief.orchestrator.runtime_state.completion_gates import (
|
|
34
|
+
_fast_rerun_import_freshness_snapshot,
|
|
35
|
+
_raise_completion_reasons,
|
|
36
|
+
)
|
|
37
|
+
from multi_agent_brief.orchestrator.runtime_state.contracts_loader import (
|
|
38
|
+
_stage_ids,
|
|
39
|
+
load_artifact_contracts,
|
|
40
|
+
load_stage_specs,
|
|
41
|
+
)
|
|
42
|
+
from multi_agent_brief.orchestrator.runtime_state.errors import (
|
|
43
|
+
E_FACT_LAYER_IMPORT_INVALID,
|
|
44
|
+
E_TRANSACTION_PARTIAL_WRITE,
|
|
45
|
+
RuntimeStateError,
|
|
46
|
+
)
|
|
47
|
+
from multi_agent_brief.orchestrator.runtime_state.event_log import append_event
|
|
48
|
+
from multi_agent_brief.orchestrator.runtime_state.identity import (
|
|
49
|
+
_validate_runtime_run_id,
|
|
50
|
+
utc_now,
|
|
51
|
+
)
|
|
52
|
+
from multi_agent_brief.orchestrator.runtime_state.lifecycle import (
|
|
53
|
+
initialize_runtime_state,
|
|
54
|
+
show_runtime_state,
|
|
55
|
+
)
|
|
56
|
+
from multi_agent_brief.orchestrator.runtime_state.paths import (
|
|
57
|
+
_require_workspace,
|
|
58
|
+
_workspace_relative,
|
|
59
|
+
runtime_state_paths,
|
|
60
|
+
)
|
|
61
|
+
from multi_agent_brief.orchestrator.runtime_state.workflow import (
|
|
62
|
+
STAGE_COMPLETE,
|
|
63
|
+
_allowed_decisions_for_stage,
|
|
64
|
+
_next_stage_id,
|
|
65
|
+
_status_entry,
|
|
66
|
+
)
|
|
67
|
+
from multi_agent_brief.orchestrator.run_archive import (
|
|
68
|
+
RUN_ARCHIVE_FACT_LAYER_SCHEMA,
|
|
69
|
+
RUN_ARCHIVE_SCHEMA,
|
|
70
|
+
)
|
|
71
|
+
from multi_agent_brief.orchestrator.run_integrity import (
|
|
72
|
+
RUN_INTEGRITY_CLEAN,
|
|
73
|
+
interpret_run_integrity as _interpret_run_integrity,
|
|
74
|
+
project_for_read as _project_run_integrity_for_read,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
FACT_LAYER_IMPORT_SCHEMA = "mabw.fact_layer_import.v1"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
FACT_LAYER_IMPORT_REQUIRED_ARTIFACT_IDS = (
|
|
82
|
+
"durable_source_evidence_or_source_pack",
|
|
83
|
+
"input_classification",
|
|
84
|
+
"candidate_claims",
|
|
85
|
+
"screened_candidates",
|
|
86
|
+
"claim_ledger",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
FACT_LAYER_IMPORT_FORBIDDEN_ARTIFACT_IDS = {"source_candidates", "source_plan"}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
FACT_LAYER_IMPORT_SOURCE_PACK_ARTIFACT_ID = "durable_source_evidence_or_source_pack"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
FACT_LAYER_IMPORT_SINGLETON_PATHS = {
|
|
97
|
+
"input_classification": "output/input_classification.json",
|
|
98
|
+
"candidate_claims": "output/intermediate/candidate_claims.json",
|
|
99
|
+
"screened_candidates": "output/intermediate/screened_candidates.json",
|
|
100
|
+
"claim_ledger": "output/intermediate/claim_ledger.json",
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _resolve_fact_layer_archive_manifest(path: str | Path) -> Path:
|
|
105
|
+
raw = Path(path).expanduser()
|
|
106
|
+
resolved = raw.resolve()
|
|
107
|
+
if resolved.is_dir():
|
|
108
|
+
resolved = resolved / "manifest.json"
|
|
109
|
+
return resolved
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _path_text_is_unsafe(path_text: str) -> bool:
|
|
113
|
+
return (
|
|
114
|
+
not path_text
|
|
115
|
+
or path_text.startswith("/")
|
|
116
|
+
or bool(re.match(r"^[A-Za-z]:[\\/]", path_text))
|
|
117
|
+
or Path(path_text).is_absolute()
|
|
118
|
+
or any(part in {"", ".", ".."} for part in Path(path_text).parts)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _target_workspace_path(workspace: Path, rel_path: str) -> Path:
|
|
123
|
+
if _path_text_is_unsafe(rel_path):
|
|
124
|
+
raise RuntimeStateError(
|
|
125
|
+
"Fact layer import path must be workspace-relative.",
|
|
126
|
+
details={"path": rel_path},
|
|
127
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
128
|
+
)
|
|
129
|
+
target = (workspace / rel_path).resolve()
|
|
130
|
+
try:
|
|
131
|
+
target.relative_to(workspace)
|
|
132
|
+
except ValueError as exc:
|
|
133
|
+
raise RuntimeStateError(
|
|
134
|
+
"Fact layer import target escapes the workspace.",
|
|
135
|
+
details={"path": rel_path},
|
|
136
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
137
|
+
) from exc
|
|
138
|
+
return target
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _source_archive_path(archive_root: Path, rel_path: str) -> Path:
|
|
142
|
+
if _path_text_is_unsafe(rel_path):
|
|
143
|
+
raise RuntimeStateError(
|
|
144
|
+
"Fact layer archive path must be archive-relative.",
|
|
145
|
+
details={"path": rel_path},
|
|
146
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
147
|
+
)
|
|
148
|
+
source = (archive_root / rel_path).resolve()
|
|
149
|
+
try:
|
|
150
|
+
source.relative_to(archive_root)
|
|
151
|
+
except ValueError as exc:
|
|
152
|
+
raise RuntimeStateError(
|
|
153
|
+
"Fact layer archive path escapes the archive root.",
|
|
154
|
+
details={"path": rel_path},
|
|
155
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
156
|
+
) from exc
|
|
157
|
+
return source
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _reject_source_plan_fact_layer_record(*, artifact_id: str, archive_path: str, original_path: str) -> None:
|
|
161
|
+
if artifact_id in FACT_LAYER_IMPORT_FORBIDDEN_ARTIFACT_IDS:
|
|
162
|
+
raise RuntimeStateError(
|
|
163
|
+
"source_candidates/source_plan artifacts cannot be imported as frozen fact layer evidence.",
|
|
164
|
+
details={"artifact_id": artifact_id},
|
|
165
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
166
|
+
)
|
|
167
|
+
for label, path_text in (("archive_path", archive_path), ("original_path", original_path)):
|
|
168
|
+
if Path(path_text).name == "source_candidates.yaml":
|
|
169
|
+
raise RuntimeStateError(
|
|
170
|
+
"source_candidates.yaml is a source plan and cannot be imported as fact layer evidence.",
|
|
171
|
+
details={"artifact_id": artifact_id, label: path_text},
|
|
172
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _archive_fact_layer_path_for(original_path: str) -> str:
|
|
177
|
+
return f"fact_layer/{original_path}"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _validate_fact_layer_import_record_scope(
|
|
181
|
+
*,
|
|
182
|
+
artifact_id: str,
|
|
183
|
+
archive_path: str,
|
|
184
|
+
original_path: str,
|
|
185
|
+
nested_in_source_pack: bool,
|
|
186
|
+
) -> None:
|
|
187
|
+
allowed_ids = {FACT_LAYER_IMPORT_SOURCE_PACK_ARTIFACT_ID, *FACT_LAYER_IMPORT_SINGLETON_PATHS}
|
|
188
|
+
if artifact_id not in allowed_ids:
|
|
189
|
+
raise RuntimeStateError(
|
|
190
|
+
"Run archive fact_layer contains an unsupported artifact_id for import.",
|
|
191
|
+
details={"artifact_id": artifact_id},
|
|
192
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if artifact_id == FACT_LAYER_IMPORT_SOURCE_PACK_ARTIFACT_ID:
|
|
196
|
+
if not nested_in_source_pack:
|
|
197
|
+
raise RuntimeStateError(
|
|
198
|
+
"Durable source evidence must be imported from the source pack file list.",
|
|
199
|
+
details={"artifact_id": artifact_id},
|
|
200
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
201
|
+
)
|
|
202
|
+
if not original_path.startswith("input/sources/"):
|
|
203
|
+
raise RuntimeStateError(
|
|
204
|
+
"Durable source evidence imports must target input/sources/.",
|
|
205
|
+
details={"artifact_id": artifact_id, "original_path": original_path},
|
|
206
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
207
|
+
)
|
|
208
|
+
if not archive_path.startswith("fact_layer/input/sources/"):
|
|
209
|
+
raise RuntimeStateError(
|
|
210
|
+
"Durable source evidence archive paths must stay under fact_layer/input/sources/.",
|
|
211
|
+
details={"artifact_id": artifact_id, "archive_path": archive_path},
|
|
212
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
213
|
+
)
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
expected_original_path = FACT_LAYER_IMPORT_SINGLETON_PATHS[artifact_id]
|
|
217
|
+
if nested_in_source_pack:
|
|
218
|
+
raise RuntimeStateError(
|
|
219
|
+
"Singleton fact layer artifacts cannot be imported from a files list.",
|
|
220
|
+
details={"artifact_id": artifact_id},
|
|
221
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
222
|
+
)
|
|
223
|
+
if original_path != expected_original_path:
|
|
224
|
+
raise RuntimeStateError(
|
|
225
|
+
"Singleton fact layer artifact targets do not match the import contract.",
|
|
226
|
+
details={
|
|
227
|
+
"artifact_id": artifact_id,
|
|
228
|
+
"expected_original_path": expected_original_path,
|
|
229
|
+
"actual_original_path": original_path,
|
|
230
|
+
},
|
|
231
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
232
|
+
)
|
|
233
|
+
expected_archive_path = _archive_fact_layer_path_for(expected_original_path)
|
|
234
|
+
if archive_path != expected_archive_path:
|
|
235
|
+
raise RuntimeStateError(
|
|
236
|
+
"Singleton fact layer archive paths do not match the import contract.",
|
|
237
|
+
details={
|
|
238
|
+
"artifact_id": artifact_id,
|
|
239
|
+
"expected_archive_path": expected_archive_path,
|
|
240
|
+
"actual_archive_path": archive_path,
|
|
241
|
+
},
|
|
242
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _require_fact_layer_file_record(
|
|
247
|
+
*,
|
|
248
|
+
workspace: Path,
|
|
249
|
+
archive_root: Path,
|
|
250
|
+
record: dict[str, Any],
|
|
251
|
+
artifact_id: str,
|
|
252
|
+
nested_in_source_pack: bool = False,
|
|
253
|
+
) -> dict[str, Any]:
|
|
254
|
+
archive_path = str(record.get("archive_path") or "")
|
|
255
|
+
original_path = str(record.get("original_path") or "")
|
|
256
|
+
sha256 = str(record.get("sha256") or "")
|
|
257
|
+
size_bytes = record.get("size_bytes")
|
|
258
|
+
_reject_source_plan_fact_layer_record(
|
|
259
|
+
artifact_id=artifact_id,
|
|
260
|
+
archive_path=archive_path,
|
|
261
|
+
original_path=original_path,
|
|
262
|
+
)
|
|
263
|
+
if not archive_path or not original_path or not sha256:
|
|
264
|
+
raise RuntimeStateError(
|
|
265
|
+
"Fact layer artifact record is missing path or hash fields.",
|
|
266
|
+
details={"artifact_id": artifact_id, "record": record},
|
|
267
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
268
|
+
)
|
|
269
|
+
_validate_fact_layer_import_record_scope(
|
|
270
|
+
artifact_id=artifact_id,
|
|
271
|
+
archive_path=archive_path,
|
|
272
|
+
original_path=original_path,
|
|
273
|
+
nested_in_source_pack=nested_in_source_pack,
|
|
274
|
+
)
|
|
275
|
+
source = _source_archive_path(archive_root, archive_path)
|
|
276
|
+
target = _target_workspace_path(workspace, original_path)
|
|
277
|
+
if not source.exists() or not source.is_file():
|
|
278
|
+
raise RuntimeStateError(
|
|
279
|
+
"Fact layer archive file is missing.",
|
|
280
|
+
details={"artifact_id": artifact_id, "archive_path": archive_path},
|
|
281
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
282
|
+
)
|
|
283
|
+
actual_sha = _sha256_file(source)
|
|
284
|
+
if actual_sha != sha256:
|
|
285
|
+
raise RuntimeStateError(
|
|
286
|
+
"Fact layer archive file hash does not match manifest.",
|
|
287
|
+
details={
|
|
288
|
+
"artifact_id": artifact_id,
|
|
289
|
+
"archive_path": archive_path,
|
|
290
|
+
"expected_sha256": sha256,
|
|
291
|
+
"actual_sha256": actual_sha,
|
|
292
|
+
},
|
|
293
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
294
|
+
)
|
|
295
|
+
if isinstance(size_bytes, int) and source.stat().st_size != size_bytes:
|
|
296
|
+
raise RuntimeStateError(
|
|
297
|
+
"Fact layer archive file size does not match manifest.",
|
|
298
|
+
details={
|
|
299
|
+
"artifact_id": artifact_id,
|
|
300
|
+
"archive_path": archive_path,
|
|
301
|
+
"expected_size_bytes": size_bytes,
|
|
302
|
+
"actual_size_bytes": source.stat().st_size,
|
|
303
|
+
},
|
|
304
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
305
|
+
)
|
|
306
|
+
return {
|
|
307
|
+
"artifact_id": artifact_id,
|
|
308
|
+
"archive_path": archive_path,
|
|
309
|
+
"workspace_path": original_path,
|
|
310
|
+
"source_path": source,
|
|
311
|
+
"target_path": target,
|
|
312
|
+
"sha256": sha256,
|
|
313
|
+
"size_bytes": source.stat().st_size,
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _read_fact_layer_import_plan(
|
|
318
|
+
*,
|
|
319
|
+
workspace: Path,
|
|
320
|
+
archive: str | Path,
|
|
321
|
+
) -> dict[str, Any]:
|
|
322
|
+
manifest_path = _resolve_fact_layer_archive_manifest(archive)
|
|
323
|
+
if not manifest_path.exists() or not manifest_path.is_file():
|
|
324
|
+
raise RuntimeStateError(
|
|
325
|
+
"Run archive manifest not found for fact layer import.",
|
|
326
|
+
details={"archive": str(archive), "manifest_path": str(manifest_path)},
|
|
327
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
328
|
+
)
|
|
329
|
+
archive_root = manifest_path.parent
|
|
330
|
+
try:
|
|
331
|
+
archive_manifest = _read_json(manifest_path)
|
|
332
|
+
except RuntimeStateError as exc:
|
|
333
|
+
raise RuntimeStateError(
|
|
334
|
+
"Run archive manifest is unreadable for fact layer import.",
|
|
335
|
+
details={"manifest_path": str(manifest_path), "reason": str(exc)},
|
|
336
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
337
|
+
) from exc
|
|
338
|
+
if archive_manifest.get("schema_version") != RUN_ARCHIVE_SCHEMA:
|
|
339
|
+
raise RuntimeStateError(
|
|
340
|
+
"Run archive manifest has unsupported schema.",
|
|
341
|
+
details={
|
|
342
|
+
"manifest_path": str(manifest_path),
|
|
343
|
+
"schema_version": archive_manifest.get("schema_version"),
|
|
344
|
+
},
|
|
345
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
346
|
+
)
|
|
347
|
+
integrity_verdict = _interpret_run_integrity(archive_manifest.get("run_integrity"), field_present=True)
|
|
348
|
+
integrity = _project_run_integrity_for_read(integrity_verdict)
|
|
349
|
+
if (
|
|
350
|
+
integrity_verdict.kind != "canonical"
|
|
351
|
+
or
|
|
352
|
+
integrity.get("status") != RUN_INTEGRITY_CLEAN
|
|
353
|
+
or integrity.get("reference_eligible") is not True
|
|
354
|
+
or integrity.get("clean_single_shot") is not True
|
|
355
|
+
):
|
|
356
|
+
raise RuntimeStateError(
|
|
357
|
+
"Only clean reference-eligible run archives can be imported as a frozen fact layer.",
|
|
358
|
+
details={"run_integrity": integrity},
|
|
359
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
360
|
+
)
|
|
361
|
+
fact_layer = archive_manifest.get("fact_layer") if isinstance(archive_manifest.get("fact_layer"), dict) else None
|
|
362
|
+
if not fact_layer or fact_layer.get("schema_version") != RUN_ARCHIVE_FACT_LAYER_SCHEMA:
|
|
363
|
+
raise RuntimeStateError(
|
|
364
|
+
"Run archive manifest does not contain a supported fact_layer projection.",
|
|
365
|
+
details={"manifest_path": str(manifest_path)},
|
|
366
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
367
|
+
)
|
|
368
|
+
if fact_layer.get("status") != "complete" or fact_layer.get("missing_artifact_ids"):
|
|
369
|
+
raise RuntimeStateError(
|
|
370
|
+
"Run archive fact_layer is incomplete and cannot be imported.",
|
|
371
|
+
details={
|
|
372
|
+
"status": fact_layer.get("status"),
|
|
373
|
+
"missing_artifact_ids": fact_layer.get("missing_artifact_ids"),
|
|
374
|
+
},
|
|
375
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
artifacts = fact_layer.get("artifacts")
|
|
379
|
+
if not isinstance(artifacts, list):
|
|
380
|
+
raise RuntimeStateError(
|
|
381
|
+
"Run archive fact_layer artifacts must be a list.",
|
|
382
|
+
details={"manifest_path": str(manifest_path)},
|
|
383
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
384
|
+
)
|
|
385
|
+
seen_ids: set[str] = set()
|
|
386
|
+
import_files: list[dict[str, Any]] = []
|
|
387
|
+
for artifact in artifacts:
|
|
388
|
+
if not isinstance(artifact, dict):
|
|
389
|
+
raise RuntimeStateError(
|
|
390
|
+
"Run archive fact_layer contains an invalid artifact record.",
|
|
391
|
+
details={"artifact": artifact},
|
|
392
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
393
|
+
)
|
|
394
|
+
artifact_id = str(artifact.get("artifact_id") or "")
|
|
395
|
+
if not artifact_id:
|
|
396
|
+
raise RuntimeStateError(
|
|
397
|
+
"Run archive fact_layer artifact is missing artifact_id.",
|
|
398
|
+
details={"artifact": artifact},
|
|
399
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
400
|
+
)
|
|
401
|
+
_reject_source_plan_fact_layer_record(
|
|
402
|
+
artifact_id=artifact_id,
|
|
403
|
+
archive_path=str(artifact.get("archive_path") or ""),
|
|
404
|
+
original_path=str(artifact.get("original_path") or ""),
|
|
405
|
+
)
|
|
406
|
+
if artifact_id in seen_ids and artifact_id != "durable_source_evidence_or_source_pack":
|
|
407
|
+
raise RuntimeStateError(
|
|
408
|
+
"Run archive fact_layer contains duplicate non-pack artifact records.",
|
|
409
|
+
details={"artifact_id": artifact_id},
|
|
410
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
411
|
+
)
|
|
412
|
+
seen_ids.add(artifact_id)
|
|
413
|
+
files = artifact.get("files")
|
|
414
|
+
if isinstance(files, list):
|
|
415
|
+
if artifact_id != FACT_LAYER_IMPORT_SOURCE_PACK_ARTIFACT_ID:
|
|
416
|
+
raise RuntimeStateError(
|
|
417
|
+
"Only durable source evidence can be imported from a files list.",
|
|
418
|
+
details={"artifact_id": artifact_id},
|
|
419
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
420
|
+
)
|
|
421
|
+
if not files:
|
|
422
|
+
raise RuntimeStateError(
|
|
423
|
+
"Run archive fact_layer source pack is empty.",
|
|
424
|
+
details={"artifact_id": artifact_id},
|
|
425
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
426
|
+
)
|
|
427
|
+
for file_record in files:
|
|
428
|
+
if not isinstance(file_record, dict):
|
|
429
|
+
raise RuntimeStateError(
|
|
430
|
+
"Run archive fact_layer source pack contains an invalid file record.",
|
|
431
|
+
details={"artifact_id": artifact_id},
|
|
432
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
433
|
+
)
|
|
434
|
+
import_files.append(
|
|
435
|
+
_require_fact_layer_file_record(
|
|
436
|
+
workspace=workspace,
|
|
437
|
+
archive_root=archive_root,
|
|
438
|
+
record=file_record,
|
|
439
|
+
artifact_id=artifact_id,
|
|
440
|
+
nested_in_source_pack=True,
|
|
441
|
+
)
|
|
442
|
+
)
|
|
443
|
+
else:
|
|
444
|
+
import_files.append(
|
|
445
|
+
_require_fact_layer_file_record(
|
|
446
|
+
workspace=workspace,
|
|
447
|
+
archive_root=archive_root,
|
|
448
|
+
record=artifact,
|
|
449
|
+
artifact_id=artifact_id,
|
|
450
|
+
nested_in_source_pack=False,
|
|
451
|
+
)
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
missing_required = sorted(set(FACT_LAYER_IMPORT_REQUIRED_ARTIFACT_IDS) - seen_ids)
|
|
455
|
+
if missing_required:
|
|
456
|
+
raise RuntimeStateError(
|
|
457
|
+
"Run archive fact_layer is missing required artifact records.",
|
|
458
|
+
details={"missing_artifact_ids": missing_required},
|
|
459
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
460
|
+
)
|
|
461
|
+
if not import_files:
|
|
462
|
+
raise RuntimeStateError(
|
|
463
|
+
"Run archive fact_layer has no importable files.",
|
|
464
|
+
details={"manifest_path": str(manifest_path)},
|
|
465
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
466
|
+
)
|
|
467
|
+
_reject_duplicate_fact_layer_import_targets(import_files)
|
|
468
|
+
|
|
469
|
+
return {
|
|
470
|
+
"archive_manifest": archive_manifest,
|
|
471
|
+
"archive_manifest_path": manifest_path,
|
|
472
|
+
"archive_manifest_sha256": _sha256_file(manifest_path),
|
|
473
|
+
"archive_root": archive_root,
|
|
474
|
+
"fact_layer": fact_layer,
|
|
475
|
+
"fact_layer_sha256": hashlib.sha256(
|
|
476
|
+
json.dumps(fact_layer, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
477
|
+
).hexdigest(),
|
|
478
|
+
"import_files": import_files,
|
|
479
|
+
"required_artifact_ids": list(FACT_LAYER_IMPORT_REQUIRED_ARTIFACT_IDS),
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def _copy_import_files(import_files: list[dict[str, Any]]) -> None:
|
|
484
|
+
for record in import_files:
|
|
485
|
+
source = record["source_path"]
|
|
486
|
+
target = record["target_path"]
|
|
487
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
488
|
+
try:
|
|
489
|
+
shutil.copy2(source, target)
|
|
490
|
+
except OSError as exc:
|
|
491
|
+
raise RuntimeStateError(
|
|
492
|
+
"Failed to copy fact layer archive file into workspace.",
|
|
493
|
+
details={
|
|
494
|
+
"archive_path": record["archive_path"],
|
|
495
|
+
"workspace_path": record["workspace_path"],
|
|
496
|
+
"reason": str(exc),
|
|
497
|
+
},
|
|
498
|
+
error_code=E_TRANSACTION_PARTIAL_WRITE,
|
|
499
|
+
) from exc
|
|
500
|
+
copied_sha = _sha256_file(target)
|
|
501
|
+
if copied_sha != record["sha256"]:
|
|
502
|
+
raise RuntimeStateError(
|
|
503
|
+
"Imported fact layer file hash mismatch after copy.",
|
|
504
|
+
details={
|
|
505
|
+
"workspace_path": record["workspace_path"],
|
|
506
|
+
"expected_sha256": record["sha256"],
|
|
507
|
+
"actual_sha256": copied_sha,
|
|
508
|
+
},
|
|
509
|
+
error_code=E_TRANSACTION_PARTIAL_WRITE,
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def _reject_existing_fact_layer_import_targets(import_files: list[dict[str, Any]]) -> None:
|
|
514
|
+
existing = [
|
|
515
|
+
{
|
|
516
|
+
"workspace_path": record["workspace_path"],
|
|
517
|
+
"sha256": _sha256_file(record["target_path"]) if record["target_path"].is_file() else "",
|
|
518
|
+
}
|
|
519
|
+
for record in import_files
|
|
520
|
+
if record["target_path"].exists()
|
|
521
|
+
]
|
|
522
|
+
if existing:
|
|
523
|
+
raise RuntimeStateError(
|
|
524
|
+
"Fact layer import target files already exist; use a fresh workspace so import cannot overwrite user files.",
|
|
525
|
+
details={"existing_targets": existing},
|
|
526
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def _reject_existing_fact_layer_import_leftovers(workspace: Path, import_files: list[dict[str, Any]]) -> None:
|
|
531
|
+
allowed_targets = {record["target_path"].resolve() for record in import_files}
|
|
532
|
+
leftovers: list[str] = []
|
|
533
|
+
|
|
534
|
+
for root in (workspace / "input" / "sources", workspace / "output"):
|
|
535
|
+
if not root.exists():
|
|
536
|
+
continue
|
|
537
|
+
for path in sorted(root.rglob("*")):
|
|
538
|
+
if not path.is_file():
|
|
539
|
+
continue
|
|
540
|
+
resolved = path.resolve()
|
|
541
|
+
if resolved not in allowed_targets:
|
|
542
|
+
leftovers.append(_workspace_relative(workspace, path))
|
|
543
|
+
|
|
544
|
+
source_candidates = workspace / "source_candidates.yaml"
|
|
545
|
+
if source_candidates.exists() and source_candidates.is_file():
|
|
546
|
+
leftovers.append("source_candidates.yaml")
|
|
547
|
+
|
|
548
|
+
if leftovers:
|
|
549
|
+
raise RuntimeStateError(
|
|
550
|
+
"Fact layer import requires a clean target workspace without existing source/output leftovers.",
|
|
551
|
+
details={"existing_leftovers": leftovers},
|
|
552
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def _reject_duplicate_fact_layer_import_targets(import_files: list[dict[str, Any]]) -> None:
|
|
557
|
+
seen_targets: dict[str, str] = {}
|
|
558
|
+
duplicates: list[dict[str, str]] = []
|
|
559
|
+
for record in import_files:
|
|
560
|
+
workspace_path = str(record["workspace_path"])
|
|
561
|
+
if workspace_path in seen_targets:
|
|
562
|
+
duplicates.append({
|
|
563
|
+
"workspace_path": workspace_path,
|
|
564
|
+
"first_artifact_id": seen_targets[workspace_path],
|
|
565
|
+
"duplicate_artifact_id": str(record["artifact_id"]),
|
|
566
|
+
})
|
|
567
|
+
else:
|
|
568
|
+
seen_targets[workspace_path] = str(record["artifact_id"])
|
|
569
|
+
if duplicates:
|
|
570
|
+
raise RuntimeStateError(
|
|
571
|
+
"Run archive fact_layer contains duplicate import targets.",
|
|
572
|
+
details={"duplicate_targets": duplicates},
|
|
573
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def _imported_required_artifact_reasons(registry: dict[str, Any]) -> list[str]:
|
|
578
|
+
records = registry.get("artifacts") if isinstance(registry.get("artifacts"), dict) else {}
|
|
579
|
+
reasons: list[str] = []
|
|
580
|
+
for artifact_id in FACT_LAYER_IMPORT_REQUIRED_ARTIFACT_IDS:
|
|
581
|
+
if artifact_id == "durable_source_evidence_or_source_pack":
|
|
582
|
+
continue
|
|
583
|
+
record = records.get(artifact_id) if isinstance(records.get(artifact_id), dict) else {}
|
|
584
|
+
status = str(record.get("status") or "")
|
|
585
|
+
validation_result = str(record.get("validation_result") or "")
|
|
586
|
+
if status != ARTIFACT_VALID:
|
|
587
|
+
reasons.append(
|
|
588
|
+
f"Imported required artifact '{artifact_id}' is {status or '<missing>'} ({validation_result or 'not_checked'})."
|
|
589
|
+
)
|
|
590
|
+
return reasons
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def import_fact_layer_transaction(
|
|
594
|
+
*,
|
|
595
|
+
workspace: str | Path,
|
|
596
|
+
archive: str | Path,
|
|
597
|
+
runtime: str = "hermes",
|
|
598
|
+
repo_workdir: str | Path | None = None,
|
|
599
|
+
actor: str = "cli",
|
|
600
|
+
) -> dict[str, Any]:
|
|
601
|
+
"""Import a complete archived frozen fact layer into a new runtime run."""
|
|
602
|
+
ws = _require_workspace(workspace)
|
|
603
|
+
paths = runtime_state_paths(ws)
|
|
604
|
+
if any(paths[key].exists() for key in ("runtime_manifest", "workflow_state", "event_log", "artifact_registry")):
|
|
605
|
+
raise RuntimeStateError(
|
|
606
|
+
"Fact layer import requires a workspace without existing runtime state. Use a fresh workspace for fast-rerun import.",
|
|
607
|
+
details={"workspace": str(ws)},
|
|
608
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
import_plan = _read_fact_layer_import_plan(workspace=ws, archive=archive)
|
|
612
|
+
_reject_existing_fact_layer_import_leftovers(ws, import_plan["import_files"])
|
|
613
|
+
_reject_existing_fact_layer_import_targets(import_plan["import_files"])
|
|
614
|
+
state_snapshots = _snapshot_state_files(paths, ("runtime_manifest", "workflow_state", "artifact_registry", "event_log"))
|
|
615
|
+
target_snapshots = _snapshot_file_paths([record["target_path"] for record in import_plan["import_files"]])
|
|
616
|
+
try:
|
|
617
|
+
initialize_runtime_state(
|
|
618
|
+
workspace=ws,
|
|
619
|
+
runtime=runtime,
|
|
620
|
+
repo_workdir=repo_workdir,
|
|
621
|
+
actor=actor,
|
|
622
|
+
recipe="fast-rerun",
|
|
623
|
+
)
|
|
624
|
+
ws, paths, manifest, workflow = _load_manifest_and_workflow(ws)
|
|
625
|
+
repo = resolve_repo_workdir(repo_workdir, workspace=ws)
|
|
626
|
+
stages = load_stage_specs(repo)
|
|
627
|
+
artifacts = load_artifact_contracts(repo)
|
|
628
|
+
_copy_import_files(import_plan["import_files"])
|
|
629
|
+
|
|
630
|
+
now = utc_now()
|
|
631
|
+
run_id = str(manifest["run_id"])
|
|
632
|
+
satisfied_stage_ids = [
|
|
633
|
+
stage_id
|
|
634
|
+
for stage_id in ("doctor", "source-discovery", "input-governance", "scout", "screener", "claim-ledger")
|
|
635
|
+
if stage_id in _stage_ids(stages)
|
|
636
|
+
]
|
|
637
|
+
imported_file_records = [
|
|
638
|
+
{
|
|
639
|
+
"artifact_id": record["artifact_id"],
|
|
640
|
+
"archive_path": record["archive_path"],
|
|
641
|
+
"workspace_path": record["workspace_path"],
|
|
642
|
+
"sha256": record["sha256"],
|
|
643
|
+
"size_bytes": record["size_bytes"],
|
|
644
|
+
}
|
|
645
|
+
for record in import_plan["import_files"]
|
|
646
|
+
]
|
|
647
|
+
source_run_id = _validate_runtime_run_id(str(import_plan["archive_manifest"].get("run_id") or ""))
|
|
648
|
+
logical_archive_manifest = f"output/runs/{source_run_id}/manifest.json"
|
|
649
|
+
import_record = {
|
|
650
|
+
"schema_version": FACT_LAYER_IMPORT_SCHEMA,
|
|
651
|
+
"imported_at": now,
|
|
652
|
+
"source_run_id": source_run_id,
|
|
653
|
+
"source_archive_manifest": logical_archive_manifest,
|
|
654
|
+
"source_archive_manifest_sha256": import_plan["archive_manifest_sha256"],
|
|
655
|
+
"fact_layer_status": import_plan["fact_layer"].get("status"),
|
|
656
|
+
"fact_layer_sha256": import_plan["fact_layer_sha256"],
|
|
657
|
+
"satisfied_stage_ids": satisfied_stage_ids,
|
|
658
|
+
"required_artifact_ids": import_plan["required_artifact_ids"],
|
|
659
|
+
"imported_file_count": len(imported_file_records),
|
|
660
|
+
"imported_files": imported_file_records,
|
|
661
|
+
"freshness_at_import": _fast_rerun_import_freshness_snapshot(ws, checked_at=now),
|
|
662
|
+
"timing_comparability": "downstream_only",
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
manifest = dict(manifest)
|
|
666
|
+
manifest["updated_at"] = now
|
|
667
|
+
manifest["recipe"] = "fast-rerun"
|
|
668
|
+
manifest["fact_layer_import"] = import_record
|
|
669
|
+
|
|
670
|
+
statuses = dict(workflow.get("stage_statuses") or {})
|
|
671
|
+
for stage_id in satisfied_stage_ids:
|
|
672
|
+
statuses[stage_id] = _status_entry(
|
|
673
|
+
STAGE_COMPLETE,
|
|
674
|
+
"Satisfied by frozen fact layer import.",
|
|
675
|
+
now,
|
|
676
|
+
metadata={
|
|
677
|
+
"satisfied_by_import": True,
|
|
678
|
+
"fact_layer_import_sha256": import_record["fact_layer_sha256"],
|
|
679
|
+
"source_run_id": import_record["source_run_id"],
|
|
680
|
+
},
|
|
681
|
+
)
|
|
682
|
+
current_stage = "analyst" if "analyst" in _stage_ids(stages) else _next_stage_id(stages, satisfied_stage_ids[-1])
|
|
683
|
+
workflow = dict(workflow)
|
|
684
|
+
workflow["updated_at"] = now
|
|
685
|
+
workflow["current_stage"] = current_stage
|
|
686
|
+
workflow["blocked"] = False
|
|
687
|
+
workflow["blocking_reason"] = ""
|
|
688
|
+
workflow["stage_statuses"] = statuses
|
|
689
|
+
workflow["last_decision"] = {
|
|
690
|
+
"stage_id": "claim-ledger",
|
|
691
|
+
"decision": "continue",
|
|
692
|
+
"reason": "Frozen fact layer imported for fast-rerun.",
|
|
693
|
+
"recorded_at": now,
|
|
694
|
+
}
|
|
695
|
+
workflow["next_allowed_decisions"] = _allowed_decisions_for_stage(stages, current_stage)
|
|
696
|
+
|
|
697
|
+
registry = _build_artifact_registry(
|
|
698
|
+
workspace=ws,
|
|
699
|
+
run_id=run_id,
|
|
700
|
+
artifacts=artifacts,
|
|
701
|
+
workflow=workflow,
|
|
702
|
+
updated_at=now,
|
|
703
|
+
)
|
|
704
|
+
imported_artifact_reasons = _imported_required_artifact_reasons(registry)
|
|
705
|
+
if imported_artifact_reasons:
|
|
706
|
+
_raise_completion_reasons(
|
|
707
|
+
message="Imported fact layer files do not satisfy current artifact contracts",
|
|
708
|
+
reasons=imported_artifact_reasons,
|
|
709
|
+
error_code=E_FACT_LAYER_IMPORT_INVALID,
|
|
710
|
+
details={"source_run_id": import_record["source_run_id"]},
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
_write_json_atomic(paths["runtime_manifest"], manifest)
|
|
714
|
+
_write_json_atomic(paths["artifact_registry"], registry)
|
|
715
|
+
_write_json_atomic(paths["workflow_state"], workflow)
|
|
716
|
+
append_event(
|
|
717
|
+
workspace=ws,
|
|
718
|
+
run_id=run_id,
|
|
719
|
+
event_type="fact_layer_imported",
|
|
720
|
+
actor=actor,
|
|
721
|
+
stage_id="claim-ledger",
|
|
722
|
+
decision="continue",
|
|
723
|
+
reason="Frozen fact layer imported for fast-rerun.",
|
|
724
|
+
metadata={
|
|
725
|
+
"source_run_id": import_record["source_run_id"],
|
|
726
|
+
"source_archive_manifest": import_record["source_archive_manifest"],
|
|
727
|
+
"fact_layer_sha256": import_record["fact_layer_sha256"],
|
|
728
|
+
"imported_file_count": import_record["imported_file_count"],
|
|
729
|
+
"satisfied_stage_ids": satisfied_stage_ids,
|
|
730
|
+
},
|
|
731
|
+
)
|
|
732
|
+
except Exception as exc:
|
|
733
|
+
try:
|
|
734
|
+
_restore_file_paths(target_snapshots)
|
|
735
|
+
_restore_state_files(paths, state_snapshots)
|
|
736
|
+
except RuntimeStateError as rollback_exc:
|
|
737
|
+
raise RuntimeStateError(
|
|
738
|
+
"Fact layer import partially wrote files and failed rollback.",
|
|
739
|
+
details={
|
|
740
|
+
"import_error": str(exc),
|
|
741
|
+
"rollback_error": str(rollback_exc),
|
|
742
|
+
},
|
|
743
|
+
error_code=E_TRANSACTION_PARTIAL_WRITE,
|
|
744
|
+
) from rollback_exc
|
|
745
|
+
if isinstance(exc, RuntimeStateError):
|
|
746
|
+
raise
|
|
747
|
+
raise RuntimeStateError(
|
|
748
|
+
"Fact layer import failed; workspace files were restored.",
|
|
749
|
+
details={"reason": str(exc)},
|
|
750
|
+
error_code=E_TRANSACTION_PARTIAL_WRITE,
|
|
751
|
+
) from exc
|
|
752
|
+
|
|
753
|
+
state = show_runtime_state(workspace=ws)
|
|
754
|
+
state["fact_layer_import"] = import_record
|
|
755
|
+
return state
|