From daab564e81709ef78d24e3d2844c69e37c5a25fc Mon Sep 17 00:00:00 2001 From: jhogyu Date: Mon, 31 Aug 2026 14:02:15 +0900 Subject: [PATCH] feat(stage2): implement S2_20 relief planner - add one inline deterministic MCP task and sealed planning assets - reseal S2_00/S2_10 release hashes for the S2_20 handoff - keep live and legal admission explicitly pending --- .../agent_scripts/Stage_2_S2_00.yml | 2 +- .../agent_scripts/Stage_2_S2_20.yml | 3975 +++++++++++++++++ .../Stage_2_Clean/corpus/build_receipt.json | 65 + .../Stage_2_Clean/corpus/chunk_manifest.jsonl | 1 + .../Stage_2_Clean/corpus/source_manifest.json | 24 + .../corpus/weaviate_collection.schema.json | 533 +++ .../stage2_code_executor_binding.yml | 318 +- .../deployment/stage2_loader_binding.yml | 2 +- .../deployment/stage2_s2_10_llm_binding.yml | 2 +- .../manifest/case_type_coverage.json | 10 +- .../manifest/case_type_registry.yml | 2208 +++++++++ .../manifest/case_type_rule_registry.yml | 1519 +++++++ .../manifest/module_manifest.json | 2 +- .../manifest/s2_00_inline_code_receipt.json | 2 +- .../manifest/s2_10_agent_receipt.json | 2 +- ...2_10_inline_prompt_projection_receipt.json | 2 +- .../manifest/s2_10_legal_review_receipt.json | 2 +- .../s2_10_model_benchmark_receipt.json | 2 +- .../s2_10_platform_adapter_receipt.json | 2 +- .../Stage_2_Clean/manifest/s2_10_release.json | 2 +- .../manifest/s2_20_inline_code_receipt.json | 1 + .../manifest/s2_20_parent_member_paths.json | 117 + ...tage2_deterministic_admission_receipt.json | 1 + .../manifest/stage2_release.json | 2 +- .../migration/actio_assets_receipt.json | 186 + .../migration/legacy_asset_disposition.yml | 32 + .../build_authority_law_value_release.py | 196 + .../build_authority_law_value_release.txt | 196 + .../offline_build/build_corpus_snapshot.py | 273 ++ .../offline_build/build_corpus_snapshot.txt | 273 ++ .../offline_build/build_release_manifests.py | 493 ++ .../offline_build/build_release_manifests.txt | 493 ++ .../build_s2_00_inline_projection.py | 62 + .../build_s2_00_inline_projection.txt | 62 + .../build_s2_10_agent_projection.py | 130 +- .../build_s2_10_agent_projection.txt | 130 +- .../build_s2_20_inline_projection.py | 665 +++ .../build_s2_20_inline_projection.txt | 665 +++ .../compile_case_type_registry.py | 267 ++ .../compile_case_type_registry.txt | 267 ++ .../compile_relief_rule_projection.py | 1006 +++++ .../compile_relief_rule_projection.txt | 1006 +++++ .../binding/binding_signature_projection.yml | 30 + .../calculations/CE-01_interest_delay.yml | 54 + .../CE-02_allocation_setoff_balance.yml | 54 + .../CE-03_limitation_deadline.yml | 54 + .../registry/calculations/CE-04_valuation.yml | 54 + .../calculations/CE-05_personal_injury.yml | 54 + .../CE-06_construction_defect.yml | 54 + .../calculations/CE-07_lease_use_gain.yml | 54 + .../CE-08_inheritance_reserved_share.yml | 54 + .../calculations/CE-09_wage_severance.yml | 54 + .../CE-10_actio_insolvency_value.yml | 54 + .../CE-11_distribution_share_division.yml | 54 + .../registry/calculations/CE-12_insurance.yml | 54 + .../calculations/CE-13_court_value_cost.yml | 54 + .../registry/calculations/CE-R1_ip_damage.yml | 54 + .../calculations/CE-R2_org_liquidation.yml | 54 + .../calculations/CE-R3_transport_maritime.yml | 54 + .../CE-R4_financial_instrument.yml | 54 + .../calculation_activation_registry.yml | 168 + .../corpus/requirement_fact_scope.yml | 7 + .../drafting/case_type_relief_rules.yml | 43 + .../drafting/counter_performance_rules.yml | 29 + .../drafting/forbidden_relief_rules.yml | 29 + .../drafting/procedural_declaration_rules.yml | 29 + .../registry/party/party_set_rules.yml | 8 + .../planning/option_disposition_policy.yml | 16 + .../regression/finding_fixture_map.yml | 32 + .../review/review_policy_registry.yml | 37 + .../temporal/inheritance_reserved_share.yml | 31 + .../release_ops/canary_rollback.py | 38 + .../release_ops/canary_rollback.txt | 38 + .../release_ops/release_validator.py | 581 +++ .../release_ops/release_validator.txt | 581 +++ .../renderers/R01_money_payment.yml | 31 + .../renderers/R02_delivery_possession.yml | 30 + .../renderers/R03_declaration_registry.yml | 30 + .../R04_special_nonmoney_performance.yml | 30 + .../renderers/R05_declaratory.yml | 30 + .../R06_constitutive_judgment_challenge.yml | 30 + .../routing/actio_pauliana_mortgage_route.yml | 23 + .../routing/actio_pauliana_route_registry.yml | 32 + .../runtime/authority/authority_preflight.py | 51 + .../runtime/authority/authority_preflight.txt | 51 + .../runtime/binding_signature_projection.py | 157 + .../runtime/binding_signature_projection.txt | 157 + .../runtime/c25_case_type_bind.py | 84 + .../runtime/c25_case_type_bind.txt | 84 + .../runtime/c26_rule_branch_bind.py | 123 + .../runtime/c26_rule_branch_bind.txt | 123 + .../Stage_2_Clean/runtime/c27_query_plan.py | 166 + .../Stage_2_Clean/runtime/c27_query_plan.txt | 166 + .../runtime/c28_weaviate_retrieve.py | 298 ++ .../runtime/c28_weaviate_retrieve.txt | 298 ++ .../Stage_2_Clean/runtime/c29_pack_verify.py | 137 + .../Stage_2_Clean/runtime/c29_pack_verify.txt | 137 + .../runtime/calculators/registry_engine.py | 286 ++ .../runtime/calculators/registry_engine.txt | 286 ++ .../Stage_2_Clean/runtime/s2_00_ingress.py | 2 +- .../Stage_2_Clean/runtime/s2_00_ingress.txt | 2 +- .../Stage_2_Clean/runtime/s2_20_reduce.py | 3917 ++++++++++++++++ .../Stage_2_Clean/runtime/s2_20_reduce.txt | 3917 ++++++++++++++++ .../schemas/binding_retrieval.schema.json | 1219 +++++ .../schemas/calculation.schema.json | 686 +++ .../schemas/deployment.schema.json | 327 +- .../schemas/domain_verdict.schema.json | 31 + .../schemas/relief_plan.schema.json | 838 ++++ .../schemas/review_status.schema.json | 1 + .../tests/fixtures/regression_manifest.json | 89 +- .../s2_20/actio_route_cap_matrix.json | 13 + .../calculation_temporal_boundaries.json | 11 + .../s2_20/case_type_rule_binding_matrix.json | 20 + .../case_type_zero_multi_hash_mutations.json | 18 + .../s2_20/minimal_supported_portfolio.json | 25 + .../s2_20/mixed_option_dispositions.json | 25 + .../s2_20/plan_publish_barrier_failure.json | 11 + .../fixtures/s2_20/regression_manifest.json | 408 ++ .../s2_20/retrieval_replay_injection.json | 23 + .../s2_00/test_cluster_bundle_compile.py | 13 +- .../s2_00/test_cluster_bundle_compile.txt | 13 +- .../s2_00/test_failure_and_atomic_publish.py | 16 +- .../s2_00/test_failure_and_atomic_publish.txt | 16 +- .../tests/s2_00/test_inline_code_parity.py | 8 +- .../tests/s2_00/test_inline_code_parity.txt | 8 +- .../s2_00/test_schema_hash_and_signals.py | 19 + .../s2_00/test_schema_hash_and_signals.txt | 19 + .../s2_20/test_agent_and_inline_parity.py | 68 + .../s2_20/test_agent_and_inline_parity.txt | 68 + .../s2_20/test_calculation_and_reports.py | 81 + .../s2_20/test_calculation_and_reports.txt | 81 + .../tests/s2_20/test_inline_output_schema.py | 924 ++++ .../tests/s2_20/test_inline_output_schema.txt | 924 ++++ .../tests/s2_20/test_offline_compilers.py | 397 ++ .../tests/s2_20/test_offline_compilers.txt | 397 ++ .../s2_20/test_option_group_and_binding.py | 141 + .../s2_20/test_option_group_and_binding.txt | 141 + .../s2_20/test_plan_publish_and_release.py | 297 ++ .../s2_20/test_plan_publish_and_release.txt | 297 ++ .../s2_20/test_regression_manifest_closure.py | 211 + .../test_regression_manifest_closure.txt | 211 + .../tests/s2_20/test_retrieval_and_pack.py | 181 + .../tests/s2_20/test_retrieval_and_pack.txt | 181 + .../actio_value_compensation_invariants.yml | 32 + .../S2_20_canonical_relief_plan_reduce.yml | 52 + .../YAML_Prompts/2. Stage_2/MEMORY.md | 12 + .../YAML_Prompts/2. Stage_2/S2_20_SOW.md | 993 ++++ .../YAML_Prompts/2. Stage_2/S2_20_assets.md | 468 ++ .../2. Stage_2/Stage_2_S2_00_v.2.yml | 2 +- .../YAML_Prompts/2. Stage_2/Stage_2_S2_20.yml | 3975 +++++++++++++++++ .../plans/s2-20-deterministic-package.md | 110 + 151 files changed, 42474 insertions(+), 118 deletions(-) create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_20.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/build_receipt.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/chunk_manifest.jsonl create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/source_manifest.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/weaviate_collection.schema.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_registry.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_rule_registry.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_20_inline_code_receipt.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_20_parent_member_paths.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/migration/actio_assets_receipt.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/migration/legacy_asset_disposition.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_authority_law_value_release.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_authority_law_value_release.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_corpus_snapshot.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_corpus_snapshot.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_release_manifests.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_release_manifests.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_20_inline_projection.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_20_inline_projection.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_case_type_registry.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_case_type_registry.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_relief_rule_projection.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_relief_rule_projection.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/binding/binding_signature_projection.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-01_interest_delay.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-02_allocation_setoff_balance.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-03_limitation_deadline.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-04_valuation.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-05_personal_injury.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-06_construction_defect.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-07_lease_use_gain.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-08_inheritance_reserved_share.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-09_wage_severance.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-10_actio_insolvency_value.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-11_distribution_share_division.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-12_insurance.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-13_court_value_cost.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R1_ip_damage.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R2_org_liquidation.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R3_transport_maritime.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R4_financial_instrument.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/calculation_activation_registry.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/corpus/requirement_fact_scope.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/case_type_relief_rules.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/counter_performance_rules.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/forbidden_relief_rules.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/procedural_declaration_rules.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/party/party_set_rules.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/planning/option_disposition_policy.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/regression/finding_fixture_map.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/review/review_policy_registry.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/temporal/inheritance_reserved_share.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/canary_rollback.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/canary_rollback.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/release_validator.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/release_validator.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R01_money_payment.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R02_delivery_possession.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R03_declaration_registry.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R04_special_nonmoney_performance.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R05_declaratory.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R06_constitutive_judgment_challenge.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/routing/actio_pauliana_mortgage_route.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/routing/actio_pauliana_route_registry.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/authority/authority_preflight.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/authority/authority_preflight.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/binding_signature_projection.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/binding_signature_projection.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c25_case_type_bind.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c25_case_type_bind.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c26_rule_branch_bind.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c26_rule_branch_bind.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c27_query_plan.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c27_query_plan.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c28_weaviate_retrieve.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c28_weaviate_retrieve.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c29_pack_verify.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c29_pack_verify.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/calculators/registry_engine.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/calculators/registry_engine.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/binding_retrieval.schema.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/calculation.schema.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/domain_verdict.schema.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/relief_plan.schema.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/actio_route_cap_matrix.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/calculation_temporal_boundaries.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/case_type_rule_binding_matrix.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/case_type_zero_multi_hash_mutations.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/minimal_supported_portfolio.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/mixed_option_dispositions.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/plan_publish_barrier_failure.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/regression_manifest.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/retrieval_replay_injection.json create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_agent_and_inline_parity.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_agent_and_inline_parity.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_calculation_and_reports.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_calculation_and_reports.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_inline_output_schema.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_inline_output_schema.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_offline_compilers.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_offline_compilers.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_option_group_and_binding.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_option_group_and_binding.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_plan_publish_and_release.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_plan_publish_and_release.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_regression_manifest_closure.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_regression_manifest_closure.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_retrieval_and_pack.py create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_retrieval_and_pack.txt create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/validators/actio_value_compensation_invariants.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/workflows/S2_20_canonical_relief_plan_reduce.yml create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/S2_20_SOW.md create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/S2_20_assets.md create mode 100644 Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Stage_2_S2_20.yml create mode 100644 Case_02_Comparison_Research/plans/s2-20-deterministic-package.md diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_00.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_00.yml index 1b6cc077..819cc013 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_00.yml +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_00.yml @@ -192,7 +192,7 @@ Agent: # literal placeholders in the offline parity mirror and its unit tests. INLINE_USER_HASH = "{{__user_hash__}}" INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}" - EXPECTED_STAGE2_RELEASE_SHA256 = "0f21af3ddca538d9b6a5853dac23222c0b632d0e573de371a8de2709e691699d" + EXPECTED_STAGE2_RELEASE_SHA256 = "70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92" INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json" INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean" INLINE_STAGE2_RELEASE_PATH = ( diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_20.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_20.yml new file mode 100644 index 00000000..a4d3b5c0 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_20.yml @@ -0,0 +1,3975 @@ +Agent: + name: Stage_2_S2_20 + version: "1.0.0" + description: >- + S2_00 context와 S2_10 canonical DomainVerdict를 보존하면서 option portfolio, + exact case/rule binding, deterministic calculation, requirement-fact pack과 + frozen ReliefPlan을 생성하는 S2_20 NON-LLM-DETERMINISTIC Agent. + metadata: + workflow_id: S2_20 + execution_class: NON-LLM-DETERMINISTIC + execution_authority: MCP_CODE_EXECUTOR_INLINE + task_constitution: S2_20_EXACTLY_ONE_INLINE_RUN_CODE_V1 + implementation_status: IMPLEMENTED_OFFLINE_VERIFIED_LIVE_AND_LEGAL_ADMISSION_PENDING + legacy_stage2_v0_v3_runtime_dependency_count: 0 + Stages: + - name: S2_20 + description: >- + 단일 Code Executor 호출 내부에서 + (C20 || C21) -> C25 -> C26 -> (C22 || (C27 -> C28 -> C29)) + -> C30 -> C35를 실행하고 plan_publish_status를 마지막에 발행한다. + prevs: [] + nexts: [] + skip_confirm: true + tools: + mcpServers: + localdocs: + type: streamable-http + url: http://mcp-localdocs:8012/mcp + code-executor: + type: streamable-http + url: https://code-executor.mcp.eroomai.com/mcp + tasks: + - task_name: Task_S2_20_deterministic_relief_plan + description: >- + release-bound request와 immutable S2_00/S2_10 입력을 두 번 읽어 + 검증하고, 고정 registry만 사용하여 frozen plan을 status-last로 발행한다. + mcp: code-executor + tool_name: run_code + parameters: + language: python + requirements: "httpx==0.28.1" + network: agent-network + timeout: 300 + code: |- + #!/usr/bin/env python3 + """Release-bound deterministic S2_20 ReliefPlan executor. + + The code is self-contained. It does not call an LLM, import workspace + Python, execute shell commands, install packages at runtime, discover + files by directory scan, or mutate S2_10 legal judgments. + """ + + from __future__ import annotations + + import base64 + import binascii + from concurrent.futures import ThreadPoolExecutor + import contextlib + from datetime import date + from decimal import Decimal, InvalidOperation, ROUND_DOWN, ROUND_HALF_UP, ROUND_UP + import hashlib + import io + import itertools + import json + import re + import sys + import unicodedata + from pathlib import PurePosixPath + from typing import Any, Iterable, Mapping, Sequence + + + WORKFLOW_ID = "S2_20" + ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0" + EXPECTED_STAGE2_RELEASE_SHA256 = "70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92" + LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp" + WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp" + MCP_PROTOCOL_VERSION = "2025-03-26" + INLINE_USER_HASH = "{{__user_hash__}}" + INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}" + REQUEST_PATH = "stage2_control/s2_20_request.json" + ASSET_ROOT = "Default_Agent/Stage_2_Clean" + MAX_FILE_BYTES = 32 * 1024 * 1024 + MAX_TOTAL_BYTES = 256 * 1024 * 1024 + HEX64 = re.compile(r"^[a-f0-9]{64}$") + CLUSTER_ID = re.compile(r"^CL-[a-f0-9]{24}$") + IDENT = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$") + RELIEF_RULE_PATH = re.compile(r"^rules/relief/CT-[0-9]{3}\.md$") + LEGAL_REVIEW_PATH = re.compile(r"^legal_review/[A-Za-z0-9][A-Za-z0-9._-]{0,191}\.json$") + DISPOSITIONS = ("selected", "alternative", "excluded", "deferred") + TARGET_SIGNATURE_FIELDS = ( + "claim_family", "legal_effect", "performance_kind", "object_kind", + "party_role_pattern", "source_obligation_kind", "remedy_mode", + "registry_action", "procedural_posture", "special_statute_tags", + ) + + + class S220Error(Exception): + def __init__(self, code: str, message: str, *, details: Any | None = None) -> None: + super().__init__(message) + self.code = code + self.message = message + self.details = details + + def as_dict(self) -> dict[str, Any]: + row: dict[str, Any] = {"code": self.code, "message": self.message} + if self.details is not None: + row["details"] = self.details + return row + + + def nfc(value: str) -> str: + return unicodedata.normalize("NFC", value) + + + def no_duplicates(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise S220Error("DUPLICATE_JSON_KEY", f"duplicate JSON key: {key}") + result[key] = value + return result + + + def load_json(raw: bytes, *, label: str) -> Any: + if len(raw) > MAX_FILE_BYTES: + raise S220Error("SOURCE_SIZE_LIMIT", f"file exceeds limit: {label}") + try: + text = raw.decode("utf-8", errors="strict") + value = json.loads( + text, + object_pairs_hook=no_duplicates, + parse_constant=lambda token: (_ for _ in ()).throw( + S220Error("NON_FINITE_NUMBER", f"non-finite number: {token}") + ), + ) + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise S220Error("JSON_PARSE_ERROR", f"strict JSON parse failed: {label}") from exc + return value + + + def canonical_bytes(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) + + "\n" + ).encode("utf-8") + + + def digest(value: Any) -> str: + payload = value if isinstance(value, bytes) else canonical_bytes(value) + return hashlib.sha256(payload).hexdigest() + + + def safe_path(value: Any, *, code: str = "PATH_INVALID") -> str: + if not isinstance(value, str) or not value or "\x00" in value: + raise S220Error(code, "path must be a non-empty string") + if value.startswith("/") or "\\" in value: + raise S220Error(code, "absolute or backslash path is forbidden") + normalized = PurePosixPath(value) + if any(part in {"", ".", ".."} for part in normalized.parts): + raise S220Error(code, "path traversal or ambiguous component") + return normalized.as_posix() + + + def join_path(*parts: str) -> str: + return safe_path("/".join(part.strip("/") for part in parts if part)) + + + def require_hex(value: Any, *, code: str) -> str: + if not isinstance(value, str) or HEX64.fullmatch(value) is None: + raise S220Error(code, "expected lower-case SHA-256") + return value + + + def stable_id(prefix: str, *parts: Any) -> str: + body = digest([nfc(str(part)) for part in parts])[:24] + return f"{prefix}-{body}" + + + def _parse_mcp_payload(raw: bytes, expected_id: int) -> Mapping[str, Any]: + content_type_json = raw.lstrip().startswith(b"{") + candidates: list[Any] = [] + if content_type_json: + candidates = [load_json(raw, label="mcp-response")] + else: + data_lines: list[str] = [] + events: list[bytes] = [] + for line in raw.decode("utf-8", errors="strict").splitlines(): + if not line: + if data_lines: + events.append("\n".join(data_lines).encode("utf-8")) + data_lines = [] + continue + if line.startswith((":", "event:", "id:", "retry:")): + continue + if not line.startswith("data:"): + raise S220Error("MCP_SSE_SHAPE", "unexpected SSE line") + data_lines.append(line[5:].lstrip()) + if data_lines: + events.append("\n".join(data_lines).encode("utf-8")) + candidates = [load_json(event, label="mcp-sse-event") for event in events] + matches = [row for row in candidates if isinstance(row, dict) and row.get("id") == expected_id] + if len(matches) != 1: + raise S220Error("MCP_RESPONSE_ID", "exactly one matching JSON-RPC result required") + row = matches[0] + if row.get("jsonrpc") != "2.0" or row.get("error") is not None: + raise S220Error("MCP_JSONRPC_ERROR", "MCP returned an invalid/error response") + if not isinstance(row.get("result"), dict): + raise S220Error("MCP_RESULT_SHAPE", "MCP result must be an object") + return row + + + def _tool_text(result: Mapping[str, Any], tool: str) -> str: + if result.get("isError") is True: + rendered = str(result.get("content", "")) + code = "LOCALDOCS_NOT_FOUND" if "not found" in rendered.lower() else "MCP_TOOL_ERROR" + raise S220Error(code, f"{tool} returned isError=true") + content = result.get("content") + if not isinstance(content, list) or len(content) != 1: + raise S220Error("MCP_CONTENT_CARDINALITY", "one content block required") + block = content[0] + if not isinstance(block, dict) or block.get("type") != "text" or not isinstance(block.get("text"), str): + raise S220Error("MCP_CONTENT_SHAPE", "one text block required") + return block["text"] + + + def _binary_from_text(text: str, path: str) -> bytes: + value = load_json(text.encode("utf-8"), label=path) + if isinstance(value, dict) and isinstance(value.get("results"), list): + rows = value["results"] + if len(rows) != 1 or not isinstance(rows[0], dict): + raise S220Error("LOCALDOCS_RESULT_CARDINALITY", "one binary result required") + value = rows[0].get("content", rows[0].get("text")) + if isinstance(value, str): + value = load_json(value.encode("utf-8"), label=path) + if not isinstance(value, dict) or not isinstance(value.get("content_base64"), str): + raise S220Error("LOCALDOCS_BINARY_ENVELOPE", "content_base64 is required") + try: + raw = base64.b64decode(value["content_base64"].encode("ascii"), validate=True) + except (UnicodeEncodeError, binascii.Error, ValueError) as exc: + raise S220Error("LOCALDOCS_BASE64", "strict base64 required") from exc + if value.get("byte_length", value.get("size", len(raw))) != len(raw): + raise S220Error("LOCALDOCS_LENGTH", f"byte length mismatch: {path}") + if value.get("sha256", digest(raw)) != digest(raw): + raise S220Error("LOCALDOCS_HASH", f"raw hash mismatch: {path}") + return raw + + + class McpClient: + def __init__(self, endpoint: str, name: str, *, client: Any | None = None) -> None: + try: + import httpx # type: ignore + except ImportError as exc: + raise S220Error("HTTPX_UNAVAILABLE", "httpx==0.28.1 is required") from exc + self.endpoint = endpoint + self.name = name + self.client = client or httpx.Client(timeout=60) + self.headers = {"Content-Type": "application/json", "Accept": "application/json, text/event-stream"} + self.ids = itertools.count(10) + self.initialized = False + + def close(self) -> None: + close = getattr(self.client, "close", None) + if callable(close): + close() + + def _post(self, body: Mapping[str, Any], expected_id: int | None) -> Mapping[str, Any] | None: + try: + response = self.client.post(self.endpoint, json=dict(body), headers=dict(self.headers)) + response.raise_for_status() + except Exception as exc: + raise S220Error("MCP_TRANSPORT", f"{self.name} transport failed") from exc + session = response.headers.get("mcp-session-id") + if session: + self.headers["mcp-session-id"] = session + if expected_id is None: + return None + return _parse_mcp_payload(bytes(response.content), expected_id) + + def initialize(self) -> None: + row = self._post( + { + "jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": { + "protocolVersion": MCP_PROTOCOL_VERSION, + "capabilities": {}, + "clientInfo": { + "name": "liti-s2-20-inline", "version": "1.0.0", + "user_id": INLINE_USER_HASH, "workspace_id": INLINE_WORKSPACE_HASH, + }, + }, + }, + 1, + ) + if row is None or row["result"].get("protocolVersion") != MCP_PROTOCOL_VERSION: + raise S220Error("MCP_PROTOCOL", "MCP protocol negotiation failed") + self._post({"jsonrpc": "2.0", "method": "notifications/initialized"}, None) + self.initialized = True + + def call(self, tool: str, arguments: Mapping[str, Any]) -> Mapping[str, Any]: + if not self.initialized: + raise S220Error("MCP_NOT_INITIALIZED", "initialize before tools/call") + message_id = next(self.ids) + row = self._post( + {"jsonrpc": "2.0", "id": message_id, "method": "tools/call", "params": {"name": tool, "arguments": dict(arguments)}}, + message_id, + ) + if row is None: + raise S220Error("MCP_EMPTY", f"empty response: {tool}") + return row["result"] + + def read_binary(self, path: str) -> bytes: + safe = safe_path(path) + return _binary_from_text(_tool_text(self.call("read_binary_doc", {"doc_name": safe}), "read_binary_doc"), safe) + + def read_optional(self, path: str) -> bytes | None: + try: + return self.read_binary(path) + except S220Error as exc: + if exc.code == "LOCALDOCS_NOT_FOUND": + return None + raise + + def write_immutable(self, path: str, payload: bytes) -> str: + safe = safe_path(path) + existing = self.read_optional(safe) + if existing is not None: + if existing != payload: + raise S220Error("IMMUTABLE_CONFLICT", f"existing bytes differ: {safe}") + return digest(existing) + encoded = base64.b64encode(payload).decode("ascii") + _tool_text( + self.call("write_binary_file", {"path": safe, "content_base64": encoded, "overwrite": False}), + "write_binary_file", + ) + observed = self.read_binary(safe) + if observed != payload: + raise S220Error("WRITE_READBACK_MISMATCH", f"read-back differs: {safe}") + return digest(observed) + + + REQUEST_KEYS = { + "schema_version", "request_id", "attempt_id", "run_binding_digest", + "user_identity_hash", "workspace_identity_hash", "stage2_run_root_ref", + "plan_output_root_ref", "host_cas_receipt_ref", "host_cas_receipt_sha256", + "detached_admission_receipt_ref", "detached_admission_receipt_sha256", + "outer_execution_receipt_target_ref", "expected_s2_10_publish_status_sha256", + "expected_parent_stage2_release_sha256", "expected_s2_10_release_sha256", + "expected_s2_20_agent_sha256", "expected_s2_20_executor_binding_sha256", + } + + + def validate_request(raw: bytes) -> dict[str, Any]: + value = load_json(raw, label=REQUEST_PATH) + if not isinstance(value, dict) or set(value) != REQUEST_KEYS: + keys = set(value) if isinstance(value, dict) else set() + raise S220Error("REQUEST_CLOSED_SHAPE", "request keys differ", details={"missing": sorted(REQUEST_KEYS - keys), "extra": sorted(keys - REQUEST_KEYS)}) + if value["schema_version"] != "stage2_s2_20_request.v1": + raise S220Error("REQUEST_VERSION", "unsupported request schema") + for key in ("request_id", "attempt_id"): + if not isinstance(value[key], str) or IDENT.fullmatch(value[key]) is None: + raise S220Error("REQUEST_ID_INVALID", f"invalid {key}") + for key in ( + "run_binding_digest", "user_identity_hash", "workspace_identity_hash", + "host_cas_receipt_sha256", "expected_s2_10_publish_status_sha256", + "expected_parent_stage2_release_sha256", "expected_s2_10_release_sha256", + "expected_s2_20_agent_sha256", "expected_s2_20_executor_binding_sha256", + ): + require_hex(value[key], code=f"{key.upper()}_INVALID") + require_hex(value["detached_admission_receipt_sha256"], code="ADMISSION_HASH_INVALID") + run = value["run_binding_digest"] + expected_root = f"stage2_runs/by-binding/{run}" + if value["stage2_run_root_ref"] != expected_root or value["plan_output_root_ref"] != f"{expected_root}/plan": + raise S220Error("REQUEST_ROOT_BINDING", "request/output roots must derive from run binding") + if value["user_identity_hash"] != INLINE_USER_HASH or value["workspace_identity_hash"] != INLINE_WORKSPACE_HASH: + raise S220Error("WORKSPACE_IDENTITY_MISMATCH", "request identity differs from Agent substitution") + exact_paths = { + "host_cas_receipt_ref": f"stage2_control/host_cas/{run}/S2_20.json", + "detached_admission_receipt_ref": f"{ASSET_ROOT}/manifest/stage2_deterministic_admission_receipt.json", + "outer_execution_receipt_target_ref": f"{expected_root}/control/s2_20_outer_execution_receipt.json", + } + for key, expected in exact_paths.items(): + safe_path(value[key]) + if value[key] != expected: + raise S220Error("REQUEST_TRUST_ANCHOR_PATH", f"{key} must equal its canonical run-bound path") + return value + + + def read_stable(localdocs: McpClient, paths: Iterable[str]) -> tuple[dict[str, bytes], str]: + ordered = sorted(set(safe_path(path) for path in paths)) + first: dict[str, bytes] = {} + total = 0 + for path in ordered: + raw = localdocs.read_binary(path) + total += len(raw) + if total > MAX_TOTAL_BYTES: + raise S220Error("TOTAL_READ_LIMIT", "input hydration exceeds total limit") + first[path] = raw + second = {path: localdocs.read_binary(path) for path in ordered} + if any(first[path] != second[path] for path in ordered): + raise S220Error("INPUT_TOCTOU", "input changed between bounded reads") + rows = [[path, digest(first[path]), len(first[path])] for path in ordered] + return first, digest(rows) + + + def option_disposition( + option: Mapping[str, Any], policy: Mapping[str, Any] + ) -> tuple[str, list[str]]: + decision = option.get("decision") + client = option.get("client_disposition") + relation = option.get("relation") + if policy.get("policy_status") == "APPROVED": + observed = { + "legal_decision": decision, + "client_instruction": "NO_SUE" if client == "DEFERRED_BY_CLIENT_INSTRUCTION" else "UNSPECIFIED", + "precondition_status": "UNRESOLVED" if option.get("precondition_refs") or relation == "precondition_candidate" else "NOT_APPLICABLE", + "compatibility_status": "INCOMPATIBLE" if option.get("incompatible_with") else "COMPATIBLE", + } + matches = [ + row for row in policy.get("rules", []) + if isinstance(row, dict) + and all(row.get(key) == value for key, value in observed.items()) + ] + if len(matches) != 1: + raise S220Error( + "DISPOSITION_POLICY_CONFLICT" if matches else "DISPOSITION_POLICY_GAP", + "approved option-disposition policy must match exactly one row", + ) + result = matches[0].get("result_disposition") + if result not in {"SELECTED", "ALTERNATIVE", "EXCLUDED", "DEFERRED"}: + raise S220Error("DISPOSITION_POLICY_GAP", "policy row cannot authorize a disposition") + return str(result).lower(), [str(matches[0].get("reason_code"))] + reason_codes = ["DISPOSITION_POLICY_NOT_ADMITTED"] + if decision == "EXCLUDED": + return "excluded", reason_codes + if client == "DEFERRED_BY_CLIENT_INSTRUCTION": + return "deferred", reason_codes + if relation in {"alternative", "precondition_candidate", "accessory"}: + return "alternative", reason_codes + if decision in {"SUPPORTED", "CONDITIONAL", "UNRESOLVED"}: + return "selected", reason_codes + raise S220Error("DISPOSITION_POLICY_GAP", "claim option has no closed disposition") + + + def project_signature( + source: Mapping[str, Any], + c20_projection: Mapping[str, Any], + c21_projection: Mapping[str, Any], + remedy_context: Mapping[str, Any], + projection_registry: Mapping[str, Any], + ) -> dict[str, Any]: + """Closed S2_10+C20+C21+remedy projection used by C25. + + No label, free text, filename, retrieval result, or model output may + supplement these explicit typed inputs. A missing/ambiguous value + remains UNKNOWN and therefore cannot produce an exact case binding. + """ + expected_source_fields = { + "vocabulary_version", "legal_basis_family", "right_holder_refs", + "obligor_refs", "performance_kind", "object_refs", "legal_effect", + "source_occurrence_refs", + } + if set(source) != expected_source_fields: + raise S220Error( + "BINDING_SIGNATURE_SOURCE_SHAPE", + "normalized claim signature must use the closed S2_10 vocabulary", + details={ + "missing": sorted(expected_source_fields - set(source)), + "extra": sorted(set(source) - expected_source_fields), + }, + ) + if source.get("vocabulary_version") != "stage2.claim_signature_vocab.v1": + raise S220Error("BINDING_SIGNATURE_SOURCE_VERSION", "unsupported source vocabulary") + source_values: dict[str, Any] = dict(source) + source_values.update( + { + "c20_remedy_projection": remedy_context.get("remedy_mode"), + "c20_dependency_projection": c20_projection.get("dependency_kind"), + "c21_party_projection": c21_projection.get("party_role_pattern"), + "c21_object_projection": c21_projection.get("object_kind"), + } + ) + projected: dict[str, Any] = { + key: ([] if key == "special_statute_tags" else "UNKNOWN") + for key in TARGET_SIGNATURE_FIELDS + } + rules = projection_registry.get("projection_rules", []) + rule_ids: list[str] = [] + provenance: list[dict[str, Any]] = [] + if projection_registry.get("registry_status") == "APPROVED" and isinstance(rules, list): + by_target: dict[str, Mapping[str, Any]] = {} + for rule in rules: + if not isinstance(rule, dict) or rule.get("target_field") not in TARGET_SIGNATURE_FIELDS: + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_INVALID", "closed projection row required") + target_field = str(rule["target_field"]) + if target_field in by_target: + raise S220Error("BINDING_SIGNATURE_PROJECTION_MULTI_RULE", target_field) + by_target[target_field] = rule + if set(by_target) != set(TARGET_SIGNATURE_FIELDS): + raise S220Error("BINDING_SIGNATURE_PROJECTION_COVERAGE", "all target fields require one rule") + for target_field in TARGET_SIGNATURE_FIELDS: + rule = by_target[target_field] + rule_id = str(rule.get("projection_rule_id", "")) + source_fields = rule.get("source_fields", []) + allowed = rule.get("allowed_output_values", []) + operator = rule.get("derivation_operator") + if not rule_id or not isinstance(source_fields, list) or not isinstance(allowed, list): + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_INVALID", target_field) + if rule.get("implementation_status") != "IMPLEMENTED" or rule.get("semantic_review_status") != "APPROVED": + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_NOT_ADMITTED", target_field) + candidates: list[str] = [] + for field in source_fields: + value = source_values.get(str(field)) + values = value if isinstance(value, list) else [value] + candidates.extend(nfc(item) for item in values if isinstance(item, str) and item) + permitted = sorted({item for item in candidates if item in allowed}) + if operator in {"DIRECT_COPY", "CLOSED_LOOKUP"} and len(permitted) == 1: + projected[target_field] = permitted[0] + elif operator == "SET_UNION" and target_field == "special_statute_tags": + projected[target_field] = permitted + elif operator == "SET_INTERSECTION" and target_field == "special_statute_tags": + projected[target_field] = permitted if permitted and len(permitted) == len(set(candidates)) else [] + rule_ids.append(rule_id) + provenance.append( + { + "target_field": target_field, + "value": projected[target_field], + "source_paths": sorted(str(item) for item in source_fields), + "status": "DERIVED" if projected[target_field] not in ("UNKNOWN", []) else "UNKNOWN", + } + ) + if not rule_ids: + rule_ids = ["PENDING_BINDING_SIGNATURE_PROJECTION"] + provenance = [ + { + "target_field": target_field, + "value": projected[target_field], + "source_paths": [], + "status": "UNKNOWN", + } + for target_field in TARGET_SIGNATURE_FIELDS + ] + return { + "schema_version": "stage2.binding_target_signature.v1", + "source_vocabulary_version": source["vocabulary_version"], + "target_vocabulary_version": projection_registry.get("target_vocabulary_version", "stage2.case_type_signature_vocab.v1"), + "projection_rule_ids": sorted(rule_ids), + "source_signature_refs": sorted(str(item) for item in source.get("source_occurrence_refs", []) if isinstance(item, str)), + "target_signature": projected, + "target_signature_sha256": digest(projected), + "signature": projected, + "signature_sha256": digest(projected), + "provenance": provenance, + } + + + def eval_predicate(signature: Mapping[str, Any], predicate: Mapping[str, Any]) -> bool: + operator = predicate.get("operator") + field = predicate.get("field") + if operator not in {"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"} or field not in TARGET_SIGNATURE_FIELDS: + raise S220Error("PREDICATE_GRAMMAR", "unsupported predicate operator/field") + actual = signature.get(str(field), "UNKNOWN") + expected = predicate.get("value") + if operator == "EXISTS": + if not isinstance(expected, bool): + raise S220Error("PREDICATE_GRAMMAR", "EXISTS requires a boolean value") + return (actual not in (None, "UNKNOWN", [])) is expected + if operator == "EQ": + return actual == expected + if operator == "IN": + return actual in (expected if isinstance(expected, list) else []) + actual_set = set(actual if isinstance(actual, list) else [actual]) + expected_set = set(expected if isinstance(expected, list) else []) + return expected_set <= actual_set if operator == "ALL_OF" else actual_set.isdisjoint(expected_set) + + + def eval_closed_predicate( + signature: Mapping[str, Any], predicate: Mapping[str, Any] + ) -> bool: + if predicate.get("legal_review_status") != "APPROVED": + return False + clauses = predicate.get("clauses") + if not isinstance(clauses, list) or not clauses or any(not isinstance(item, dict) for item in clauses): + raise S220Error("PREDICATE_GRAMMAR", "approved predicate requires non-empty clauses") + return all(eval_predicate(signature, item) for item in clauses) + + + def bind_case( + signature: Mapping[str, Any], registry: Mapping[str, Any] + ) -> tuple[str | None, str, list[str], list[str]]: + rows = registry.get("rows", registry.get("case_types", [])) + matches: list[tuple[str, str]] = [] + for row in rows if isinstance(rows, list) else []: + if not isinstance(row, dict): + continue + if row.get("row_kind") not in {"CLAIM", "GENERIC"}: + continue + if row.get("claim_capable_disposition") != "CLAIM_CAPABLE" or row.get("legal_classification_status") != "APPROVED": + continue + predicates = row.get("case_type_predicates", []) + if ( + isinstance(predicates, list) + and len(predicates) == 1 + and isinstance(predicates[0], dict) + and eval_closed_predicate(signature, predicates[0]) + ): + matches.append((str(row.get("case_type_id")), str(predicates[0].get("predicate_id")))) + matches = sorted(set(matches)) + if len(matches) == 1: + return matches[0][0], "BOUND", [], [matches[0][1]] + if not matches: + return None, "UNRESOLVED", ["CASE_TYPE_BINDING_ZERO_MATCH"], [] + return None, "CONFLICT", ["CASE_TYPE_BINDING_MULTI_MATCH"], [] + + + def bind_rule( + case_type_id: str | None, + signature: Mapping[str, Any], + rule_registry: Mapping[str, Any], + ) -> tuple[dict[str, Any] | None, list[str], list[str]]: + if case_type_id is None: + return None, ["SUB_RULE_BINDING_ZERO_MATCH"], [] + registry_rows = rule_registry.get("rows", rule_registry.get("case_type_rule_rows", rule_registry.get("rule_bindings", []))) + rows = [row for row in registry_rows if isinstance(row, dict) and row.get("case_type_id") == case_type_id] + branches: list[dict[str, Any]] = [] + for row in rows: + row_branches = row.get("rule_branches", row.get("sub_rule_bindings", [])) + for branch in row_branches if isinstance(row_branches, list) else []: + if not isinstance(branch, dict) or branch.get("legal_content_status") != "APPROVED": + continue + predicates = branch.get("sub_rule_predicates", []) + if ( + isinstance(predicates, list) + and len(predicates) == 1 + and isinstance(predicates[0], dict) + and eval_closed_predicate(signature, predicates[0]) + ): + branches.append(branch) + if len(branches) == 1: + branch = branches[0] + predicate_ids = [str(item.get("predicate_id")) for item in branch.get("sub_rule_predicates", []) if isinstance(item, dict) and item.get("predicate_id")] + return branch, [], sorted(predicate_ids or [str(branch.get("predicate_id", "UNKNOWN"))]) + if len(branches) > 1: + return None, ["SUB_RULE_BINDING_MULTI_MATCH"], [] + return None, ["RULE_BRANCH_MISSING"], [] + + + def _source_ref(path: str, sha256: str, pointer: str = "") -> dict[str, str]: + return {"path": path, "sha256": sha256, "json_pointer": pointer} + + + def validate_rule_foreign_keys( + case_type_id: str, + branch: Mapping[str, Any], + assets: Mapping[str, Any], + ) -> tuple[list[str], dict[str, list[dict[str, str]]]]: + """C26 exact FK/hash/status closure for an otherwise matched branch.""" + codes: list[str] = [] + refs: dict[str, list[dict[str, str]]] = { + "structured_relief_rule_refs": [], + "markdown_source_refs": [], + "renderer_refs": [], + "party_set_rule_refs": [], + "ce13_branch_refs": [], + "review_policy_refs": [], + } + legal_bytes = assets.get("release_selected_legal_bytes", {}) + markdown_path = branch.get("markdown_source_path") + markdown_hash = branch.get("markdown_source_sha256") + if ( + not isinstance(markdown_path, str) + or not isinstance(markdown_hash, str) + or not isinstance(legal_bytes, dict) + or markdown_path not in legal_bytes + or digest(legal_bytes[markdown_path]) != markdown_hash + ): + codes.append("RELIEF_RULE_SOURCE_HASH_MISMATCH") + else: + refs["markdown_source_refs"].append(_source_ref(markdown_path, markdown_hash)) + receipt_path = branch.get("legal_review_receipt_ref") + receipt_hash = branch.get("legal_review_receipt_sha256") + if ( + not isinstance(receipt_path, str) + or not isinstance(receipt_hash, str) + or receipt_path not in legal_bytes + or digest(legal_bytes[receipt_path]) != receipt_hash + ): + codes.append("RULE_BRANCH_LEGAL_RECEIPT_HASH_MISMATCH") + + relief_rules = assets.get("relief_rules", {}) + relief_rows = relief_rules.get("rule_branches", []) if isinstance(relief_rules, dict) else [] + relief_matches = [ + row for row in relief_rows + if isinstance(row, dict) + and row.get("case_type_id") == case_type_id + and row.get("sub_rule_id") == branch.get("sub_rule_id") + and row.get("rule_branch_key", f"{case_type_id}::{row.get('sub_rule_id')}") == branch.get("rule_branch_key") + ] + if ( + not isinstance(relief_rules, dict) + or relief_rules.get("execution_eligible") is not True + or relief_rules.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(relief_matches) != 1 + ): + codes.append("STRUCTURED_RELIEF_RULE_FK_UNRESOLVED") + else: + refs["structured_relief_rule_refs"].append( + _source_ref( + "registry/drafting/case_type_relief_rules.yml", + str(assets["asset_hashes"]["relief_rules"]), + f"/rule_branches/{relief_rows.index(relief_matches[0])}", + ) + ) + + renderer_descriptors = assets.get("renderer_descriptors", {}) + renderer_hashes = assets.get("renderer_hashes", {}) + for renderer_id in branch.get("renderer_ids", []): + descriptor = renderer_descriptors.get(renderer_id) if isinstance(renderer_descriptors, dict) else None + renderer_hash = renderer_hashes.get(renderer_id) if isinstance(renderer_hashes, dict) else None + if ( + not isinstance(descriptor, dict) + or descriptor.get("renderer_id") != renderer_id + or descriptor.get("execution_eligible") is not True + or descriptor.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or not isinstance(renderer_hash, str) + ): + codes.append("RENDERER_FK_UNRESOLVED") + else: + renderer_paths = { + "R01": "renderers/R01_money_payment.yml", + "R02": "renderers/R02_delivery_possession.yml", + "R03": "renderers/R03_declaration_registry.yml", + "R04": "renderers/R04_special_nonmoney_performance.yml", + "R05": "renderers/R05_declaratory.yml", + "R06": "renderers/R06_constitutive_judgment_challenge.yml", + } + refs["renderer_refs"].append(_source_ref(renderer_paths[renderer_id], renderer_hash)) + + party_registry = assets.get("party_rules", {}) + party_rows = party_registry.get("rules", []) if isinstance(party_registry, dict) else [] + party_matches = [ + row for row in party_rows + if isinstance(row, dict) + and row.get("party_set_rule_id") == branch.get("party_set_rule_id") + and row.get("legal_content_status") == "APPROVED" + ] + if party_registry.get("registry_status") != "APPROVED" or len(party_matches) != 1: + codes.append("PARTY_SET_RULE_FK_UNRESOLVED") + else: + refs["party_set_rule_refs"].append( + _source_ref( + "registry/party/party_set_rules.yml", + str(assets["asset_hashes"]["party_rules"]), + f"/rules/{party_rows.index(party_matches[0])}", + ) + ) + + scope_registry = assets.get("corpus_scope", {}) + scope_rows = scope_registry.get("rows", []) if isinstance(scope_registry, dict) else [] + scope_matches = [ + row for row in scope_rows + if isinstance(row, dict) + and row.get("corpus_scope_id") == branch.get("corpus_scope_id") + and row.get("case_type_id") == case_type_id + and branch.get("sub_rule_id") in row.get("sub_rule_ids", []) + and row.get("element_set_id") == branch.get("element_set_id") + and row.get("approval_status") == "APPROVED" + ] + if scope_registry.get("registry_status") != "APPROVED" or len(scope_matches) != 1: + codes.append("CORPUS_SCOPE_FK_UNRESOLVED") + + ce13 = assets.get("calculator_descriptors", {}).get("CE-13") + ce13_rows = ce13.get("formula_rows", []) if isinstance(ce13, dict) else [] + ce13_matches = [ + row for row in ce13_rows + if isinstance(row, dict) + and row.get("formula_id") == branch.get("ce13_branch_id") + ] + if ( + not isinstance(ce13, dict) + or ce13.get("execution_eligible") is not True + or ce13.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(ce13_matches) != 1 + ): + codes.append("CE13_BRANCH_FK_UNRESOLVED") + else: + refs["ce13_branch_refs"].append( + _source_ref( + "registry/calculations/CE-13_court_value_cost.yml", + str(assets["calculator_descriptor_hashes"]["CE-13"]), + f"/formula_rows/{ce13_rows.index(ce13_matches[0])}", + ) + ) + + review_registry = assets.get("review_policy", {}) + policy_rows = review_registry.get("policy_rows", []) if isinstance(review_registry, dict) else [] + policy_matches = [ + row for row in policy_rows + if isinstance(row, dict) and row.get("review_policy_id", row.get("policy_id")) == branch.get("review_policy_id") + ] + if ( + not isinstance(review_registry, dict) + or review_registry.get("execution_eligible") is not True + or review_registry.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(policy_matches) != 1 + ): + codes.append("REVIEW_POLICY_FK_UNRESOLVED") + else: + refs["review_policy_refs"].append( + _source_ref( + "registry/review/review_policy_registry.yml", + str(assets["asset_hashes"]["review_policy"]), + f"/policy_rows/{policy_rows.index(policy_matches[0])}", + ) + ) + return sorted(set(codes)), { + key: sorted(value, key=lambda row: (row["path"], row["json_pointer"])) + for key, value in refs.items() + } + + + def build_portfolio( + verdicts: Sequence[Mapping[str, Any]], + verdict_sources: Mapping[str, Mapping[str, str]], + disposition_policy: Mapping[str, Any], + ) -> tuple[list[dict[str, Any]], dict[str, list[str]], list[dict[str, Any]], list[dict[str, Any]]]: + atomic: list[dict[str, Any]] = [] + partitions = {name: [] for name in DISPOSITIONS} + dispositions: list[dict[str, Any]] = [] + option_to_group: dict[str, str] = {} + relation_rows: list[tuple[str, str, str]] = [] + for verdict in verdicts: + cluster_id = str(verdict.get("cluster_id", "UNKNOWN")) + source = verdict_sources.get(cluster_id, {}) + options = verdict.get("claim_options", []) + for option_index, option in enumerate(options if isinstance(options, list) else []): + if not isinstance(option, dict): + continue + option_id = str(option.get("claim_option_id", "")) + if not option_id: + raise S220Error("CLAIM_OPTION_ID_MISSING", "canonical option ID is required") + disposition, disposition_reason_codes = option_disposition(option, disposition_policy) + partitions[disposition].append(option_id) + active = disposition in {"selected", "alternative"} + atomic_id = stable_id("AC", option_id) if active else None + group_id = stable_id("CG", option.get("same_underlying_recovery_key") or option_id) if active else None + if active and atomic_id is not None and group_id is not None: + option_to_group[option_id] = group_id + signature = option.get("normalized_claim_signature", {}) + source_path = source.get("path", f"map_s2_10/domain_verdicts/{cluster_id}.json") + source_sha = source.get("sha256", digest(verdict)) + atomic.append( + { + "atomic_claim_id": atomic_id, + "claim_option_id": option_id, + "claim_group_id": group_id, + "cluster_id": cluster_id, + "disposition": disposition.upper(), + "legal_decision": option.get("decision"), + "relation": option.get("relation"), + "client_disposition": option.get("client_disposition"), + "client_instruction_refs": sorted(str(item) for item in option.get("client_instruction_refs", []) if isinstance(item, str)), + "right_holder_refs": sorted(str(item) for item in signature.get("right_holder_refs", []) if isinstance(item, str)), + "obligor_refs": sorted(str(item) for item in signature.get("obligor_refs", []) if isinstance(item, str)), + "object_refs": sorted(str(item) for item in signature.get("object_refs", []) if isinstance(item, str)), + "same_underlying_recovery_key": option.get("same_underlying_recovery_key"), + "incompatible_with": sorted(str(item) for item in option.get("incompatible_with", []) if isinstance(item, str)), + "precondition_refs": sorted(str(item) for item in option.get("precondition_refs", []) if isinstance(item, str)), + "element_statuses": option.get("element_statuses", []), + "defense_statuses": option.get("defense_statuses", []), + "remedy_candidates": option.get("remedy_candidates", []), + "normalized_claim_signature": signature, + "c20_projection": { + "dependency_kind": option.get("relation", "UNKNOWN"), + "incompatible_option_ids": sorted(str(item) for item in option.get("incompatible_with", []) if isinstance(item, str)), + "precondition_option_ids": sorted(str(item) for item in option.get("precondition_refs", []) if isinstance(item, str)), + }, + "normalized_signature_sha256": digest(signature), + "source_refs": [{ + "source_artifact_path": source_path, + "source_artifact_sha256": source_sha, + "json_pointer": f"/claim_options/{option_index}", + }], + "typed_provenance": sorted( + [item for item in option.get("typed_provenance", []) if isinstance(item, dict)], + key=digest, + ), + "source_option_sha256": digest(option), + } + ) + relation_rows.extend((option_id, target, "INCOMPATIBLE") for target in option.get("incompatible_with", []) if isinstance(target, str)) + relation_rows.extend((option_id, target, "PRECONDITION") for target in option.get("precondition_refs", []) if isinstance(target, str)) + dispositions.append( + { + "schema_version": "stage2_option_disposition.v1", + "claim_option_id": option_id, + "source_cluster_id": cluster_id, + "source_domain_verdict_sha256": source.get("sha256", digest(verdict)), + "legal_decision": option.get("decision"), + "client_instruction": "NO_SUE" if option.get("client_disposition") == "DEFERRED_BY_CLIENT_INSTRUCTION" else "UNSPECIFIED", + "disposition": disposition.upper(), + "reason_codes": sorted(set(disposition_reason_codes + [f"S2_10_{str(option.get('decision', 'UNKNOWN'))}", f"RELATION_{str(option.get('relation', 'UNKNOWN')).upper()}"])), + "atomic_claim_ids": [atomic_id] if atomic_id is not None else [], + "claim_group_id": group_id, + "issue_refs": [], + } + ) + for values in partitions.values(): + values.sort() + seen = [item for values in partitions.values() for item in values] + if len(seen) != len(set(seen)) or set(seen) != {row["claim_option_id"] for row in dispositions}: + raise S220Error("OPTION_CONSERVATION", "disposition partition is not exact") + dependencies: list[dict[str, Any]] = [] + for source_option, target_option, relation in sorted(set(relation_rows)): + source_group = option_to_group.get(source_option) + target_group = option_to_group.get(target_option) + if source_group is None or target_group is None or source_group == target_group: + continue + dependencies.append( + { + "source_group_id": source_group, + "target_group_id": target_group, + "relation": relation, + "source_option_relation_refs": [f"{source_option}->{target_option}"], + } + ) + return ( + sorted(atomic, key=lambda row: row["atomic_claim_id"]), + partitions, + sorted(dispositions, key=lambda row: row["claim_option_id"]), + sorted(dependencies, key=digest), + ) + + + def build_party_projection( + party_context: Mapping[str, Any], object_registry: Mapping[str, Any] + ) -> dict[str, Any]: + """C21 facts-only projection; no required-party legal conclusion.""" + parties = party_context.get("parties", []) + party_refs = sorted( + str(row.get("party_identity", {}).get("party_context_id")) + for row in parties + if isinstance(row, dict) + and isinstance(row.get("party_identity"), dict) + and row["party_identity"].get("party_context_id") + ) + identity_issues = sorted( + str(row.get("party_identity", {}).get("party_context_id")) + for row in parties + if isinstance(row, dict) + and isinstance(row.get("party_identity"), dict) + and row["party_identity"].get("identity_disposition") == "IDENTITY_UNRESOLVED" + ) + objects = object_registry.get("objects", []) + object_refs = sorted( + str(row.get("object_context_id")) + for row in objects + if isinstance(row, dict) and row.get("object_context_id") + ) + object_identity_issues = sorted( + str(row.get("object_context_id")) + for row in objects + if isinstance(row, dict) + and row.get("object_context_id") + and row.get("identity_disposition") == "IDENTITY_UNRESOLVED" + ) + present_role_tokens = sorted( + { + nfc(str(row[key])) + for row in parties + if isinstance(row, dict) + for key in ("party_title", "defendant_role") + if isinstance(row.get(key), str) and row.get(key) + } + ) + return { + "schema_version": "stage2_party_projection.v1", + "party_refs": party_refs, + "object_refs": object_refs, + "title_chain_refs": [], + "identity_issue_refs": sorted(set(identity_issues + object_identity_issues)), + "present_role_tokens": present_role_tokens, + "legal_required_party_determination_made": False, + } + + + def claim_c21_projection( + claim: Mapping[str, Any], party_projection: Mapping[str, Any] + ) -> dict[str, str]: + known_parties = set(str(item) for item in party_projection.get("party_refs", [])) + known_objects = set(str(item) for item in party_projection.get("object_refs", [])) + holders = set(str(item) for item in claim.get("right_holder_refs", [])) + obligors = set(str(item) for item in claim.get("obligor_refs", [])) + objects = set(str(item) for item in claim.get("object_refs", [])) + if holders and obligors and holders | obligors <= known_parties: + party_role_pattern = f"RH_{len(holders)}_OB_{len(obligors)}" + else: + party_role_pattern = "UNKNOWN" + if not objects: + object_kind = "NO_OBJECT_REF" + elif objects <= known_objects: + object_kind = "IDENTIFIED_OBJECT_SET" + else: + object_kind = "UNKNOWN" + return { + "party_role_pattern": party_role_pattern, + "object_kind": object_kind, + } + + + def calculate_all( + atomic: Sequence[Mapping[str, Any]], + bindings: Sequence[Mapping[str, Any]], + activation_registry: Mapping[str, Any], + descriptor_hashes: Mapping[str, str], + descriptor_objects: Mapping[str, Mapping[str, Any]], + law_value_tables: Sequence[Mapping[str, Any]], + request: Mapping[str, Any], + ) -> list[dict[str, Any]]: + """C22 closed dispatcher and Decimal formula interpreter. + + The present distribution contains no admitted legal formula rows. + This implementation nevertheless closes the admitted branch: an exact + activation row and release-selected descriptor are required, every + absent operand/law value remains explicit, and no numeric fallback is + permitted. + """ + descriptors = activation_registry.get("calculator_descriptors", []) + calculator_ids = sorted( + str(row["calculator_id"]) + for row in descriptors + if isinstance(row, dict) and isinstance(row.get("calculator_id"), str) + ) + descriptor_refs = { + str(row["calculator_id"]): row + for row in descriptors + if isinstance(row, dict) and isinstance(row.get("calculator_id"), str) + } + activation_rows = activation_registry.get("activation_rows", []) + if not isinstance(activation_rows, list): + raise S220Error("CALCULATION_ACTIVATION_ROWS_INVALID", "activation_rows must be an array") + registry_ready = ( + activation_registry.get("execution_eligible") is True + and activation_registry.get("status") == "LEGAL_CONTENT_ADMITTED" + ) + binding_by_atomic = { + str(row["atomic_claim_id"]): row + for row in bindings + if isinstance(row, dict) and isinstance(row.get("atomic_claim_id"), str) + } + law_values: dict[str, Mapping[str, Any]] = {} + for table in law_value_tables: + if not isinstance(table, Mapping) or table.get("release_eligible") is not True: + continue + for token in table.get("tokens", []): + if not isinstance(token, Mapping) or token.get("review_status") != "VERIFIED": + continue + law_value_id = token.get("law_value_id") + if isinstance(law_value_id, str) and law_value_id: + if law_value_id in law_values: + raise S220Error("LAW_VALUE_DUPLICATE", f"duplicate law value: {law_value_id}") + law_values[law_value_id] = token + + rounding_modes = { + "ROUND_HALF_UP": ROUND_HALF_UP, + "ROUND_DOWN": ROUND_DOWN, + "ROUND_UP": ROUND_UP, + "TRUNCATE": ROUND_DOWN, + } + + def decimal_value(value: Any) -> Decimal: + try: + return Decimal(str(value)) + except (InvalidOperation, ValueError) as exc: + raise S220Error("INVALID_DECIMAL_OPERAND", "operand is not an exact decimal") from exc + + def claim_metrics(claim: Mapping[str, Any]) -> list[str]: + values: set[str] = set() + for remedy in claim.get("remedy_candidates", []): + if not isinstance(remedy, Mapping): + continue + if isinstance(remedy.get("remedy_kind"), str): + values.add(str(remedy["remedy_kind"])) + values.update( + str(item) + for item in remedy.get("condition_codes", []) + if isinstance(item, str) and item + ) + return sorted(values) + + def row_matches( + activation_row: Mapping[str, Any], + claim: Mapping[str, Any], + binding: Mapping[str, Any], + requested_metrics: Sequence[str], + ) -> bool: + row_metrics = activation_row.get("requested_metrics", []) + equals = activation_row.get("equals", {}) + if not isinstance(row_metrics, list) or not isinstance(equals, Mapping): + raise S220Error("INVALID_ACTIVATION_ROW", "activation row shape is invalid") + if not set(str(item) for item in row_metrics) & set(requested_metrics): + return False + actual = { + "normalized_claim_signature": str(claim.get("normalized_signature_sha256", "")), + "case_type_id": str(binding.get("case_type_id", "")), + "rule_branch_key": str(binding.get("rule_branch_key", "")), + } + for key, expected in equals.items(): + if key == "requested_metric": + if str(expected) not in requested_metrics: + return False + elif key not in actual or actual[key] != str(expected): + return False + return True + + def empty_receipt( + claim: Mapping[str, Any], calculator_id: str, status: str, issues: Sequence[str] + ) -> dict[str, Any]: + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": stable_id("CR", claim["atomic_claim_id"], calculator_id), + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": claim["atomic_claim_id"], + "claim_group_id": claim["claim_group_id"], + "calculator_id": calculator_id, + "activation_status": status, + "calculation_status": status, + "activation_row_id": None, + "descriptor_sha256": descriptor_hashes.get(calculator_id), + "formula_id": None, + "formula_sha256": None, + "operand_rows": [], + "operand_refs": [], + "units": [], + "law_value_uses": [], + "effective_interval": None, + "rounding_rule": None, + "period_rows": [], + "missing_operands": [], + "result": None, + "result_sha256": None, + "issue_codes": sorted(set(str(item) for item in issues)), + } + return {**core, "receipt_sha256": digest(core)} + + def operand_row(operand_id: str, unit_id: str) -> dict[str, Any]: + return { + "operand_id": operand_id, + "source_ref": f"UNRESOLVED_STAGE1_CALCULATION_REQUEST:{operand_id}", + "status": "MISSING", + "value_type": "DECIMAL", + "value": None, + "unit_id": unit_id, + "currency": "KRW" if unit_id == "KRW" else None, + "value_sha256": None, + } + + receipts: list[dict[str, Any]] = [] + for row in sorted(atomic, key=lambda item: str(item["atomic_claim_id"])): + binding = binding_by_atomic.get(str(row["atomic_claim_id"]), {}) + requested_metrics = claim_metrics(row) + for calculator_id in calculator_ids: + if not registry_ready: + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_ACTIVATION_REGISTRY_NOT_ADMITTED"])) + continue + candidate_rows = [ + item for item in activation_rows + if isinstance(item, Mapping) and item.get("calculator_id") == calculator_id + ] + matches = [ + item for item in candidate_rows + if row_matches(item, row, binding, requested_metrics) + ] + if len(matches) > 1: + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_CONFLICT", ["CALCULATION_ACTIVATION_CONFLICT"])) + continue + if not matches: + relevant_metrics = { + str(metric) + for item in candidate_rows + for metric in item.get("requested_metrics", []) + if isinstance(metric, str) + } + if relevant_metrics & set(requested_metrics): + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_ACTIVATION_GAP"])) + else: + receipts.append(empty_receipt(row, calculator_id, "NOT_APPLICABLE", [])) + continue + activation_row = matches[0] + activation_row_id = activation_row.get("activation_row_id") + descriptor_ref = descriptor_refs.get(calculator_id, {}) + descriptor = descriptor_objects.get(calculator_id, {}) + if ( + not isinstance(activation_row_id, str) + or descriptor_ref.get("execution_eligible") is not True + or descriptor_ref.get("status") != "LEGAL_CONTENT_ADMITTED" + or descriptor.get("execution_eligible") is not True + or descriptor.get("status") != "LEGAL_CONTENT_ADMITTED" + or descriptor_ref.get("descriptor_sha256") != descriptor_hashes.get(calculator_id) + or descriptor.get("descriptor_sha256") != descriptor_hashes.get(calculator_id) + ): + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_DESCRIPTOR_NOT_ADMITTED_OR_HASH_MISMATCH"])) + continue + formulas = [item for item in descriptor.get("formula_rows", []) if isinstance(item, Mapping)] + formula_matches = [item for item in formulas if item.get("output_metric_id") in requested_metrics] + if len(formula_matches) != 1: + status = "ACTIVATION_CONFLICT" if len(formula_matches) > 1 else "ACTIVATION_GAP" + receipts.append(empty_receipt(row, calculator_id, status, ["CALCULATION_FORMULA_SELECTION_NOT_EXACT"])) + continue + formula = formula_matches[0] + required_operands = sorted(str(item) for item in formula.get("input_operand_ids", []) if isinstance(item, str)) + output_unit = str(formula.get("output_unit_id", "")) + operand_rows = [operand_row(item, output_unit) for item in required_operands] + missing_operands = list(required_operands) + law_value_uses: list[dict[str, Any]] = [] + missing_law_values: list[str] = [] + for law_value_id in formula.get("law_value_ids", []): + source = law_values.get(str(law_value_id)) + if not isinstance(source, Mapping): + missing_law_values.append(str(law_value_id)) + continue + law_value_uses.append( + { + "law_value_id": str(law_value_id), + "authority_id": str(source["authority_id"]), + "authority_release_sha256": require_hex(source["authority_release_sha256"], code="AUTHORITY_RELEASE_SHA256_INVALID"), + "law_value_release_sha256": require_hex(source["law_value_release_sha256"], code="LAW_VALUE_RELEASE_SHA256_INVALID"), + "effective_interval": source["effective_interval"], + "value": format(decimal_value(source["value"]), "f"), + "unit_id": str(source.get("unit_id", source.get("unit"))), + "approval_status": "APPROVED", + } + ) + calculation_status = "MISSING_OPERAND" if missing_operands else ("LAW_VALUE_GAP" if missing_law_values else "CALCULATED") + result: dict[str, Any] | None = None + result_sha256: str | None = None + if calculation_status == "CALCULATED": + # The current S2_00 handoff deliberately preserves only + # calculation-request references, not numeric values. This + # branch becomes reachable only after exact PRESENT operand + # rows are supplied by a compatible handoff revision. + values = [decimal_value(item["value"]) for item in operand_rows] + operator = formula.get("operator") + if operator == "ADD": + computed = sum(values, Decimal("0")) + elif operator == "SUBTRACT": + computed = values[0] - sum(values[1:], Decimal("0")) + elif operator == "MULTIPLY": + computed = Decimal("1") + for value in values: + computed *= value + elif operator == "DIVIDE": + if len(values) != 2 or values[1] == 0: + raise S220Error("CALCULATION_INVALID_DIVISION", "division requires two operands and non-zero divisor") + computed = values[0] / values[1] + elif operator == "MIN": + computed = min(values) + elif operator == "MAX": + computed = max(values) + else: + raise S220Error("CALCULATION_OPERATOR_UNSUPPORTED", "formula operator is outside the closed set") + rounding = formula.get("rounding_rule", {}) + if not isinstance(rounding, Mapping) or rounding.get("mode") not in rounding_modes: + raise S220Error("CALCULATION_ROUNDING_INVALID", "rounding rule is invalid") + rounded = computed.quantize(decimal_value(rounding["quantum"]), rounding=rounding_modes[str(rounding["mode"])]) + result = { + "metric_id": str(formula["output_metric_id"]), + "value": format(rounded, "f"), + "unit_id": output_unit, + "currency": "KRW" if output_unit == "KRW" else None, + } + result_sha256 = digest(result) + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": stable_id("CR", row["atomic_claim_id"], calculator_id), + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": row["atomic_claim_id"], + "claim_group_id": row["claim_group_id"], + "calculator_id": calculator_id, + "activation_status": "ACTIVATED", + "calculation_status": calculation_status, + "activation_row_id": activation_row_id, + "descriptor_sha256": descriptor_hashes[calculator_id], + "formula_id": str(formula["formula_id"]), + "formula_sha256": require_hex(formula["formula_sha256"], code="FORMULA_SHA256_INVALID"), + "operand_rows": operand_rows, + "operand_refs": required_operands, + "units": sorted({item["unit_id"] for item in operand_rows if item["unit_id"]}), + "law_value_uses": sorted(law_value_uses, key=lambda item: item["law_value_id"]), + "effective_interval": formula["effective_interval"], + "rounding_rule": formula["rounding_rule"], + "period_rows": [], + "missing_operands": missing_operands, + "result": result, + "result_sha256": result_sha256, + "issue_codes": sorted( + (["CALCULATION_OPERAND_MISSING"] if missing_operands else []) + + [f"CALCULATION_LAW_VALUE_UNRESOLVED:{item}" for item in missing_law_values] + ), + } + receipts.append({**core, "receipt_sha256": digest(core)}) + return sorted(receipts, key=lambda row: row["calculation_receipt_id"]) + + + def call_with_transient_retry( + client: McpClient, + tool: str, + arguments: Mapping[str, Any], + *, + max_attempts: int = 3, + ) -> tuple[Mapping[str, Any], int]: + if not isinstance(max_attempts, int) or isinstance(max_attempts, bool) or not 1 <= max_attempts <= 3: + raise S220Error("RETRIEVAL_RETRY_POLICY_INVALID", "max_attempts must be an integer in 1..3") + for attempt_count in range(1, max_attempts + 1): + try: + return client.call(tool, arguments), attempt_count + except S220Error as exc: + if exc.code != "MCP_TRANSPORT" or attempt_count == max_attempts: + raise + raise S220Error("RETRIEVAL_RETRY_EXHAUSTED", "unreachable retry state") + + + def canonicalize_retrieval_hits( + candidates: Sequence[Mapping[str, Any]], + ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + ordered = sorted( + (dict(row) for row in candidates), + key=lambda row: ( + int(row.get("post_rerank_rank", 2**31 - 1)), + -float(row.get("reranker_score") or row.get("hybrid_score") or 0), + str(row.get("chunk_id", "")), + ), + ) + unique: dict[str, dict[str, Any]] = {} + dropped: list[dict[str, Any]] = [] + for candidate in ordered: + chunk_id = str(candidate.get("chunk_id", "")) + chunk_sha256 = str(candidate.get("chunk_sha256", "")) + if not chunk_id or HEX64.fullmatch(chunk_sha256) is None: + raise S220Error("RETRIEVAL_HIT_IDENTITY_INVALID", "canonical hit requires chunk ID and hash") + if chunk_id in unique: + dropped.append( + { + "chunk_id": chunk_id, + "chunk_sha256": chunk_sha256, + "drop_reason_code": "DUPLICATE_CHUNK", + } + ) + else: + unique[chunk_id] = candidate + return list(unique.values()), dropped + + + def build_retrieval_branch( + atomic: Sequence[Mapping[str, Any]], + bindings: Sequence[Mapping[str, Any]], + corpus_release: Mapping[str, Any], + corpus_scope: Mapping[str, Any], + request: Mapping[str, Any], + ) -> dict[str, Any]: + """C27->C28->C29 closed branch; pending corpus is explicit, never widened.""" + by_atomic = {str(row["atomic_claim_id"]): row for row in bindings} + queries: list[dict[str, Any]] = [] + results: list[dict[str, Any]] = [] + receipts: list[dict[str, Any]] = [] + pack_items: dict[str, list[dict[str, Any]]] = {} + unmapped_by_atomic: dict[str, list[str]] = {} + conflicting_by_atomic: dict[str, list[str]] = {} + corpus_admitted = ( + corpus_release.get("status") == "ADMITTED" + and corpus_release.get("available") is True + and corpus_release.get("sealed") is True + and corpus_scope.get("registry_status") == "APPROVED" + and isinstance(corpus_scope.get("rows"), list) + and bool(corpus_scope.get("rows")) + ) + snapshot_id = str(corpus_release.get("snapshot_id") or "UNAVAILABLE") + snapshot_sha256 = str(corpus_release.get("snapshot_sha256") or ("0" * 64)) + scope_rows = [row for row in corpus_scope.get("rows", []) if isinstance(row, dict)] + ready_query_rows: list[dict[str, Any]] = [] + for claim in atomic: + atomic_id = str(claim["atomic_claim_id"]) + binding = by_atomic[atomic_id] + pack_items[atomic_id] = [] + unmapped_by_atomic[atomic_id] = [] + conflicting_by_atomic[atomic_id] = [] + binding_ready = ( + binding.get("case_type_binding_status") == "BOUND" + and binding.get("sub_rule_binding_status") == "BOUND" + ) + matched_scopes = [ + row for row in scope_rows + if row.get("corpus_scope_id") == binding.get("corpus_scope_id") + and row.get("case_type_id") == binding.get("case_type_id") + and binding.get("sub_rule_id") in row.get("sub_rule_ids", []) + and row.get("element_set_id") == binding.get("element_set_id") + and row.get("approval_status") == "APPROVED" + ] + query_rows: list[dict[str, Any]] = [] + if corpus_admitted and binding_ready and len(matched_scopes) == 1: + scope = matched_scopes[0] + crosswalk = [row for row in scope.get("slot_crosswalk_rows", []) if isinstance(row, dict)] + for doctrine_role in sorted({str(row.get("doctrine_role")) for row in crosswalk if row.get("doctrine_role")}): + expected_ids = sorted( + str(row["corpus_element_id"]) + for row in crosswalk + if row.get("doctrine_role") == doctrine_role and row.get("corpus_element_id") + ) + search_filter = { + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "corpus_scope_id": binding["corpus_scope_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "jurisdiction": "KR", + "snapshot_id": snapshot_id, + "approval_status": "APPROVED", + "effective_on": str(corpus_release.get("effective_on") or corpus_release.get("as_of") or ""), + } + if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", search_filter["effective_on"]): + raise S220Error("CORPUS_EFFECTIVE_DATE_UNBOUND", "admitted corpus requires effective_on") + query_projection = { + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "corpus_element_ids": expected_ids, + } + query_text = json.dumps(query_projection, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + row = { + "query_id": stable_id("Q", atomic_id, doctrine_role), + "atomic_claim_id": atomic_id, + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "corpus_scope_id": binding["corpus_scope_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "query_status": "READY", + "query_text": query_text, + "query_sha256": digest(query_text.encode("utf-8")), + "filter": search_filter, + "alpha": corpus_release.get("alpha"), + "top_k": corpus_release.get("top_k"), + "embedding_configuration_digest": corpus_release.get("embedding_configuration_digest"), + "reranker_configuration_digest": corpus_release.get("reranker_configuration_digest"), + "index_configuration_digest": corpus_release.get("index_configuration_digest"), + "expected_corpus_element_ids": expected_ids, + "issue_codes": [], + } + for key in ("embedding_configuration_digest", "reranker_configuration_digest", "index_configuration_digest"): + require_hex(row[key], code=f"CORPUS_{key.upper()}_UNBOUND") + if not isinstance(row["alpha"], (int, float)) or isinstance(row["alpha"], bool) or not 0 <= row["alpha"] <= 1: + raise S220Error("CORPUS_ALPHA_UNBOUND", "admitted corpus requires alpha in [0,1]") + if not isinstance(row["top_k"], int) or isinstance(row["top_k"], bool) or not 1 <= row["top_k"] <= 100: + raise S220Error("CORPUS_TOP_K_UNBOUND", "admitted corpus requires top_k 1..100") + query_rows.append(row) + ready_query_rows.append(row) + query_base = { + "schema_version": "stage2_requirement_fact_query_plan.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "rows": sorted(query_rows, key=lambda row: row["query_id"]), + } + query = {**query_base, "query_plan_sha256": digest(query_base)} + queries.append(query) + if not query_rows: + issue_codes = ["QUERY_NOT_RUN_UNRESOLVED_BINDING"] + if binding_ready and not corpus_admitted: + issue_codes.append("CORPUS_NOT_ADMITTED") + elif binding_ready and len(matched_scopes) != 1: + issue_codes.append("CORPUS_SCOPE_FK_UNRESOLVED") + result_base = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": query["query_plan_sha256"], + "snapshot_id": snapshot_id, + "snapshot_sha256": snapshot_sha256, + "result_status": "NOT_RUN_UNRESOLVED_BINDING", + "selected_hits": [], + "dropped_hits": [], + "issue_codes": sorted(set(issue_codes)), + } + results.append({**result_base, "result_sha256": digest(result_base)}) + + if ready_query_rows: + collection = corpus_release.get("collection") + tenant = corpus_release.get("tenant") + credential_ref = corpus_release.get("credential_ref") + if not all(isinstance(item, str) and item for item in (collection, tenant, credential_ref)): + raise S220Error("CORPUS_TRANSPORT_BINDING_UNBOUND", "collection, tenant and credential_ref are required") + weaviate = McpClient(WEAVIATE_MCP_URL, "weaviate") + try: + weaviate.initialize() + by_plan = {row["atomic_claim_id"]: row for row in queries} + selected_by_atomic: dict[str, list[dict[str, Any]]] = {} + dropped_by_atomic: dict[str, list[dict[str, Any]]] = {} + requests_by_atomic: dict[str, list[dict[str, Any]]] = {} + responses_by_atomic: dict[str, list[Any]] = {} + filters_by_atomic: dict[str, list[dict[str, Any]]] = {} + attempts_by_atomic: dict[str, list[int]] = {} + for query_row in sorted(ready_query_rows, key=lambda row: row["query_id"]): + atomic_id = str(query_row["atomic_claim_id"]) + arguments = { + "collection_name": collection, + "tenant": tenant, + "query": query_row["query_text"], + "filters": query_row["filter"], + "alpha": query_row["alpha"], + "limit": query_row["top_k"], + } + tool_result, attempt_count = call_with_transient_retry( + weaviate, "search_hybrid", arguments, max_attempts=3 + ) + text = _tool_text(tool_result, "search_hybrid") + response = load_json(text.encode("utf-8"), label="weaviate-search-response") + if isinstance(response, dict): + raw_hits = response.get("hits", response.get("objects", response.get("results"))) + else: + raw_hits = response + if not isinstance(raw_hits, list): + raise S220Error("WEAVIATE_RESPONSE_SHAPE", "search response must expose one hit array") + accepted: list[dict[str, Any]] = [] + dropped: list[dict[str, Any]] = [] + for rank, raw_hit in enumerate(raw_hits, 1): + if not isinstance(raw_hit, dict) or not isinstance(raw_hit.get("metadata"), dict): + raise S220Error("WEAVIATE_HIT_SHAPE", "hit and metadata objects are required") + metadata = raw_hit["metadata"] + chunk_id = str(raw_hit.get("chunk_id", metadata.get("chunk_id", ""))) + chunk_sha = str(raw_hit.get("chunk_sha256", metadata.get("chunk_sha256", ""))) + reason: str | None = None + if metadata.get("approval_status") != "APPROVED": + reason = "UNAPPROVED_CHUNK" + elif metadata.get("chunk_sha256") != chunk_sha or HEX64.fullmatch(chunk_sha) is None: + reason = "HASH_MISMATCH" + elif not ( + query_row["filter"]["case_type_id"] in metadata.get("case_type_ids", []) + and query_row["filter"]["sub_rule_id"] in metadata.get("sub_rule_ids", []) + and query_row["filter"]["rule_branch_key"] in metadata.get("rule_branch_keys", []) + and query_row["filter"]["corpus_scope_id"] in metadata.get("corpus_scope_ids", []) + and query_row["filter"]["element_set_id"] in metadata.get("element_set_ids", []) + and query_row["doctrine_role"] in metadata.get("doctrine_roles", []) + and metadata.get("jurisdiction") == "KR" + ): + reason = "FILTER_MISMATCH" + if reason: + dropped.append({"chunk_id": chunk_id or "UNBOUND", "chunk_sha256": chunk_sha if HEX64.fullmatch(chunk_sha) else "0" * 64, "drop_reason_code": reason}) + continue + accepted.append( + { + "chunk_id": chunk_id, + "chunk_sha256": chunk_sha, + "source_document_id": str(raw_hit.get("source_document_id", metadata.get("document_id"))), + "source_document_sha256": str(raw_hit.get("source_document_sha256", metadata.get("document_sha256"))), + "source_locator": str(raw_hit.get("source_locator", metadata.get("source_locator"))), + "pre_rerank_rank": int(raw_hit.get("pre_rerank_rank", rank)), + "hybrid_score": raw_hit.get("hybrid_score", raw_hit.get("score", 0)), + "post_rerank_rank": int(raw_hit.get("post_rerank_rank", rank)), + "reranker_score": raw_hit.get("reranker_score"), + "metadata": metadata, + } + ) + selected_by_atomic.setdefault(atomic_id, []).extend(accepted) + dropped_by_atomic.setdefault(atomic_id, []).extend(dropped) + requests_by_atomic.setdefault(atomic_id, []).append(arguments) + responses_by_atomic.setdefault(atomic_id, []).append(response) + filters_by_atomic.setdefault(atomic_id, []).append(query_row["filter"]) + attempts_by_atomic.setdefault(atomic_id, []).append(attempt_count) + for atomic_id in sorted(requests_by_atomic): + plan = by_plan[atomic_id] + hits, duplicate_drops = canonicalize_retrieval_hits( + selected_by_atomic.get(atomic_id, []) + ) + dropped_by_atomic.setdefault(atomic_id, []).extend(duplicate_drops) + dropped = sorted(dropped_by_atomic.get(atomic_id, []), key=lambda row: (row["chunk_id"], row["drop_reason_code"])) + result_base = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": plan["query_plan_sha256"], + "snapshot_id": snapshot_id, + "snapshot_sha256": snapshot_sha256, + "result_status": "COMPLETE" if hits else "NO_HIT", + "selected_hits": hits, + "dropped_hits": dropped, + "issue_codes": [] if hits else ["RETRIEVAL_NO_HIT"], + } + results.append({**result_base, "result_sha256": digest(result_base)}) + receipt_base = { + "schema_version": "stage2_requirement_fact_retrieval_receipt.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": plan["query_plan_sha256"], + "request_sha256": digest(requests_by_atomic.get(atomic_id, [])), + "response_sha256": digest(responses_by_atomic.get(atomic_id, [])), + "transport": "MCP_OVER_HTTP_JSON_RPC", + "endpoint": WEAVIATE_MCP_URL, + "tool_name": "search_hybrid", + "collection_name": collection, + "tenant": tenant, + "snapshot_sha256": snapshot_sha256, + "filter_sha256": digest(filters_by_atomic.get(atomic_id, [])), + "configuration_digest": digest( + { + "collection_name": collection, + "tenant": tenant, + "query_count": len(requests_by_atomic.get(atomic_id, [])), + } + ), + "attempt_count": max(attempts_by_atomic.get(atomic_id, [1])), + "replay_status": "NOT_REQUESTED", + "read_only_verified": True, + "credential_ref": credential_ref, + "credential_material_persisted": False, + } + receipts.append({**receipt_base, "receipt_sha256": digest(receipt_base)}) + binding = by_atomic[atomic_id] + scope = next(row for row in scope_rows if row.get("corpus_scope_id") == binding.get("corpus_scope_id")) + crosswalk_by_element = { + str(row["corpus_element_id"]): row + for row in scope.get("slot_crosswalk_rows", []) + if isinstance(row, dict) and row.get("corpus_element_id") + } + for hit in hits: + matched_element = False + for element_id in sorted(str(item) for item in hit["metadata"].get("corpus_element_ids", [])): + crosswalk = crosswalk_by_element.get(element_id) + if crosswalk is None: + unmapped_by_atomic[atomic_id].append(hit["chunk_id"]) + continue + matched_element = True + if crosswalk.get("mapping_status") == "CONFLICT": + conflicting_by_atomic[atomic_id].append(hit["chunk_id"]) + pack_items[atomic_id].append( + { + "corpus_element_id": element_id, + "doctrine_role": crosswalk["doctrine_role"], + "chunk_id": hit["chunk_id"], + "chunk_sha256": hit["chunk_sha256"], + "source_document_id": hit["source_document_id"], + "source_document_sha256": hit["source_document_sha256"], + "source_locator": hit["source_locator"], + "authority_ids": sorted(str(item) for item in hit["metadata"].get("authority_ids", [])), + "proposition_ids": sorted(str(item) for item in hit["metadata"].get("proposition_ids", [])), + "stage1_slot_refs": sorted(str(item) for item in crosswalk.get("stage1_slot_refs", [])), + "mapping_status": crosswalk.get("mapping_status", "UNMAPPED"), + "fact_creation_allowed": False, + "legal_decision_override_allowed": False, + } + ) + if not matched_element: + unmapped_by_atomic[atomic_id].append(hit["chunk_id"]) + finally: + weaviate.close() + key = lambda row: str(row["atomic_claim_id"]) + return { + "queries": sorted(queries, key=key), + "results": sorted(results, key=key), + "receipts": sorted(receipts, key=lambda row: str(row["atomic_claim_id"])), + "pack_items": { + atomic_id: sorted(rows, key=lambda row: (row["corpus_element_id"], row["chunk_id"])) + for atomic_id, rows in sorted(pack_items.items()) + }, + "unmapped_by_atomic": {key: sorted(set(value)) for key, value in sorted(unmapped_by_atomic.items())}, + "conflicting_by_atomic": {key: sorted(set(value)) for key, value in sorted(conflicting_by_atomic.items())}, + } + + + def execute_core(inputs: Mapping[str, Any], assets: Mapping[str, Any], request: Mapping[str, Any], snapshot_digest: str) -> dict[str, Any]: + verdicts = inputs["verdicts"] + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="s2-20-c20-c21") as pool: + c20_future = pool.submit( + build_portfolio, + verdicts, + inputs["verdict_sources"], + assets["disposition_policy"], + ) + c21_future = pool.submit( + build_party_projection, + inputs["party_context"], + inputs.get("object_registry", {}), + ) + atomic_work, partitions, disposition_rows, group_dependencies = c20_future.result() + party_projection = c21_future.result() + for row in atomic_work: + remedy_kinds = sorted( + { + str(item.get("remedy_kind")) + for item in row.get("remedy_candidates", []) + if isinstance(item, dict) and item.get("remedy_kind") + } + ) + row["signature_projection"] = project_signature( + row["normalized_claim_signature"], + row["c20_projection"], + claim_c21_projection(row, party_projection), + {"remedy_mode": remedy_kinds[0] if len(remedy_kinds) == 1 else "UNKNOWN"}, + assets["signature_projection_registry"], + ) + case_registry = assets["case_registry"] + rule_registry = assets["rule_registry"] + bindings: list[dict[str, Any]] = [] + rule_asset_refs: dict[str, dict[str, list[dict[str, str]]]] = {} + issues: list[dict[str, Any]] = [] + for row in atomic_work: + signature = row["signature_projection"]["target_signature"] + case_type_id, status, codes, case_predicate_ids = bind_case(signature, case_registry) + rule, rule_codes, rule_predicate_ids = bind_rule(case_type_id, signature, rule_registry) + fk_codes: list[str] = [] + refs = { + "structured_relief_rule_refs": [], "markdown_source_refs": [], + "renderer_refs": [], "party_set_rule_refs": [], + "ce13_branch_refs": [], "review_policy_refs": [], + } + if rule is not None and case_type_id is not None: + fk_codes, refs = validate_rule_foreign_keys(case_type_id, rule, assets) + all_codes = sorted(set(codes + rule_codes + fk_codes)) + sub_status = ( + "BOUND" + if rule is not None and not fk_codes + else ("CONFLICT" if any("MULTI" in code for code in rule_codes) else "UNRESOLVED") + ) + rule_asset_refs[row["atomic_claim_id"]] = refs + binding = { + "atomic_claim_id": row["atomic_claim_id"], + "target_signature": signature, + "target_signature_sha256": row["signature_projection"]["target_signature_sha256"], + "projection_rule_ids": row["signature_projection"]["projection_rule_ids"], + "case_type_binding_status": status, + "matched_case_type_predicate_ids": case_predicate_ids, + "case_type_id": case_type_id, + "sub_rule_binding_status": sub_status, + "matched_sub_rule_predicate_ids": rule_predicate_ids, + "sub_rule_id": rule.get("sub_rule_id") if rule else None, + "rule_branch_key": rule.get("rule_branch_key") if rule else None, + "renderer_ids": sorted(str(item) for item in (rule.get("renderer_ids", []) if rule else []) if isinstance(item, str)), + "corpus_scope_id": rule.get("corpus_scope_id") if rule else None, + "element_set_id": rule.get("element_set_id") if rule else None, + "party_set_rule_id": rule.get("party_set_rule_id") if rule else None, + "ce13_branch_id": rule.get("ce13_branch_id") if rule else None, + "legal_content_status": "APPROVED" if rule is not None and not fk_codes else ("PENDING_LEGAL_CLASSIFICATION" if status != "BOUND" else "PENDING_LEGAL_CONTENT"), + "binding_issue_codes": all_codes, + } + bindings.append(binding) + for code in all_codes: + issues.append({"issue_id": stable_id("ISS", row["atomic_claim_id"], code), "code": code, "scope_ref": row["atomic_claim_id"], "status": "OPEN"}) + + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="s2-20-c22-c27") as pool: + c22_future = pool.submit( + calculate_all, + atomic_work, + bindings, + assets["activation_registry"], + assets["calculator_descriptor_hashes"], + assets.get("calculator_descriptors", {}), + assets.get("law_value_tables", []), + request, + ) + c27_future = pool.submit( + build_retrieval_branch, + atomic_work, + bindings, + assets["corpus_release"], + assets["corpus_scope"], + request, + ) + calculation_receipts = c22_future.result() + retrieval = c27_future.result() + + groups: dict[str, dict[str, Any]] = {} + for row in atomic_work: + group = groups.setdefault( + row["claim_group_id"], + { + "schema_version": "stage2_claim_group.v1", + "claim_group_id": row["claim_group_id"], + "atomic_claim_ids": [], + "party_refs": [], + "object_refs": [], + "case_type_ids": [], + "rule_branch_keys": [], + "renderer_ids": [], + "party_set_rule_ids": [], + "calculation_receipt_ids": [], + "corpus_scope_ids": [], + "amount_slots": [], + "period_slots": [], + "counter_performance_refs": [], + "procedural_declaration_refs": [], + "dependencies": [], + "drafting_permission": "WORKNOTE_ONLY", + "issue_refs": [], + }, + ) + group["atomic_claim_ids"].append(row["atomic_claim_id"]) + bound_by_id = {row["atomic_claim_id"]: row for row in bindings} + calc_by_atomic: dict[str, list[dict[str, Any]]] = {} + for receipt in calculation_receipts: + calc_by_atomic.setdefault(str(receipt["atomic_claim_id"]), []).append(receipt) + retrieval_by_atomic = {row["atomic_claim_id"]: row for row in retrieval["results"]} + atom_work_by_id = {row["atomic_claim_id"]: row for row in atomic_work} + for group in groups.values(): + group["atomic_claim_ids"].sort() + group_bindings = [bound_by_id[item] for item in group["atomic_claim_ids"]] + group_calculations = [receipt for item in group["atomic_claim_ids"] for receipt in calc_by_atomic.get(item, [])] + group["party_refs"] = sorted({ref for item in group["atomic_claim_ids"] for ref in atom_work_by_id[item]["right_holder_refs"] + atom_work_by_id[item]["obligor_refs"]}) + group["object_refs"] = sorted({ref for item in group["atomic_claim_ids"] for ref in atom_work_by_id[item]["object_refs"]}) + group["case_type_ids"] = sorted({str(row["case_type_id"]) for row in group_bindings if row["case_type_id"]}) + group["rule_branch_keys"] = sorted({str(row["rule_branch_key"]) for row in group_bindings if row["rule_branch_key"]}) + group["renderer_ids"] = sorted({item for row in group_bindings for item in row["renderer_ids"]}) + group["party_set_rule_ids"] = sorted({str(row["party_set_rule_id"]) for row in group_bindings if row["party_set_rule_id"]}) + group["calculation_receipt_ids"] = sorted(str(row["calculation_receipt_id"]) for row in group_calculations) + group["corpus_scope_ids"] = sorted({str(row["corpus_scope_id"]) for row in group_bindings if row["corpus_scope_id"]}) + group["dependencies"] = [row for row in group_dependencies if row["source_group_id"] == group["claim_group_id"]] + permission_codes: list[str] = [] + if any(row["case_type_binding_status"] != "BOUND" or row["sub_rule_binding_status"] != "BOUND" for row in group_bindings): + permission_codes.append("BINDING_NOT_EXACT") + if any(row["activation_status"] in {"ACTIVATION_GAP", "ACTIVATION_CONFLICT"} for row in group_calculations): + permission_codes.append("CALCULATION_NOT_CLOSED") + if any(retrieval_by_atomic[item]["result_status"] != "COMPLETE" for item in group["atomic_claim_ids"]): + permission_codes.append("REQUIREMENT_FACT_PACK_NOT_ADMITTED") + if assets["party_rules_eligible"] is not True: + permission_codes.append("PARTY_SET_RULES_NOT_ADMITTED") + if assets["signature_projection_eligible"] is not True: + permission_codes.append("SIGNATURE_PROJECTION_NOT_ADMITTED") + if assets["disposition_policy_eligible"] is not True: + permission_codes.append("DISPOSITION_POLICY_NOT_ADMITTED") + if assets["authority_admitted"] is not True: + permission_codes.append("AUTHORITY_RELEASE_NOT_ADMITTED") + group_issue_refs: list[str] = [] + for code in sorted(set(permission_codes)): + issue_id = stable_id("ISS", group["claim_group_id"], code) + group_issue_refs.append(issue_id) + issues.append({"issue_id": issue_id, "code": code, "scope_ref": group["claim_group_id"], "status": "OPEN"}) + group["issue_refs"] = group_issue_refs + legal_decisions = {str(atom_work_by_id[item]["legal_decision"]) for item in group["atomic_claim_ids"]} + group["drafting_permission"] = ( + "CLEAN_DRAFT" + if not permission_codes + else ("WORKNOTE_ONLY" if "UNRESOLVED" in legal_decisions else "REVIEW_DRAFT") + ) + + group_rows = sorted(groups.values(), key=lambda row: row["claim_group_id"]) + permission_by_group = {row["claim_group_id"]: row["drafting_permission"] for row in group_rows} + issue_refs_by_scope: dict[str, list[str]] = {} + for issue in issues: + issue_refs_by_scope.setdefault(str(issue["scope_ref"]), []).append(str(issue["issue_id"])) + atomic_rows: list[dict[str, Any]] = [] + for row in atomic_work: + binding = bound_by_id[row["atomic_claim_id"]] + atomic_rows.append( + { + "schema_version": "stage2_atomic_claim.v1", + "atomic_claim_id": row["atomic_claim_id"], + "source_claim_option_ids": [row["claim_option_id"]], + "normalized_signature_sha256": row["normalized_signature_sha256"], + "right_holder_refs": row["right_holder_refs"], + "obligor_refs": row["obligor_refs"], + "object_refs": row["object_refs"], + "remedy_mode": str(row["signature_projection"]["target_signature"].get("remedy_mode") or "UNKNOWN"), + "case_type_binding_status": binding["case_type_binding_status"], + "case_type_id": binding["case_type_id"], + "sub_rule_binding_status": binding["sub_rule_binding_status"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "claim_group_id": row["claim_group_id"], + "drafting_permission": permission_by_group[row["claim_group_id"]], + "source_refs": row["source_refs"], + "issue_refs": sorted(issue_refs_by_scope.get(row["atomic_claim_id"], [])), + } + ) + + issue_base = inputs["issue_ledger"] + base_rows = issue_base.get("issues", []) if isinstance(issue_base, dict) else [] + issue_plan = { + "schema_version": "stage2_issue_ledger_plan.v1", + "base_ledger_sha256": digest(inputs["issue_ledger_raw"]), + "preserved_base_issue_ids": sorted(str(row.get("issue_id")) for row in base_rows if isinstance(row, dict) and row.get("issue_id")), + "plan_issues": sorted(issues, key=lambda row: row["issue_id"]), + } + option_report = { + "schema_version": "stage2_option_conservation_report.v1", + "run_binding_digest": request["run_binding_digest"], + "all_option_ids": sorted(row["claim_option_id"] for row in disposition_rows), + "selected_option_ids": partitions["selected"], + "alternative_option_ids": partitions["alternative"], + "excluded_option_ids": partitions["excluded"], + "deferred_option_ids": partitions["deferred"], + "partition_disjoint": True, + "partition_union_exact": True, + "silent_loss_count": 0, + "duplicate_recovery_issue_refs": [], + "status": "PASS", + } + party_rule_index = { + str(rule.get("party_set_rule_id")): rule + for rule in assets.get("party_rules", {}).get("rules", []) + if isinstance(rule, dict) and rule.get("party_set_rule_id") + } + party_rows: list[dict[str, Any]] = [] + present_role_tokens = set(str(item) for item in party_projection.get("present_role_tokens", [])) + for group in group_rows: + rule_id = group["party_set_rule_ids"][0] if len(group["party_set_rule_ids"]) == 1 else "UNBOUND" + rule = party_rule_index.get(rule_id) + required_roles = sorted( + str(item) for item in (rule.get("required_role_tokens", []) if isinstance(rule, dict) else []) + ) + missing_roles = sorted(set(required_roles) - present_role_tokens) + if rule_id == "UNBOUND" or not isinstance(rule, dict): + missing_roles = sorted(set(missing_roles + ["PARTY_SET_RULE_UNBOUND"])) + identity_conflicts = party_projection["identity_issue_refs"] + party_rows.append( + { + "claim_group_id": group["claim_group_id"], + "party_set_rule_id": rule_id, + "required_role_tokens": required_roles, + "present_party_refs": group["party_refs"], + "missing_role_tokens": missing_roles, + "identity_conflict_refs": identity_conflicts, + "status": "REVIEW_REQUIRED" if missing_roles or identity_conflicts else "COMPLETE", + "issue_refs": group["issue_refs"], + } + ) + party_report = { + "schema_version": "stage2_party_completeness_report.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": party_rows, + "report_status": "REVIEW_REQUIRED" if any(row["status"] != "COMPLETE" for row in party_rows) else "COMPLETE", + } + evidence_inventory = inputs.get("evidence_inventory", {}) + evidence_items = evidence_inventory.get("items", []) if isinstance(evidence_inventory, dict) else [] + if not isinstance(evidence_items, list) or any(not isinstance(item, dict) for item in evidence_items): + raise S220Error("EVIDENCE_INVENTORY_SHAPE", "evidence inventory items must be objects") + evidence_ids = [item.get("evidence_id") for item in evidence_items] + if any(not isinstance(item, str) or not item for item in evidence_ids): + raise S220Error("EVIDENCE_ID_REQUIRED", "every evidence inventory row requires evidence_id") + if len(evidence_ids) != len(set(evidence_ids)): + raise S220Error("EVIDENCE_ID_DUPLICATE", "evidence inventory contains duplicate evidence_id") + exhibit_rows = [ + { + "evidence_ref": evidence_id, + "source_key": evidence_id, + "existing_label": None, + "assigned_label": f"갑 제{ordinal}호증", + "assignment_status": "DETERMINISTICALLY_ASSIGNED", + } + for ordinal, evidence_id in enumerate(sorted(evidence_ids), 1) + ] + exhibit_base = { + "schema_version": "stage2_exhibit_register.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": exhibit_rows, + } + exhibit_register = {**exhibit_base, "register_digest": digest(exhibit_base)} + report_documents = { + "option_conservation_report.json": (option_report, "schemas/relief_plan.schema.json#/$defs/option_conservation_report"), + "party_completeness_report.json": (party_report, "schemas/relief_plan.schema.json#/$defs/party_completeness_report"), + "exhibit_register.json": (exhibit_register, "schemas/relief_plan.schema.json#/$defs/exhibit_register"), + } + issue_raw = canonical_bytes(issue_plan) + plan_base = { + "schema_version": "stage2_canonical_relief_plan.v1", + "producer_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": snapshot_digest, + "parent_stage2_release_sha256": request["expected_parent_stage2_release_sha256"], + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "option_dispositions": disposition_rows, + "atomic_claims": atomic_rows, + "claim_groups": group_rows, + "group_dependencies": group_dependencies, + "selected_option_ids": partitions["selected"], + "alternative_option_ids": partitions["alternative"], + "excluded_option_ids": partitions["excluded"], + "deferred_option_ids": partitions["deferred"], + "report_refs": [ + {"path": path, "schema_ref": schema_ref, "sha256": digest(canonical_bytes(document))} + for path, (document, schema_ref) in sorted(report_documents.items()) + ], + "issue_ledger_ref": { + "path": "issue_ledger.plan.json", + "schema_ref": "schemas/relief_plan.schema.json#/$defs/issue_ledger_plan", + "sha256": digest(issue_raw), + }, + } + plan = {**plan_base, "plan_content_digest": digest(plan_base)} + issue_codes = [str(row["code"]) for row in issue_plan["plan_issues"]] + issue_summary = { + "total": len(issue_codes), + "legal": sum("LEGAL" in code or "AUTHORITY" in code for code in issue_codes), + "binding": sum("BINDING" in code or "RULE" in code for code in issue_codes), + "corpus": sum("CORPUS" in code or "REQUIREMENT_FACT" in code for code in issue_codes), + "calculation": sum("CALCULATION" in code or code.startswith("CE_") for code in issue_codes), + "technical": 0, + } + return { + "plan": plan, + "groups": group_rows, + "atomic": atomic_rows, + "bindings": bindings, + "issues": issue_plan, + "calculations": calculation_receipts, + "retrieval": retrieval, + "rule_asset_refs": rule_asset_refs, + "party_projection": party_projection, + "partitions": partitions, + "option_report": option_report, + "party_report": party_report, + "exhibit_register": exhibit_register, + "asset_hashes": assets["asset_hashes"], + "corpus_release": assets["corpus_release"], + "s2_20_code_sha256": assets["s2_20_code_sha256"], + "schema_documents": assets["schema_documents"], + "issue_summary": issue_summary, + } + + + def _require_mapping(value: Any, *, code: str, label: str) -> Mapping[str, Any]: + if not isinstance(value, dict): + raise S220Error(code, f"object required: {label}") + return value + + + def _verify_binding_text(raw: bytes, request: Mapping[str, Any]) -> None: + try: + text = raw.decode("utf-8", errors="strict") + except UnicodeDecodeError as exc: + raise S220Error("EXECUTOR_BINDING_UTF8", "executor binding must be UTF-8") from exc + marker = "\n - stage_id: S2_20\n" + framed = "\n" + text + if framed.count(marker) != 1: + raise S220Error("EXECUTOR_BINDING_S2_20_ROW", "exactly one S2_20 binding row is required") + section = framed.split(marker, 1)[1] + section = section.split("\n - stage_id:", 1)[0] + section = section.split("\nlegacy_fallbacks:", 1)[0] + + # Parse the closed scalar mapping by indentation instead of treating + # untrusted YAML as a bag of substrings. Sequence values are checked + # separately below; aliases, tags, flow mappings and duplicate scalar + # paths are not accepted in the live trust row. + stack: list[tuple[int, str]] = [] + scalars: dict[Any, str] = {} + for raw_line in section.splitlines(): + if not raw_line.strip(): + continue + if "\t" in raw_line or raw_line.lstrip().startswith(("#", "&", "*", "!", "{", "[")): + raise S220Error("EXECUTOR_BINDING_YAML_PROFILE", "binding uses a forbidden YAML construct") + indent = len(raw_line) - len(raw_line.lstrip(" ")) + stripped = raw_line.strip() + if stripped.startswith("- "): + continue + match = re.fullmatch(r"([A-Za-z0-9_]+):(.*)", stripped) + if match is None: + raise S220Error("EXECUTOR_BINDING_YAML_PROFILE", f"unsupported binding line: {stripped}") + while stack and stack[-1][0] >= indent: + stack.pop() + key, tail = match.group(1), match.group(2).strip() + path = tuple(item[1] for item in stack) + (key,) + if tail: + if path in scalars: + raise S220Error("EXECUTOR_BINDING_DUPLICATE_KEY", ".".join(path)) + if len(tail) >= 2 and tail[0] == tail[-1] and tail[0] in {'"', "'"}: + tail = tail[1:-1] + scalars[path] = tail + else: + stack.append((indent, key)) + + required_values = { + ("binding_id",): "S2-BINDING-S2_20-CODE-EXECUTOR-V1", + ("workflow_id",): "S2_20", + ("execution_class",): "NON-LLM-DETERMINISTIC", + ("active_runtime_authority",): "true", + ("agent_script_ref", "path"): "agent_scripts/Stage_2_S2_20.yml", + ("agent_script_ref", "sha256"): request["expected_s2_20_agent_sha256"], + ("workflow_contract_ref", "path"): "workflows/S2_20_canonical_relief_plan_reduce.yml", + ("inline_code_receipt_ref", "path"): "manifest/s2_20_inline_code_receipt.json", + ("stage2_release_ref", "path"): "manifest/stage2_release.json", + ("stage2_release_ref", "sha256"): request["expected_parent_stage2_release_sha256"], + ("expected_release_sha256",): request["expected_parent_stage2_release_sha256"], + ("agent_script_sha256",): request["expected_s2_20_agent_sha256"], + ("mcp_server_id",): "code-executor", + ("tool_name",): "run_code", + ("language",): "python", + ("network",): "agent-network", + ("timeout_seconds",): "300", + ("runtime_image_status",): "PINNED_VERIFIED", + ("dependency_lock", "requirements"): "httpx==0.28.1", + ("dependency_lock", "lock_status"): "STATIC_PIN_BOUND", + ("localdocs_contract", "fixed_request_path"): "stage2_control/s2_20_request.json", + ("external_mcp_contract", "endpoint"): WEAVIATE_MCP_URL, + ("external_mcp_contract", "operation_mode"): "READ_ONLY_EXACT_FILTER", + ("external_mcp_contract", "admission_status"): "LIVE_VERIFIED", + ("egress_profile_status",): "LIVE_VERIFIED", + ("live_admission_status",): "LIVE_CANARY_ADMITTED", + } + mismatches = sorted( + ".".join(path) + for path, expected in required_values.items() + if scalars.get(path) != expected + ) + for path in (("runtime_image_digest",), ("canonical_code_sha256",)): + if HEX64.fullmatch(scalars.get(path, "")) is None: + mismatches.append(".".join(path)) + for path in (("external_mcp_contract", "collection"), ("external_mcp_contract", "tenant")): + value = scalars.get(path, "") + if not value or value.startswith("PENDING"): + mismatches.append(".".join(path)) + if any(value.startswith("PENDING") for value in scalars.values()): + mismatches.append("pending_scalar") + + allowlist_marker = " read_path_allowlist:\n" + allowlist_tail = section.split(allowlist_marker, 1) + if len(allowlist_tail) != 2: + mismatches.append("localdocs_contract.read_path_allowlist") + allowlist_text = "" + else: + allowlist_text = allowlist_tail[1].split(" write_root_rule:", 1)[0] + required_allowlist_entries = ( + f"{ASSET_ROOT}/schemas/deployment.schema.json", + f"{ASSET_ROOT}/rules/relief/.md", + f"{ASSET_ROOT}/legal_review/.json", + ) + for entry in required_allowlist_entries: + if f" - {entry}\n" not in allowlist_text + "\n": + mismatches.append(f"allowlist:{entry}") + if mismatches: + raise S220Error( + "EXECUTOR_BINDING_NOT_ADMITTED", + "S2_20 binding is incomplete or structurally inconsistent", + details={"mismatches": sorted(set(mismatches))}, + ) + + + def _admission_signed_material(admission: Mapping[str, Any]) -> dict[str, Any]: + return { + key: value + for key, value in admission.items() + if key not in {"signature", "signature_verification_status", "signed_payload_sha256"} + } + + + def _verify_external_trust( + raw_map: Mapping[str, bytes], + fixed: Mapping[str, str], + request: Mapping[str, Any], + schema_documents: Mapping[str, Any], + ) -> tuple[Mapping[str, Any], Mapping[str, Any], Mapping[str, Any]]: + if digest(raw_map[fixed["binding"]]) != request["expected_s2_20_executor_binding_sha256"]: + raise S220Error("EXECUTOR_BINDING_HASH_MISMATCH", "executor binding bytes differ") + _verify_binding_text(raw_map[fixed["binding"]], request) + if digest(raw_map[fixed["agent"]]) != request["expected_s2_20_agent_sha256"]: + raise S220Error("S2_20_AGENT_HASH_MISMATCH", "deployment Agent bytes differ") + if digest(raw_map[fixed["s2_10_release"]]) != request["expected_s2_10_release_sha256"]: + raise S220Error("S2_10_RELEASE_HASH_MISMATCH", "S2_10 child release bytes differ") + + # Host CAS is the first semantic write-authority gate. No plan + # artifact, including a diagnostic, may be published unless this + # exact run-bound receipt has passed its release-bound schema. + host_cas = _require_mapping(load_json(raw_map[fixed["host_cas"]], label=fixed["host_cas"]), code="HOST_CAS_SHAPE", label="host CAS") + _validate_loaded_value( + host_cas, + raw_map[fixed["host_cas"]], + "schemas/deployment.schema.json#/$defs/s2_20_host_cas_receipt", + label=fixed["host_cas"], + schema_documents=schema_documents, + ) + expected_scope = f"S2_20:{request['run_binding_digest']}" + if ( + host_cas.get("run_binding_digest") != request["run_binding_digest"] + or host_cas.get("exclusivity_key") != expected_scope + ): + raise S220Error("HOST_CAS_NOT_ACQUIRED", "host CAS receipt does not prove the exact single writer") + + if digest(raw_map[fixed["admission"]]) != request["detached_admission_receipt_sha256"]: + raise S220Error("ADMISSION_RECEIPT_HASH_MISMATCH", "detached admission bytes differ") + admission = _require_mapping(load_json(raw_map[fixed["admission"]], label=fixed["admission"]), code="ADMISSION_SHAPE", label="admission") + _validate_loaded_value( + admission, + raw_map[fixed["admission"]], + "schemas/deployment.schema.json#/$defs/stage2_deterministic_admission_receipt", + label=fixed["admission"], + schema_documents=schema_documents, + ) + if admission.get("admission_status") not in {"CANARY_ADMITTED", "PRODUCTION_ADMITTED"}: + raise S220Error("ADMISSION_PENDING", "direct execution requires canary or production admission") + if admission.get("parent_release_sha256") != request["expected_parent_stage2_release_sha256"]: + raise S220Error("ADMISSION_PARENT_RELEASE", "admission parent release differs") + if admission.get("executor_binding_sha256") != request["expected_s2_20_executor_binding_sha256"]: + raise S220Error("ADMISSION_EXECUTOR_BINDING", "admission executor binding differs") + if "S2_20" not in admission.get("present_deterministic_stage_ids", []): + raise S220Error("ADMISSION_STAGE_SET", "S2_20 is absent from deterministic admission") + signature = admission.get("signature") + if ( + not isinstance(signature, str) + or not signature + or signature.startswith("PENDING") + or admission.get("signature_verification_status") != "BACKEND_VERIFIED" + ): + raise S220Error("ADMISSION_SIGNATURE", "backend-verified external admission signature is required") + signed_payload_sha256 = require_hex( + admission.get("signed_payload_sha256"), code="ADMISSION_SIGNED_PAYLOAD_HASH" + ) + signed_material = _admission_signed_material(admission) + if signed_payload_sha256 != digest(signed_material): + raise S220Error("ADMISSION_SIGNED_PAYLOAD_HASH", "admission signed payload digest differs") + capability_sha256 = require_hex( + admission.get("backend_capability_receipt_sha256"), + code="ADMISSION_BACKEND_CAPABILITY_HASH", + ) + if capability_sha256 != host_cas.get("backend_capability_receipt_sha256"): + raise S220Error( + "ADMISSION_BACKEND_CAPABILITY_MISMATCH", + "admission verification is not bound to the host CAS backend capability", + ) + matching_stage_receipts = [ + row for row in admission.get("stage_receipts", []) + if isinstance(row, dict) and row.get("asset_id") == "RECEIPT-S2_20-INLINE-CODE" + ] + if ( + len(matching_stage_receipts) != 1 + or matching_stage_receipts[0].get("path") != "manifest/s2_20_inline_code_receipt.json" + or matching_stage_receipts[0].get("sha256") != digest(raw_map[fixed["inline_receipt"]]) + ): + raise S220Error("ADMISSION_STAGE_RECEIPT", "admission does not bind the exact S2_20 inline receipt") + child = _require_mapping(load_json(raw_map[fixed["s2_10_release"]], label=fixed["s2_10_release"]), code="S2_10_RELEASE_SHAPE", label="S2_10 release") + parent_ref = child.get("parent_stage2_release_ref", {}) + if not isinstance(parent_ref, dict) or parent_ref.get("sha256") != request["expected_parent_stage2_release_sha256"]: + raise S220Error("S2_10_PARENT_RELEASE_MISMATCH", "S2_10 child release is not bound to the parent") + release = _require_mapping(load_json(raw_map[fixed["release"]], label=fixed["release"]), code="PARENT_RELEASE_SHAPE", label="parent release") + return release, child, admission + + + def _require_object_self_hash( + value: Mapping[str, Any], field: str, *, code: str, label: str + ) -> str: + declared = value.get(field) + require_hex(declared, code=code) + material = {key: item for key, item in value.items() if key != field} + if declared != digest(material): + raise S220Error(code, f"object self hash differs: {label}") + return str(declared) + + + def _validate_loaded_value( + value: Any, + raw: bytes, + schema_ref: str, + *, + label: str, + schema_documents: Mapping[str, Any], + require_canonical_bytes: bool = True, + ) -> None: + validate_artifact_payload( + canonical_bytes(value), schema_ref, label, schema_documents + ) + if require_canonical_bytes and raw != canonical_bytes(value): + raise S220Error( + "UPSTREAM_CANONICAL_BYTES_MISMATCH", + f"upstream JSON is not canonical bytes: {label}", + ) + + + def _sealed_child_asset_hash(child_release: Mapping[str, Any], path: str) -> str: + rows = child_release.get("sealed_assets", []) + matches = [ + row for row in rows + if isinstance(row, dict) and row.get("path") == path + ] if isinstance(rows, list) else [] + if len(matches) != 1: + raise S220Error( + "S2_10_CHILD_ASSET_BINDING", + f"child release must seal exactly one asset row: {path}", + ) + return require_hex(matches[0].get("sha256"), code="S2_10_CHILD_ASSET_HASH") + + + def _validate_special_legal_asset( + value: Any, + *, + schema_version: str, + identity_field: str, + identity: str, + rows_field: str, + label: str, + ) -> Mapping[str, Any]: + row = _require_mapping( + value, code="SPECIAL_LEGAL_ASSET_SHAPE", label=label + ) + if ( + row.get("schema_version") != schema_version + or row.get(identity_field) != identity + or row.get("status") not in {"PENDING_LEGAL_CONTENT", "LEGAL_CONTENT_ADMITTED"} + or not isinstance(row.get(rows_field), list) + ): + raise S220Error( + "SPECIAL_LEGAL_ASSET_CONTRACT", + f"special legal asset envelope differs: {label}", + ) + if row.get("execution_eligible") is True: + if row.get("status") != "LEGAL_CONTENT_ADMITTED" or not row.get(rows_field): + raise S220Error( + "SPECIAL_LEGAL_ASSET_NOT_ADMITTED", + f"executable special asset lacks admitted rows: {label}", + ) + elif row.get("execution_eligible") is not False: + raise S220Error( + "SPECIAL_LEGAL_ASSET_ELIGIBILITY", + f"execution_eligible must be boolean: {label}", + ) + return row + + + def validate_upstream_handoff( + all_raw: Mapping[str, bytes], + fixed: Mapping[str, str], + request: Mapping[str, Any], + root: str, + cluster_ids: Sequence[str], + wave_paths: Sequence[str], + schema_documents: Mapping[str, Any], + child_release: Mapping[str, Any], + ) -> dict[str, Any]: + """Validate and preserve the complete S2_00/S2_10 handoff consumed here.""" + + def loaded(path: str, schema_ref: str) -> Any: + value = load_json(all_raw[path], label=path) + _validate_loaded_value( + value, + all_raw[path], + schema_ref, + label=path, + schema_documents=schema_documents, + ) + return value + + ingress = _require_mapping( + loaded(fixed["ingress_status"], "schemas/ingress.schema.json#/$defs/ingress_status"), + code="INGRESS_STATUS_SHAPE", + label="ingress status", + ) + cluster_plan = _require_mapping( + loaded(fixed["cluster_plan"], "schemas/context.schema.json#/$defs/cluster_plan"), + code="CLUSTER_PLAN_SHAPE", + label="cluster plan", + ) + bundle_plan = _require_mapping( + loaded(fixed["bundle_plan"], "schemas/context.schema.json#/$defs/bundle_plan"), + code="BUNDLE_PLAN_SHAPE", + label="bundle plan", + ) + context_values: dict[str, Any] = {} + for key, definition in ( + ("case_context", "case_context"), + ("evidence_inventory", "evidence_inventory"), + ("object_registry", "object_registry"), + ("party_context", "party_and_title_context"), + ("slot_crosswalk", "slot_crosswalk"), + ): + context_values[key] = loaded( + fixed[key], f"schemas/context.schema.json#/$defs/{definition}" + ) + issue_ledger = loaded( + fixed["issue_ledger"], + "schemas/review_status.schema.json#/$defs/issue_ledger_base", + ) + + binding_receipt = _require_mapping( + ingress.get("run_binding_receipt"), + code="INGRESS_RUN_BINDING_SHAPE", + label="ingress run binding", + ) + barrier = _require_mapping( + ingress.get("output_barrier"), + code="INGRESS_BARRIER_SHAPE", + label="ingress output barrier", + ) + expected_root = f"{root}/" + if ( + binding_receipt.get("run_binding_digest") != request["run_binding_digest"] + or binding_receipt.get("canonical_output_root") != expected_root + or barrier.get("canonical_output_root") != expected_root + or barrier.get("branch") != "NORMAL" + or barrier.get("written_last") is not True + or barrier.get("non_status_artifacts_read_back_verified") is not True + or ingress.get("route") not in {"TO_S2_10", "TO_S2_10_WITH_ISSUES"} + ): + raise S220Error( + "INGRESS_BARRIER_BINDING", + "S2_00 ingress status does not prove the exact normal-route binding", + ) + if sorted(ingress.get("executable_cluster_ids", [])) != sorted(cluster_ids): + raise S220Error( + "INGRESS_CLUSTER_SET", + "ingress executable cluster set differs from cluster plan", + ) + artifact_rows = barrier.get("artifacts", []) + if not isinstance(artifact_rows, list) or barrier.get("artifact_set_digest") != digest(artifact_rows): + raise S220Error( + "INGRESS_ARTIFACT_SET_DIGEST", + "ingress output artifact set digest differs", + ) + artifact_by_path: dict[str, Mapping[str, Any]] = {} + for row in artifact_rows: + if not isinstance(row, dict) or not isinstance(row.get("path"), str): + raise S220Error("INGRESS_ARTIFACT_ROW_SHAPE", "ingress artifact row is invalid") + if row["path"] in artifact_by_path: + raise S220Error("INGRESS_ARTIFACT_PATH_DUPLICATE", f"duplicate ingress artifact: {row['path']}") + artifact_by_path[row["path"]] = row + s2_00_consumed_paths = [ + fixed[key] + for key in ( + "case_context", "evidence_inventory", "object_registry", "cluster_plan", + "party_context", "slot_crosswalk", "bundle_plan", "issue_ledger", + ) + ] + [f"{root}/context/cluster_slices/{cluster_id}.json" for cluster_id in cluster_ids] + for path in s2_00_consumed_paths: + relative = path.removeprefix(f"{root}/") + row = artifact_by_path.get(relative) + if not isinstance(row, dict) or row.get("raw_sha256") != digest(all_raw[path]): + raise S220Error( + "INGRESS_ARTIFACT_LINK_MISMATCH", + f"ingress barrier does not bind consumed artifact: {relative}", + ) + + expected_context_schema_hash = digest(all_raw[fixed["schema_context"]]) + expected_run_id = ingress.get("run_id") + expected_input_set = binding_receipt.get("input_set_digest") + expected_release_digest = binding_receipt.get("stage2_release_digest") + for label, value in { + "cluster_plan": cluster_plan, + "bundle_plan": bundle_plan, + **context_values, + }.items(): + header = value.get("artifact_header") if isinstance(value, dict) else None + if ( + not isinstance(header, dict) + or header.get("schema_sha256") != expected_context_schema_hash + or header.get("run_id") != expected_run_id + or header.get("input_set_digest") != expected_input_set + or header.get("stage2_release_digest") != expected_release_digest + ): + raise S220Error( + "CONTEXT_HEADER_BINDING", + f"context artifact header differs: {label}", + ) + + cluster_slices: dict[str, Mapping[str, Any]] = {} + slice_raw_hashes: dict[str, str] = {} + for cluster_id in sorted(cluster_ids): + path = f"{root}/context/cluster_slices/{cluster_id}.json" + slice_value = _require_mapping( + loaded(path, "schemas/context.schema.json#/$defs/cluster_slice"), + code="CLUSTER_SLICE_SHAPE", + label=path, + ) + slice_material = {key: value for key, value in slice_value.items() if key != "slice_digest"} + if ( + slice_value.get("cluster_id") != cluster_id + or slice_value.get("immutable") is not True + or slice_value.get("slice_digest") != digest(slice_material) + ): + raise S220Error( + "CLUSTER_SLICE_BINDING", + f"cluster slice identity/self hash differs: {cluster_id}", + ) + header = slice_value.get("artifact_header") + if ( + not isinstance(header, dict) + or header.get("schema_sha256") != expected_context_schema_hash + or header.get("run_id") != expected_run_id + or header.get("input_set_digest") != expected_input_set + or header.get("stage2_release_digest") != expected_release_digest + ): + raise S220Error("CLUSTER_SLICE_HEADER", f"cluster slice header differs: {cluster_id}") + cluster_slices[cluster_id] = slice_value + slice_raw_hashes[cluster_id] = digest(all_raw[path]) + + child_agent_hash = _sealed_child_asset_hash( + child_release, "agent_scripts/Stage_2_S2_10.yml" + ) + child_binding_hash = _sealed_child_asset_hash( + child_release, "deployment/stage2_s2_10_llm_binding.yml" + ) + cohorts = bundle_plan.get("cohorts", []) + if not isinstance(cohorts, list): + raise S220Error("BUNDLE_COHORT_SHAPE", "bundle cohorts must be an array") + cohort_ids: list[str] = [] + executable_ids: list[str] = [] + non_executable_ids: list[str] = [] + member_wave_by_cluster: dict[str, int] = {} + for cohort in cohorts: + if not isinstance(cohort, dict) or not isinstance(cohort.get("bundle_cohort_id"), str): + raise S220Error("BUNDLE_COHORT_SHAPE", "bundle cohort object/ID required") + cohort_id = str(cohort["bundle_cohort_id"]) + cohort_ids.append(cohort_id) + status = cohort.get("cohort_status") + if status == "EXECUTABLE": + executable_ids.append(cohort_id) + elif status == "NON_EXECUTABLE": + non_executable_ids.append(cohort_id) + member_ids = cohort.get("cohort_member_cluster_ids", []) + member_slices = cohort.get("member_slices", []) + if ( + member_ids != sorted(member_ids) + or not isinstance(member_slices, list) + or member_slices != sorted(member_slices, key=lambda row: str(row.get("cluster_id", ""))) + or member_ids != [row.get("cluster_id") for row in member_slices if isinstance(row, dict)] + ): + raise S220Error("BUNDLE_MEMBER_SET", f"bundle membership differs: {cohort_id}") + selected_refs = cohort.get("selected_context_refs", []) + if ( + not isinstance(selected_refs, list) + or [row.get("order_index") for row in selected_refs if isinstance(row, dict)] != list(range(len(selected_refs))) + or cohort.get("selected_context_ordered_refs_sha256") != digest(selected_refs) + ): + raise S220Error("BUNDLE_SELECTED_CONTEXT", f"selected context order/hash differs: {cohort_id}") + if ( + cohort.get("s2_10_agent_sha256") != child_agent_hash + or cohort.get("s2_10_llm_binding_sha256") != child_binding_hash + ): + raise S220Error("BUNDLE_S2_10_BINDING", f"bundle S2_10 binding differs: {cohort_id}") + membership_hash = digest(member_ids) + member_slice_hash = digest(member_slices) + receipt = cohort.get("context_materialization_receipt") + echoed = { + "bundle_cohort_id": cohort_id, + "s2_10_agent_sha256": child_agent_hash, + "s2_10_llm_binding_sha256": child_binding_hash, + "selected_context_ordered_refs_sha256": digest(selected_refs), + "cohort_membership_sha256": membership_hash, + "member_slice_ref_set_sha256": member_slice_hash, + } + materialization = { + "handoff_contract_version": cohort.get("handoff_contract_version"), + **echoed, + } + if ( + not isinstance(receipt, dict) + or any(receipt.get(key) != value for key, value in echoed.items()) + or receipt.get("materialization_input_sha256") != digest(materialization) + ): + raise S220Error("BUNDLE_MATERIALIZATION_RECEIPT", f"materialization receipt differs: {cohort_id}") + if status == "EXECUTABLE": + if receipt.get("verification_status") != "PASS" or cohort.get("reason_codes") != []: + raise S220Error("BUNDLE_EXECUTABLE_STATUS", f"executable cohort is not PASS: {cohort_id}") + for member in member_slices: + cluster_id = str(member["cluster_id"]) + expected_path = f"context/cluster_slices/{cluster_id}.json" + if ( + cluster_id in member_wave_by_cluster + or cluster_id not in slice_raw_hashes + or member.get("path") != expected_path + or member.get("sha256") != slice_raw_hashes[cluster_id] + or not isinstance(member.get("dependency_wave_ordinal"), int) + ): + raise S220Error("BUNDLE_SLICE_LINK", f"bundle slice link differs: {cluster_id}") + member_wave_by_cluster[cluster_id] = int(member["dependency_wave_ordinal"]) + if len(cohort_ids) != len(set(cohort_ids)): + raise S220Error("BUNDLE_COHORT_DUPLICATE", "bundle cohort IDs must be unique") + if ( + executable_ids != bundle_plan.get("executable_bundle_cohort_ids") + or non_executable_ids != bundle_plan.get("non_executable_bundle_cohort_ids") + or set(member_wave_by_cluster) != set(cluster_ids) + ): + raise S220Error("BUNDLE_EXECUTABLE_COVERAGE", "bundle executable coverage differs") + expected_wave_ordinals = list(range(len(wave_paths))) + if sorted(set(member_wave_by_cluster.values())) != expected_wave_ordinals: + raise S220Error("BUNDLE_WAVE_ORDINALS", "bundle wave ordinals are not contiguous/exact") + + status = _require_mapping( + loaded(fixed["s2_10_status"], "schemas/s2_10.schema.json#/$defs/global_publish_status"), + code="S2_10_STATUS_SHAPE", + label="S2_10 publish status", + ) + if ( + status.get("status") != "COMPLETE" + or status.get("route") != "TO_S2_20" + or status.get("run_binding_digest") != request["run_binding_digest"] + or status.get("parent_stage2_release_sha256") != request["expected_parent_stage2_release_sha256"] + or status.get("s2_10_release_sha256") != request["expected_s2_10_release_sha256"] + or status.get("status_written_last") is not True + or sorted(status.get("expected_executable_cluster_ids", [])) != sorted(cluster_ids) + or sorted(status.get("valid_domain_verdict_cluster_ids", [])) != sorted(cluster_ids) + or status.get("terminal_technical_failure_cluster_ids") != [] + ): + raise S220Error("S2_10_BARRIER", "S2_10 global barrier differs") + _require_object_self_hash( + status, "status_sha256", code="S2_10_STATUS_SELF_DIGEST", label="S2_10 publish status" + ) + producer = child_release.get("producer_contract", {}) + producer_digest = producer.get("s2_10_producer_contract_digest") if isinstance(producer, dict) else None + require_hex(producer_digest, code="S2_10_PRODUCER_CONTRACT") + + verdicts: list[Mapping[str, Any]] = [] + verdict_sources: dict[str, dict[str, str]] = {} + item_receipts: dict[str, Mapping[str, Any]] = {} + issue_parts: dict[str, Mapping[str, Any]] = {} + assumption_parts: dict[str, Mapping[str, Any]] = {} + usage_parts: dict[str, Mapping[str, Any]] = {} + request_ids: set[str] = set() + for cluster_id in sorted(cluster_ids): + slice_path = f"{root}/context/cluster_slices/{cluster_id}.json" + verdict_path = f"{root}/map_s2_10/domain_verdicts/{cluster_id}.json" + item_path = f"{root}/map_s2_10/item_receipts/{cluster_id}.json" + issue_path = f"{root}/map_s2_10/issue_patches/{cluster_id}.json" + assumption_path = f"{root}/map_s2_10/assumption_parts/{cluster_id}.json" + usage_path = f"{root}/map_s2_10/usage_parts/{cluster_id}.json" + verdict = _require_mapping( + loaded(verdict_path, "schemas/s2_10.schema.json#/$defs/canonical_domain_verdict"), + code="DOMAIN_VERDICT_SHAPE", + label=cluster_id, + ) + verdict_hash = _require_object_self_hash( + verdict, "domain_verdict_sha256", code="DOMAIN_VERDICT_SELF_DIGEST", label=cluster_id + ) + if ( + verdict.get("cluster_id") != cluster_id + or verdict.get("run_binding_digest") != request["run_binding_digest"] + or verdict.get("cluster_slice_sha256") != digest(all_raw[slice_path]) + or verdict.get("producer_contract_digest") != producer_digest + ): + raise S220Error("DOMAIN_VERDICT_LINK", f"domain verdict link differs: {cluster_id}") + item = _require_mapping( + loaded(item_path, "schemas/s2_10.schema.json#/$defs/item_receipt"), + code="ITEM_RECEIPT_SHAPE", + label=item_path, + ) + _require_object_self_hash( + item, "receipt_sha256", code="ITEM_RECEIPT_SELF_DIGEST", label=cluster_id + ) + if ( + item.get("cluster_id") != cluster_id + or item.get("run_binding_digest") != request["run_binding_digest"] + or item.get("domain_verdict_path") != f"map_s2_10/domain_verdicts/{cluster_id}.json" + or item.get("domain_verdict_sha256") != verdict_hash + or item.get("read_back_sha256") != verdict_hash + or item.get("validation_status") != "PASS" + or item.get("persistence_status") not in {"PUBLISHED", "IDEMPOTENT_BYTE_IDENTICAL"} + or item.get("request_id") != verdict.get("request_id") + or item.get("wave_ordinal") != verdict.get("wave_ordinal") + or item.get("attempt_no") != verdict.get("attempt_no") + or item.get("raw_model_output_sha256") != verdict.get("raw_model_output_sha256") + ): + raise S220Error("ITEM_RECEIPT_INVALID", f"invalid S2_10 item receipt: {cluster_id}") + issue = _require_mapping( + loaded(issue_path, "schemas/s2_10.schema.json#/$defs/issue_part"), + code="ISSUE_PART_SHAPE", + label=issue_path, + ) + assumption = _require_mapping( + loaded(assumption_path, "schemas/s2_10.schema.json#/$defs/assumption_part"), + code="ASSUMPTION_PART_SHAPE", + label=assumption_path, + ) + usage = _require_mapping( + loaded(usage_path, "schemas/s2_10.schema.json#/$defs/usage_part"), + code="USAGE_PART_SHAPE", + label=usage_path, + ) + for part, expected_hash, label in ( + (issue, item.get("issue_part_sha256"), "issue"), + (assumption, item.get("assumption_part_sha256"), "assumption"), + (usage, item.get("usage_part_sha256"), "usage"), + ): + part_hash = _require_object_self_hash( + part, "part_sha256", code=f"{label.upper()}_PART_SELF_DIGEST", label=cluster_id + ) + header = part.get("header") + if ( + part_hash != expected_hash + or not isinstance(header, dict) + or header.get("producer_contract_digest") != producer_digest + or header.get("request_id") != item.get("request_id") + or header.get("run_binding_digest") != request["run_binding_digest"] + or header.get("wave_ordinal") != item.get("wave_ordinal") + or header.get("attempt_no") != item.get("attempt_no") + or header.get("cluster_id") != cluster_id + or header.get("domain_verdict_sha256") != verdict_hash + ): + raise S220Error(f"{label.upper()}_PART_LINK", f"{label} part link differs: {cluster_id}") + if assumption.get("assumptions") != verdict.get("assumptions"): + raise S220Error("ASSUMPTION_PART_CONTENT", f"assumption part differs from verdict: {cluster_id}") + request_ids.add(str(item.get("request_id"))) + verdicts.append(verdict) + verdict_sources[cluster_id] = { + "path": verdict_path.removeprefix(f"{root}/"), + "sha256": digest(all_raw[verdict_path]), + } + item_receipts[cluster_id] = item + issue_parts[cluster_id] = issue + assumption_parts[cluster_id] = assumption + usage_parts[cluster_id] = usage + if len(request_ids) != 1: + raise S220Error("S2_10_REQUEST_ID_SET", "all S2_10 items must share one request ID") + + wave_receipts: list[Mapping[str, Any]] = [] + wave_cluster_union: set[str] = set() + for ordinal, path in enumerate(wave_paths): + wave = _require_mapping( + loaded(path, "schemas/s2_10.schema.json#/$defs/wave_receipt"), + code="WAVE_RECEIPT_SHAPE", + label=path, + ) + _require_object_self_hash( + wave, "receipt_sha256", code="WAVE_RECEIPT_SELF_DIGEST", label=path + ) + expected_clusters = sorted( + cluster_id for cluster_id, wave_ordinal in member_wave_by_cluster.items() + if wave_ordinal == ordinal + ) + if ( + wave.get("request_id") not in request_ids + or wave.get("run_binding_digest") != request["run_binding_digest"] + or wave.get("wave_ordinal") != ordinal + or wave.get("is_final_wave") != (ordinal == len(wave_paths) - 1) + or sorted(wave.get("expected_wave_cluster_ids", [])) != expected_clusters + or sorted(wave.get("valid_domain_verdict_cluster_ids", [])) != expected_clusters + or wave.get("terminal_technical_failure_cluster_ids") != [] + or wave.get("wave_status") != "WAVE_COMPLETE" + or wave.get("route") != ("TO_S2_20" if ordinal == len(wave_paths) - 1 else "NEXT_WAVE") + or wave.get("written_once") is not True + ): + raise S220Error("WAVE_RECEIPT_INVALID", f"terminal wave receipt differs: {ordinal}") + for cluster_id in expected_clusters: + if item_receipts[cluster_id].get("wave_ordinal") != ordinal: + raise S220Error("ITEM_WAVE_LINK", f"item/wave ordinal differs: {cluster_id}") + wave_cluster_union.update(expected_clusters) + wave_receipts.append(wave) + if wave_cluster_union != set(cluster_ids): + raise S220Error("WAVE_CLUSTER_COVERAGE", "terminal wave coverage differs") + if status.get("terminal_wave_receipt_sha256s") != [ + wave["receipt_sha256"] for wave in wave_receipts + ]: + raise S220Error("WAVE_RECEIPT_HASH_LINK", "global status wave hashes differ") + + return { + "ingress_status": ingress, + "cluster_plan": cluster_plan, + "bundle_plan": bundle_plan, + "cluster_slices": cluster_slices, + "verdicts": verdicts, + "verdict_sources": verdict_sources, + "item_receipts": item_receipts, + "issue_patches": issue_parts, + "assumption_parts": assumption_parts, + "usage_parts": usage_parts, + "wave_receipts": wave_receipts, + "case_context": context_values["case_context"], + "evidence_inventory": context_values["evidence_inventory"], + "object_registry": context_values["object_registry"], + "party_context": context_values["party_context"], + "slot_crosswalk": context_values["slot_crosswalk"], + "issue_ledger": issue_ledger, + } + + + def hydrate( + localdocs: McpClient, + request: Mapping[str, Any], + publication_guard: dict[str, bool], + ) -> tuple[dict[str, Any], dict[str, Any], str]: + root = request["stage2_run_root_ref"] + fixed = { + "release": f"{ASSET_ROOT}/manifest/stage2_release.json", + "module_manifest": f"{ASSET_ROOT}/manifest/module_manifest.json", + "s2_10_release": f"{ASSET_ROOT}/manifest/s2_10_release.json", + "authority_release": f"{ASSET_ROOT}/manifest/authority_release.json", + "case_coverage": f"{ASSET_ROOT}/manifest/case_type_coverage.json", + "binding": f"{ASSET_ROOT}/deployment/stage2_code_executor_binding.yml", + "agent": f"{ASSET_ROOT}/agent_scripts/Stage_2_S2_20.yml", + "inline_receipt": f"{ASSET_ROOT}/manifest/s2_20_inline_code_receipt.json", + "admission": request["detached_admission_receipt_ref"], + "host_cas": request["host_cas_receipt_ref"], + "ingress_status": f"{root}/ingress/ingress_status.json", + "case_context": f"{root}/context/case_context.json", + "evidence_inventory": f"{root}/context/evidence_inventory.json", + "object_registry": f"{root}/context/object_registry.json", + "s2_10_status": f"{root}/map_s2_10/s2_10_publish_status.json", + "cluster_plan": f"{root}/context/cluster_plan.json", + "party_context": f"{root}/context/party_and_title_context.json", + "slot_crosswalk": f"{root}/context/slot_crosswalk.json", + "bundle_plan": f"{root}/context/bundle_plan.json", + "issue_ledger": f"{root}/review/issue_ledger.base.json", + "case_registry": f"{ASSET_ROOT}/manifest/case_type_registry.yml", + "rule_registry": f"{ASSET_ROOT}/manifest/case_type_rule_registry.yml", + "signature_projection": f"{ASSET_ROOT}/registry/binding/binding_signature_projection.yml", + "disposition_policy": f"{ASSET_ROOT}/registry/planning/option_disposition_policy.yml", + "party_rules": f"{ASSET_ROOT}/registry/party/party_set_rules.yml", + "relief_rules": f"{ASSET_ROOT}/registry/drafting/case_type_relief_rules.yml", + "review_policy": f"{ASSET_ROOT}/registry/review/review_policy_registry.yml", + "authority_registry": f"{ASSET_ROOT}/registry/authority/authority_registry.yml", + "corpus_scope": f"{ASSET_ROOT}/registry/corpus/requirement_fact_scope.yml", + "activation": f"{ASSET_ROOT}/registry/calculations/calculation_activation_registry.yml", + "general_law_values": f"{ASSET_ROOT}/law_values/general_law_values.yml", + "court_fee_values": f"{ASSET_ROOT}/law_values/court_fee_values.yml", + "corpus_release": f"{ASSET_ROOT}/manifest/corpus_release.json", + "schema_relief_plan": f"{ASSET_ROOT}/schemas/relief_plan.schema.json", + "schema_binding_retrieval": f"{ASSET_ROOT}/schemas/binding_retrieval.schema.json", + "schema_calculation": f"{ASSET_ROOT}/schemas/calculation.schema.json", + "schema_corpus_collection": f"{ASSET_ROOT}/corpus/weaviate_collection.schema.json", + "schema_ingress": f"{ASSET_ROOT}/schemas/ingress.schema.json", + "schema_context": f"{ASSET_ROOT}/schemas/context.schema.json", + "schema_s2_10": f"{ASSET_ROOT}/schemas/s2_10.schema.json", + "schema_review_status": f"{ASSET_ROOT}/schemas/review_status.schema.json", + "schema_deployment": f"{ASSET_ROOT}/schemas/deployment.schema.json", + "inheritance_reserved_share": f"{ASSET_ROOT}/registry/temporal/inheritance_reserved_share.yml", + "forbidden_relief_rules": f"{ASSET_ROOT}/registry/drafting/forbidden_relief_rules.yml", + "counter_performance_rules": f"{ASSET_ROOT}/registry/drafting/counter_performance_rules.yml", + "procedural_declaration_rules": f"{ASSET_ROOT}/registry/drafting/procedural_declaration_rules.yml", + "actio_route_registry": f"{ASSET_ROOT}/routing/actio_pauliana_route_registry.yml", + "actio_mortgage_route": f"{ASSET_ROOT}/routing/actio_pauliana_mortgage_route.yml", + "actio_value_invariants": f"{ASSET_ROOT}/validators/actio_value_compensation_invariants.yml", + "renderer_R01": f"{ASSET_ROOT}/renderers/R01_money_payment.yml", + "renderer_R02": f"{ASSET_ROOT}/renderers/R02_delivery_possession.yml", + "renderer_R03": f"{ASSET_ROOT}/renderers/R03_declaration_registry.yml", + "renderer_R04": f"{ASSET_ROOT}/renderers/R04_special_nonmoney_performance.yml", + "renderer_R05": f"{ASSET_ROOT}/renderers/R05_declaratory.yml", + "renderer_R06": f"{ASSET_ROOT}/renderers/R06_constitutive_judgment_challenge.yml", + } + seed, _ = read_stable(localdocs, fixed.values()) + if digest(seed[fixed["release"]]) != EXPECTED_STAGE2_RELEASE_SHA256 or digest(seed[fixed["release"]]) != request["expected_parent_stage2_release_sha256"]: + raise S220Error("PARENT_RELEASE_HASH_MISMATCH", "parent release bytes differ") + if digest(seed[fixed["s2_10_status"]]) != request["expected_s2_10_publish_status_sha256"]: + raise S220Error("S2_10_STATUS_HASH_MISMATCH", "S2_10 publish status differs") + if digest(seed[fixed["host_cas"]]) != request["host_cas_receipt_sha256"]: + raise S220Error("HOST_CAS_HASH_MISMATCH", "host CAS receipt differs") + seed_schema_documents = build_schema_documents(seed, fixed) + validate_artifact_payload( + canonical_bytes(request), + "schemas/deployment.schema.json#/$defs/s2_20_request", + REQUEST_PATH, + seed_schema_documents, + ) + release, child_release, _ = _verify_external_trust( + seed, fixed, request, seed_schema_documents + ) + publication_guard["external_trust_verified"] = True + release_without_digest = {key: value for key, value in release.items() if key != "release_digest"} + if release.get("release_digest") != digest(release_without_digest): + raise S220Error("PARENT_RELEASE_SELF_DIGEST", "parent release self digest differs") + module_manifest = _require_mapping( + load_json(seed[fixed["module_manifest"]], label=fixed["module_manifest"]), + code="MODULE_MANIFEST_SHAPE", + label="module manifest", + ) + module_ref = release.get("module_manifest_ref", {}) + if not isinstance(module_ref, dict) or module_ref.get("sha256") != digest(seed[fixed["module_manifest"]]): + raise S220Error("MODULE_MANIFEST_RELEASE_BINDING", "parent release does not bind the module manifest") + module_rows = module_manifest.get("modules", module_manifest.get("module_rows", [])) + by_module_path = { + str(row.get("path")): row + for row in module_rows + if isinstance(row, dict) and isinstance(row.get("path"), str) + } + release_bound_keys = ( + "case_registry", "rule_registry", "signature_projection", "disposition_policy", + "party_rules", "relief_rules", "review_policy", "authority_registry", + "corpus_scope", "activation", "authority_release", "corpus_release", "case_coverage", + "general_law_values", "court_fee_values", + "schema_relief_plan", "schema_binding_retrieval", "schema_calculation", "schema_corpus_collection", + "schema_ingress", "schema_context", "schema_s2_10", "schema_review_status", "schema_deployment", + "inheritance_reserved_share", "forbidden_relief_rules", "counter_performance_rules", + "procedural_declaration_rules", "actio_route_registry", "actio_mortgage_route", + "actio_value_invariants", + "renderer_R01", "renderer_R02", "renderer_R03", "renderer_R04", "renderer_R05", "renderer_R06", + ) + for key in release_bound_keys: + relative = fixed[key].removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(seed[fixed[key]]): + raise S220Error("MODULE_MEMBER_HASH_MISMATCH", f"module manifest does not bind: {relative}") + for key in ("schema_context", "schema_s2_10", "schema_review_status"): + relative = fixed[key].removeprefix(f"{ASSET_ROOT}/") + if _sealed_child_asset_hash(child_release, relative) != digest(seed[fixed[key]]): + raise S220Error( + "S2_10_CHILD_SCHEMA_HASH_MISMATCH", + f"S2_10 child release does not bind schema: {relative}", + ) + cluster_plan = load_json(seed[fixed["cluster_plan"]], label=fixed["cluster_plan"]) + _validate_loaded_value( + cluster_plan, + seed[fixed["cluster_plan"]], + "schemas/context.schema.json#/$defs/cluster_plan", + label=fixed["cluster_plan"], + schema_documents=seed_schema_documents, + ) + ingress_status = load_json(seed[fixed["ingress_status"]], label=fixed["ingress_status"]) + _validate_loaded_value( + ingress_status, + seed[fixed["ingress_status"]], + "schemas/ingress.schema.json#/$defs/ingress_status", + label=fixed["ingress_status"], + schema_documents=seed_schema_documents, + ) + bundle_plan = load_json(seed[fixed["bundle_plan"]], label=fixed["bundle_plan"]) + _validate_loaded_value( + bundle_plan, + seed[fixed["bundle_plan"]], + "schemas/context.schema.json#/$defs/bundle_plan", + label=fixed["bundle_plan"], + schema_documents=seed_schema_documents, + ) + status = load_json(seed[fixed["s2_10_status"]], label=fixed["s2_10_status"]) + if not isinstance(status, dict) or status.get("status") != "COMPLETE" or status.get("route") != "TO_S2_20": + raise S220Error("S2_10_BARRIER", "S2_10 must be COMPLETE/TO_S2_20") + cluster_ids = cluster_plan.get("executable_cluster_ids", []) if isinstance(cluster_plan, dict) else [] + if ( + not isinstance(cluster_ids, list) + or any(not isinstance(item, str) or CLUSTER_ID.fullmatch(item) is None for item in cluster_ids) + ): + raise S220Error("CLUSTER_PLAN_SHAPE", "executable_cluster_ids must be canonical cluster IDs") + dynamic_paths: list[str] = [] + seed_activation = _require_mapping(load_json(seed[fixed["activation"]], label=fixed["activation"]), code="ACTIVATION_REGISTRY_SHAPE", label="activation registry") + descriptor_paths: dict[str, str] = {} + for descriptor in seed_activation.get("calculator_descriptors", []): + if not isinstance(descriptor, dict) or not isinstance(descriptor.get("calculator_id"), str) or not isinstance(descriptor.get("descriptor_path"), str): + raise S220Error("CALCULATOR_DESCRIPTOR_SHAPE", "calculator descriptor ID/path required") + path = join_path(ASSET_ROOT, descriptor["descriptor_path"]) + descriptor_paths[descriptor["calculator_id"]] = path + dynamic_paths.append(path) + seed_rule_registry = _require_mapping( + load_json(seed[fixed["rule_registry"]], label=fixed["rule_registry"]), + code="RULE_REGISTRY_SHAPE", + label="rule registry", + ) + release_selected_legal_paths: set[str] = set() + for registry_key, rows_key in ( + ("case_registry", "rows"), + ("party_rules", "rules"), + ("corpus_scope", "rows"), + ): + registry_value = _require_mapping( + load_json(seed[fixed[registry_key]], label=fixed[registry_key]), + code="LEGAL_REGISTRY_SHAPE", + label=registry_key, + ) + for legal_row in registry_value.get(rows_key, []): + if isinstance(legal_row, dict) and isinstance(legal_row.get("legal_review_receipt_ref"), str): + legal_review_ref = safe_path(str(legal_row["legal_review_receipt_ref"])) + if LEGAL_REVIEW_PATH.fullmatch(legal_review_ref) is None: + raise S220Error( + "LEGAL_REVIEW_PATH_FAMILY", + f"legal review receipt is outside the closed family: {legal_review_ref}", + ) + release_selected_legal_paths.add( + join_path(ASSET_ROOT, legal_review_ref) + ) + for rule_row in seed_rule_registry.get("rows", []): + if not isinstance(rule_row, dict): + continue + for branch in rule_row.get("rule_branches", []): + if not isinstance(branch, dict): + continue + for branch_path_key in ("markdown_source_path", "legal_review_receipt_ref"): + if isinstance(branch.get(branch_path_key), str): + branch_ref = safe_path(str(branch[branch_path_key])) + path_family = RELIEF_RULE_PATH if branch_path_key == "markdown_source_path" else LEGAL_REVIEW_PATH + if path_family.fullmatch(branch_ref) is None: + raise S220Error( + "LEGAL_ASSET_PATH_FAMILY", + f"{branch_path_key} is outside the closed family: {branch_ref}", + ) + release_selected_legal_paths.add( + join_path(ASSET_ROOT, branch_ref) + ) + dynamic_paths.extend(sorted(release_selected_legal_paths)) + for cluster_id in sorted(cluster_ids): + dynamic_paths.extend( + [ + f"{root}/context/cluster_slices/{cluster_id}.json", + f"{root}/map_s2_10/domain_verdicts/{cluster_id}.json", + f"{root}/map_s2_10/item_receipts/{cluster_id}.json", + f"{root}/map_s2_10/issue_patches/{cluster_id}.json", + f"{root}/map_s2_10/assumption_parts/{cluster_id}.json", + f"{root}/map_s2_10/usage_parts/{cluster_id}.json", + ] + ) + bundle_wave_ordinals = sorted({ + int(member["dependency_wave_ordinal"]) + for cohort in bundle_plan.get("cohorts", []) + if isinstance(cohort, dict) and cohort.get("cohort_status") == "EXECUTABLE" + for member in cohort.get("member_slices", []) + if isinstance(member, dict) and isinstance(member.get("dependency_wave_ordinal"), int) + }) + if ( + not bundle_wave_ordinals + or bundle_wave_ordinals[-1] >= len(cluster_ids) + or bundle_wave_ordinals != list(range(bundle_wave_ordinals[-1] + 1)) + ): + raise S220Error("BUNDLE_WAVE_ORDINALS", "executable bundle wave ordinals must be contiguous from zero") + wave_paths = [ + f"{root}/map_s2_10/wave_receipts/wave-{ordinal:04d}.json" + for ordinal in bundle_wave_ordinals + ] + dynamic_paths.extend(wave_paths) + all_raw, snapshot_digest = read_stable(localdocs, list(fixed.values()) + dynamic_paths) + schema_documents = build_schema_documents(all_raw, fixed) + upstream = validate_upstream_handoff( + all_raw, + fixed, + request, + root, + cluster_ids, + wave_paths, + schema_documents, + child_release, + ) + activation = load_json(all_raw[fixed["activation"]], label=fixed["activation"]) + case_registry = _require_mapping(load_json(all_raw[fixed["case_registry"]], label=fixed["case_registry"]), code="CASE_REGISTRY_SHAPE", label="case registry") + rule_registry = _require_mapping(load_json(all_raw[fixed["rule_registry"]], label=fixed["rule_registry"]), code="RULE_REGISTRY_SHAPE", label="rule registry") + case_rows = case_registry.get("rows", case_registry.get("case_types", [])) + rule_rows = rule_registry.get("rows", rule_registry.get("case_type_rule_rows", [])) + if not isinstance(case_rows, list) or len(case_rows) != 137 or len({row.get("case_type_id") for row in case_rows if isinstance(row, dict)}) != 137: + raise S220Error("CASE_REGISTRY_137_CLOSURE", "case registry must contain exactly 137 unique case_type_id rows") + if not isinstance(rule_rows, list) or len(rule_rows) != 137 or {row.get("case_type_id") for row in rule_rows if isinstance(row, dict)} != {row.get("case_type_id") for row in case_rows if isinstance(row, dict)}: + raise S220Error("RULE_REGISTRY_137_CLOSURE", "rule registry must cover the exact case registry set") + if not isinstance(activation, dict) or len(activation.get("calculator_descriptors", [])) != 17: + raise S220Error("CALCULATOR_17_CLOSURE", "calculation registry must enumerate CE-01..13 and CE-R1..R4") + for path in descriptor_paths.values(): + relative = path.removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(all_raw[path]): + raise S220Error("CALCULATOR_MODULE_HASH_MISMATCH", f"calculator descriptor is not release-bound: {relative}") + for path in sorted(release_selected_legal_paths): + relative = path.removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(all_raw[path]): + raise S220Error( + "LEGAL_ASSET_MODULE_HASH_MISMATCH", + f"selected legal asset is not release-bound: {relative}", + ) + inputs = { + **upstream, + "issue_ledger_raw": all_raw[fixed["issue_ledger"]], + } + corpus_release = _require_mapping(load_json(all_raw[fixed["corpus_release"]], label=fixed["corpus_release"]), code="CORPUS_RELEASE_SHAPE", label="corpus release") + signature_projection_registry = _require_mapping( + load_json(all_raw[fixed["signature_projection"]], label=fixed["signature_projection"]), + code="SIGNATURE_PROJECTION_REGISTRY_SHAPE", + label="signature projection registry", + ) + disposition_policy = _require_mapping( + load_json(all_raw[fixed["disposition_policy"]], label=fixed["disposition_policy"]), + code="DISPOSITION_POLICY_SHAPE", + label="option disposition policy", + ) + party_rules = _require_mapping( + load_json(all_raw[fixed["party_rules"]], label=fixed["party_rules"]), + code="PARTY_RULES_SHAPE", + label="party-set rules", + ) + corpus_scope = _require_mapping( + load_json(all_raw[fixed["corpus_scope"]], label=fixed["corpus_scope"]), + code="CORPUS_SCOPE_SHAPE", + label="requirement-fact scope registry", + ) + special_asset_contracts = { + "inheritance_reserved_share": ( + "stage2.s2_20.inheritance_reserved_share_temporal.v1", + "registry_id", "S2-20-INHERITANCE-RESERVED-SHARE-TEMPORAL", "branch_rows", + ), + "forbidden_relief_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-FORBIDDEN-RELIEF-RULES", "rule_rows", + ), + "counter_performance_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-COUNTER-PERFORMANCE-RULES", "rule_rows", + ), + "procedural_declaration_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-PROCEDURAL-DECLARATION-RULES", "rule_rows", + ), + "actio_route_registry": ( + "stage2.s2_20.actio_pauliana_route_registry.v1", + "registry_id", "S2-20-ACTIO-PAULIANA-ROUTES", "route_rows", + ), + "actio_mortgage_route": ( + "stage2.s2_20.actio_pauliana_mortgage_route.v1", + "registry_id", "S2-20-ACTIO-MORTGAGE-ROUTES", "route_rows", + ), + "actio_value_invariants": ( + "stage2.s2_20.actio_value_compensation_invariants.v1", + "validator_id", "S2-20-ACTIO-VALUE-COMPENSATION-INVARIANTS", "invariant_rows", + ), + } + special_legal_assets = { + key: _validate_special_legal_asset( + load_json(all_raw[fixed[key]], label=fixed[key]), + schema_version=contract[0], + identity_field=contract[1], + identity=contract[2], + rows_field=contract[3], + label=fixed[key], + ) + for key, contract in special_asset_contracts.items() + } + assets = { + "case_registry": case_registry, + "rule_registry": rule_registry, + "activation_registry": activation, + "corpus_release": corpus_release, + "corpus_scope": corpus_scope, + "party_rules": party_rules, + "relief_rules": load_json(all_raw[fixed["relief_rules"]], label=fixed["relief_rules"]), + "review_policy": load_json(all_raw[fixed["review_policy"]], label=fixed["review_policy"]), + "signature_projection_registry": signature_projection_registry, + "disposition_policy": disposition_policy, + "special_legal_assets": special_legal_assets, + "party_rules_eligible": party_rules.get("registry_status") == "APPROVED", + "signature_projection_eligible": signature_projection_registry.get("registry_status") == "APPROVED", + "disposition_policy_eligible": disposition_policy.get("policy_status") == "APPROVED", + "authority_admitted": load_json(all_raw[fixed["authority_release"]], label=fixed["authority_release"]).get("status") == "ADMITTED", + "asset_hashes": { + "case_registry": digest(all_raw[fixed["case_registry"]]), + "rule_registry": digest(all_raw[fixed["rule_registry"]]), + "signature_projection": digest(all_raw[fixed["signature_projection"]]), + "corpus_release": digest(all_raw[fixed["corpus_release"]]), + "relief_rules": digest(all_raw[fixed["relief_rules"]]), + "party_rules": digest(all_raw[fixed["party_rules"]]), + "review_policy": digest(all_raw[fixed["review_policy"]]), + "corpus_scope": digest(all_raw[fixed["corpus_scope"]]), + **{ + key: digest(all_raw[fixed[key]]) + for key in sorted(special_legal_assets) + }, + }, + "calculator_descriptor_hashes": { + calculator_id: digest(all_raw[path]) for calculator_id, path in sorted(descriptor_paths.items()) + }, + "calculator_descriptors": { + calculator_id: load_json(all_raw[path], label=path) + for calculator_id, path in sorted(descriptor_paths.items()) + }, + "law_value_tables": [ + load_json(all_raw[fixed["general_law_values"]], label=fixed["general_law_values"]), + load_json(all_raw[fixed["court_fee_values"]], label=fixed["court_fee_values"]), + ], + "renderer_descriptors": { + renderer_id: load_json( + all_raw[fixed[f"renderer_{renderer_id}"]], + label=fixed[f"renderer_{renderer_id}"], + ) + for renderer_id in ("R01", "R02", "R03", "R04", "R05", "R06") + }, + "renderer_hashes": { + renderer_id: digest(all_raw[fixed[f"renderer_{renderer_id}"]]) + for renderer_id in ("R01", "R02", "R03", "R04", "R05", "R06") + }, + "release_selected_legal_bytes": { + path.removeprefix(f"{ASSET_ROOT}/"): all_raw[path] + for path in sorted(release_selected_legal_paths) + }, + "schema_documents": schema_documents, + } + inline_receipt = _require_mapping(load_json(all_raw[fixed["inline_receipt"]], label=fixed["inline_receipt"]), code="INLINE_RECEIPT_SHAPE", label="inline receipt") + canonical_code = inline_receipt.get("canonical_code", {}) + code_sha256 = canonical_code.get("code_sha256") if isinstance(canonical_code, dict) else None + assets["s2_20_code_sha256"] = require_hex(code_sha256, code="INLINE_CODE_HASH_INVALID") + return inputs, assets, snapshot_digest + + + def _artifact_ref(path: str, schema_ref: str, payload: bytes) -> dict[str, str]: + return {"path": path, "schema_ref": schema_ref, "sha256": digest(payload)} + + + def artifact_payloads( + result: Mapping[str, Any], request: Mapping[str, Any] + ) -> dict[str, tuple[bytes, str, str]]: + plan = result["plan"] + groups = result["groups"] + claim_groups_base = { + "schema_version": "stage2_claim_groups.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_groups": groups, + "group_dependencies": plan["group_dependencies"], + } + claim_groups = {**claim_groups_base, "document_digest": digest(claim_groups_base)} + bindings = result["bindings"] + binding_base = { + "schema_version": "stage2_case_type_bindings.v1", + "run_binding_digest": request["run_binding_digest"], + "case_type_registry_sha256": result["asset_hashes"]["case_registry"], + "case_type_rule_registry_sha256": result["asset_hashes"]["rule_registry"], + "binding_signature_projection_sha256": result["asset_hashes"]["signature_projection"], + "bindings": bindings, + "bound_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if row["case_type_binding_status"] == "BOUND" and row["sub_rule_binding_status"] == "BOUND"), + "unresolved_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if "UNRESOLVED" in {row["case_type_binding_status"], row["sub_rule_binding_status"]}), + "conflict_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if "CONFLICT" in {row["case_type_binding_status"], row["sub_rule_binding_status"]}), + } + binding_document = {**binding_base, "document_digest": digest(binding_base)} + calculation_state_counts = { + state: sum(row["activation_status"] == state for row in result["calculations"]) + for state in ("ACTIVATED", "NOT_APPLICABLE", "ACTIVATION_GAP", "ACTIVATION_CONFLICT") + } + calculation_report_base = { + "schema_version": "stage2.calculation_report.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_ids": sorted({row["atomic_claim_id"] for row in result["calculations"]}), + "receipt_refs": sorted(f"calculation_receipts/{row['calculation_receipt_id']}.json" for row in result["calculations"]), + "receipt_sha256s": sorted(digest(canonical_bytes(row)) for row in result["calculations"]), + "state_counts": calculation_state_counts, + "missing_operand_ids": sorted({item for row in result["calculations"] for item in row["missing_operands"]}), + "issue_codes": sorted({item for row in result["calculations"] for item in row["issue_codes"]}), + } + calculation_report = {**calculation_report_base, "report_sha256": digest(calculation_report_base)} + filing_metrics_base = { + "schema_version": "stage2.filing_metrics.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": [], + "filing_decision_status": "REVIEW_REQUIRED", + "source_ce13_receipt_ids": [], + } + filing_metrics = {**filing_metrics_base, "report_sha256": digest(filing_metrics_base)} + payloads: dict[str, tuple[bytes, str, str]] = { + "canonical_relief_plan.json": (canonical_bytes(plan), "schemas/relief_plan.schema.json#/$defs/canonical_relief_plan", "C35"), + "claim_groups.json": (canonical_bytes(claim_groups), "schemas/relief_plan.schema.json#/$defs/claim_groups_document", "C30"), + "case_type_bindings.json": (canonical_bytes(binding_document), "schemas/binding_retrieval.schema.json#/$defs/case_type_bindings_document", "C26"), + "issue_ledger.plan.json": (canonical_bytes(result["issues"]), "schemas/relief_plan.schema.json#/$defs/issue_ledger_plan", "C30"), + "calculation_report.json": (canonical_bytes(calculation_report), "schemas/calculation.schema.json#/$defs/calculation_report", "C22"), + "filing_metrics.json": (canonical_bytes(filing_metrics), "schemas/calculation.schema.json#/$defs/filing_metrics", "C22"), + "exhibit_register.json": (canonical_bytes(result["exhibit_register"]), "schemas/relief_plan.schema.json#/$defs/exhibit_register", "C30"), + "option_conservation_report.json": (canonical_bytes(result["option_report"]), "schemas/relief_plan.schema.json#/$defs/option_conservation_report", "C20"), + "party_completeness_report.json": (canonical_bytes(result["party_report"]), "schemas/relief_plan.schema.json#/$defs/party_completeness_report", "C30"), + } + for receipt in result["calculations"]: + payloads[f"calculation_receipts/{receipt['calculation_receipt_id']}.json"] = (canonical_bytes(receipt), "schemas/calculation.schema.json#/$defs/calculation_receipt", "C22") + result_by_atomic = {row["atomic_claim_id"]: row for row in result["retrieval"]["results"]} + receipt_by_atomic = {row["atomic_claim_id"]: row for row in result["retrieval"]["receipts"]} + for group in groups: + group_id = group["claim_group_id"] + group_bindings = [row for row in bindings if row["atomic_claim_id"] in group["atomic_claim_ids"]] + group_rule_refs = [ + result["rule_asset_refs"].get(atomic_id, {}) + for atomic_id in group["atomic_claim_ids"] + ] + def merged_rule_refs(key: str) -> list[dict[str, str]]: + indexed = { + (str(ref["path"]), str(ref["sha256"]), str(ref["json_pointer"])): ref + for row in group_rule_refs + for ref in row.get(key, []) + if isinstance(ref, dict) + } + return [indexed[index] for index in sorted(indexed)] + rule_base = { + "schema_version": "stage2_rule_context_pack.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "binding_rows": group_bindings, + "structured_relief_rule_refs": merged_rule_refs("structured_relief_rule_refs"), + "markdown_source_refs": merged_rule_refs("markdown_source_refs"), + "renderer_refs": merged_rule_refs("renderer_refs"), + "party_set_rule_refs": merged_rule_refs("party_set_rule_refs"), + "ce13_branch_refs": merged_rule_refs("ce13_branch_refs"), + "review_policy_refs": merged_rule_refs("review_policy_refs"), + "authority_class": "RELIEF_ONLY", + } + rule_pack = {**rule_base, "pack_sha256": digest(rule_base)} + group_receipts = [receipt_by_atomic[item] for item in group["atomic_claim_ids"] if item in receipt_by_atomic] + fact_base = { + "schema_version": "stage2_requirement_fact_pack.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "atomic_claim_ids": group["atomic_claim_ids"], + "corpus_release_sha256": result["asset_hashes"]["corpus_release"], + "snapshot_id": str(result["corpus_release"].get("snapshot_id") or "UNAVAILABLE"), + "snapshot_sha256": str(result["corpus_release"].get("snapshot_sha256") or ("0" * 64)), + "retrieval_receipt_refs": [ + {"path": f"retrieval_receipts/{row['atomic_claim_id']}.json", "sha256": digest(canonical_bytes(row)), "json_pointer": ""} + for row in group_receipts + ], + "items": sorted( + [ + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["pack_items"].get(atomic_id, []) + ], + key=lambda row: (row["corpus_element_id"], row["chunk_id"]), + ), + "unmapped_chunk_ids": sorted( + { + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["unmapped_by_atomic"].get(atomic_id, []) + } + ), + "conflicting_chunk_ids": sorted( + { + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["conflicting_by_atomic"].get(atomic_id, []) + } + ), + "authority_class": "CAUSE_AND_ELEMENT_CHECK", + "issue_codes": sorted({code for item in group["atomic_claim_ids"] for code in result_by_atomic[item]["issue_codes"]}), + } + fact_pack = {**fact_base, "pack_sha256": digest(fact_base)} + gap_rows = [{ + "corpus_element_id": "UNRESOLVED", + "doctrine_role": "element", + "stage1_slot_refs": [], + "fact_refs": [], + "evidence_refs": [], + "coverage_status": "GAP", + "issue_refs": group["issue_refs"], + }] + gap_document = { + "schema_version": "stage2_element_evidence_gap.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "rows": gap_rows, + "gap_count": 1, + "conflict_count": 0, + } + rule_path = f"rule_context_packs/{group_id}.json" + fact_path = f"requirement_fact_packs/{group_id}.json" + gap_path = f"element_evidence_gap/{group_id}.json" + rule_raw = canonical_bytes(rule_pack) + fact_raw = canonical_bytes(fact_pack) + gap_raw = canonical_bytes(gap_document) + payloads[rule_path] = (rule_raw, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", "C26") + payloads[fact_path] = (fact_raw, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", "C29") + payloads[gap_path] = (gap_raw, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", "C30") + calculation_refs = [ + _artifact_ref( + f"calculation_receipts/{row['calculation_receipt_id']}.json", + "schemas/calculation.schema.json#/$defs/calculation_receipt", + canonical_bytes(row), + ) + for row in result["calculations"] if row["atomic_claim_id"] in group["atomic_claim_ids"] + ] + group_slice_base = { + "schema_version": "stage2_s2_20_group_slice.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "frozen_plan_sha256": digest(canonical_bytes(plan)), + "claim_group": group, + "rule_context_pack_ref": _artifact_ref(rule_path, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", rule_raw), + "requirement_fact_pack_ref": _artifact_ref(fact_path, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", fact_raw), + "element_evidence_gap_ref": _artifact_ref(gap_path, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", gap_raw), + "calculation_receipt_refs": calculation_refs, + "issue_refs": group["issue_refs"], + "drafting_permission": group["drafting_permission"], + } + group_slice = {**group_slice_base, "group_slice_sha256": digest(group_slice_base)} + group_path = f"group_slices/{group_id}.json" + group_raw = canonical_bytes(group_slice) + payloads[group_path] = (group_raw, "schemas/relief_plan.schema.json#/$defs/group_slice", "C35") + for segment_id, authority_class, refs in ( + ("P31_rule_and_pack", "RELIEF_ONLY_AND_CAUSE_CHECK", [ + _artifact_ref(rule_path, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", rule_raw), + _artifact_ref(fact_path, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", fact_raw), + _artifact_ref(gap_path, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", gap_raw), + ]), + ("P32_common_authority", "COMMON_AUTHORITY", []), + ): + segment_base = { + "schema_version": "stage2_s2_20_prompt_segment.v1", + "segment_id": segment_id, + "claim_group_id": group_id, + "authority_class": authority_class, + "ordered_asset_refs": refs, + "source_group_slice_sha256": digest(group_raw), + } + segment = {**segment_base, "segment_payload_sha256": digest(segment_base)} + payloads[f"prompt_segments/{group_id}/{segment_id}.json"] = (canonical_bytes(segment), "schemas/relief_plan.schema.json#/$defs/prompt_segment", "C35") + for row in result["retrieval"]["queries"]: + payloads[f"query_plans/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/query_plan", "C27") + for row in result["retrieval"]["results"]: + payloads[f"retrieval_results/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/retrieval_result", "C28") + for row in result["retrieval"]["receipts"]: + payloads[f"retrieval_receipts/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/retrieval_receipt", "C28") + return payloads + + + def build_schema_documents( + all_raw: Mapping[str, bytes], fixed: Mapping[str, str] + ) -> dict[str, Mapping[str, Any]]: + """Load release-bound schemas and close their declared external $ref families. + + ``binding_retrieval.schema.json`` deliberately reuses the sealed + Weaviate collection schema. The inline validator has no filesystem + resolver, so this function merges the external definitions into a + private in-memory validation documents and rewrites only declared + relative references. Conflicting shared definitions fail closed. + """ + + relief = _require_mapping( + load_json(all_raw[fixed["schema_relief_plan"]], label=fixed["schema_relief_plan"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="relief plan schema", + ) + binding = _require_mapping( + load_json(all_raw[fixed["schema_binding_retrieval"]], label=fixed["schema_binding_retrieval"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="binding/retrieval schema", + ) + calculation = _require_mapping( + load_json(all_raw[fixed["schema_calculation"]], label=fixed["schema_calculation"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="calculation schema", + ) + corpus = _require_mapping( + load_json(all_raw[fixed["schema_corpus_collection"]], label=fixed["schema_corpus_collection"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="corpus collection schema", + ) + ingress = _require_mapping( + load_json(all_raw[fixed["schema_ingress"]], label=fixed["schema_ingress"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="ingress schema", + ) + context = _require_mapping( + load_json(all_raw[fixed["schema_context"]], label=fixed["schema_context"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="context schema", + ) + s2_10 = _require_mapping( + load_json(all_raw[fixed["schema_s2_10"]], label=fixed["schema_s2_10"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="S2_10 schema", + ) + review = _require_mapping( + load_json(all_raw[fixed["schema_review_status"]], label=fixed["schema_review_status"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="review/status schema", + ) + deployment = _require_mapping( + load_json(all_raw[fixed["schema_deployment"]], label=fixed["schema_deployment"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="deployment schema", + ) + merged_binding = load_json(canonical_bytes(binding), label="binding schema canonical copy") + merged_defs = merged_binding.get("$defs") + corpus_defs = corpus.get("$defs") + if not isinstance(merged_defs, dict) or not isinstance(corpus_defs, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", "binding and corpus schemas require $defs") + for name, definition in corpus_defs.items(): + if name in merged_defs and merged_defs[name] != definition: + raise S220Error( + "ARTIFACT_SCHEMA_EXTERNAL_DEF_CONFLICT", + f"shared schema definition differs: {name}", + ) + merged_defs.setdefault(name, definition) + + external_prefix = "../corpus/weaviate_collection.schema.json#" + + def rewrite_external_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith(external_prefix): + value["$ref"] = "#" + ref[len(external_prefix):] + for child in value.values(): + rewrite_external_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_external_refs(child) + + rewrite_external_refs(merged_binding) + + def merge_namespaced_external_defs( + target: Mapping[str, Any], + source: Mapping[str, Any], + *, + external_prefix: str, + namespace: str, + ) -> dict[str, Any]: + merged = load_json(canonical_bytes(target), label=f"{namespace} target schema copy") + source_copy = load_json(canonical_bytes(source), label=f"{namespace} source schema copy") + merged_defs = merged.get("$defs") + source_defs = source_copy.get("$defs") + if not isinstance(merged_defs, dict) or not isinstance(source_defs, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", "merged schemas require $defs") + + def rewrite_source_local_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith("#/$defs/"): + value["$ref"] = f"#/$defs/{namespace}__{ref[len('#/$defs/'):]}" + for child in value.values(): + rewrite_source_local_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_source_local_refs(child) + + for name, definition in source_defs.items(): + copied = load_json(canonical_bytes(definition), label=f"{namespace} definition copy") + rewrite_source_local_refs(copied) + target_name = f"{namespace}__{name}" + if target_name in merged_defs and merged_defs[target_name] != copied: + raise S220Error( + "ARTIFACT_SCHEMA_EXTERNAL_DEF_CONFLICT", + f"namespaced schema definition differs: {target_name}", + ) + merged_defs[target_name] = copied + + def rewrite_target_external_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith(external_prefix): + fragment = ref[len(external_prefix):] + if not fragment.startswith("#/$defs/"): + raise S220Error( + "ARTIFACT_SCHEMA_REF_UNSUPPORTED", + f"external schema reference is not a $defs pointer: {ref}", + ) + value["$ref"] = f"#/$defs/{namespace}__{fragment[len('#/$defs/'):]}" + for child in value.values(): + rewrite_target_external_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_target_external_refs(child) + + rewrite_target_external_refs(merged) + return merged + + merged_context = merge_namespaced_external_defs( + context, + review, + external_prefix="review_status.schema.json", + namespace="review_status", + ) + merged_review = merge_namespaced_external_defs( + review, + relief, + external_prefix="relief_plan.schema.json", + namespace="relief_plan", + ) + return { + "schemas/relief_plan.schema.json": relief, + "schemas/binding_retrieval.schema.json": merged_binding, + "schemas/calculation.schema.json": calculation, + "schemas/ingress.schema.json": ingress, + "schemas/context.schema.json": merged_context, + "schemas/s2_10.schema.json": s2_10, + "schemas/review_status.schema.json": merged_review, + "schemas/deployment.schema.json": deployment, + } + + + def _schema_pointer(document: Any, fragment: str) -> Any: + if fragment in {"", "#"}: + return document + pointer = fragment[1:] if fragment.startswith("#") else fragment + if not pointer.startswith("/"): + raise S220Error("ARTIFACT_SCHEMA_REF_UNSUPPORTED", f"non-pointer schema fragment: {fragment}") + current = document + for raw_token in pointer[1:].split("/"): + token = raw_token.replace("~1", "/").replace("~0", "~") + try: + current = current[int(token)] if isinstance(current, list) else current[token] + except (KeyError, IndexError, TypeError, ValueError) as exc: + raise S220Error("ARTIFACT_SCHEMA_REF_UNRESOLVED", f"schema pointer is unresolved: {fragment}") from exc + return current + + + def _schema_type_matches(instance: Any, expected: str) -> bool: + if expected == "null": + return instance is None + if expected == "boolean": + return isinstance(instance, bool) + if expected == "object": + return isinstance(instance, dict) + if expected == "array": + return isinstance(instance, list) + if expected == "string": + return isinstance(instance, str) + if expected == "integer": + return isinstance(instance, int) and not isinstance(instance, bool) + if expected == "number": + return isinstance(instance, (int, float)) and not isinstance(instance, bool) + raise S220Error("ARTIFACT_SCHEMA_KEYWORD_UNSUPPORTED", f"unsupported JSON Schema type: {expected}") + + + def _schema_identity(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + + def _schema_errors( + instance: Any, + schema: Any, + root_schema: Mapping[str, Any], + instance_path: str, + stack: set[tuple[int, int]], + ) -> list[str]: + if schema is True: + return [] + if schema is False: + return [f"{instance_path}: boolean schema false"] + if not isinstance(schema, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"schema is not an object/boolean at {instance_path}") + marker = (id(schema), id(instance)) + if marker in stack: + return [] + nested_stack = set(stack) + nested_stack.add(marker) + errors: list[str] = [] + + if "$ref" in schema: + ref = schema["$ref"] + if not isinstance(ref, str) or not ref.startswith("#"): + raise S220Error("ARTIFACT_SCHEMA_REF_UNSUPPORTED", f"only local schema refs are allowed: {ref!r}") + errors.extend( + _schema_errors( + instance, + _schema_pointer(root_schema, ref), + root_schema, + instance_path, + nested_stack, + ) + ) + + for branch in schema.get("allOf", []): + errors.extend(_schema_errors(instance, branch, root_schema, instance_path, nested_stack)) + if "anyOf" in schema: + branches = [ + _schema_errors(instance, branch, root_schema, instance_path, nested_stack) + for branch in schema["anyOf"] + ] + if not any(not branch for branch in branches): + errors.append(f"{instance_path}: anyOf matched no branch") + if "oneOf" in schema: + branches = [ + _schema_errors(instance, branch, root_schema, instance_path, nested_stack) + for branch in schema["oneOf"] + ] + matched = sum(not branch for branch in branches) + if matched != 1: + errors.append(f"{instance_path}: oneOf expected one match, got {matched}") + if "not" in schema and not _schema_errors( + instance, schema["not"], root_schema, instance_path, nested_stack + ): + errors.append(f"{instance_path}: not subschema matched") + if "if" in schema: + condition_matches = not _schema_errors( + instance, schema["if"], root_schema, instance_path, nested_stack + ) + selected = "then" if condition_matches else "else" + if selected in schema: + errors.extend( + _schema_errors(instance, schema[selected], root_schema, instance_path, nested_stack) + ) + + expected_type = schema.get("type") + if expected_type is not None: + expected_types = [expected_type] if isinstance(expected_type, str) else expected_type + if not isinstance(expected_types, list) or not all(isinstance(item, str) for item in expected_types): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"invalid type declaration at {instance_path}") + if not any(_schema_type_matches(instance, item) for item in expected_types): + errors.append(f"{instance_path}: type mismatch; expected {expected_types}") + return errors + + if "const" in schema and instance != schema["const"]: + errors.append(f"{instance_path}: const mismatch") + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{instance_path}: value is outside enum") + + if isinstance(instance, str): + if "pattern" in schema: + try: + pattern_matches = re.search(str(schema["pattern"]), instance) is not None + except re.error as exc: + raise S220Error("ARTIFACT_SCHEMA_PATTERN_INVALID", f"invalid schema pattern at {instance_path}") from exc + if not pattern_matches: + errors.append(f"{instance_path}: pattern mismatch") + if len(instance) < int(schema.get("minLength", 0)): + errors.append(f"{instance_path}: shorter than minLength") + if schema.get("format") == "date": + try: + parsed_date = date.fromisoformat(instance) + except ValueError: + parsed_date = None + if parsed_date is None or parsed_date.isoformat() != instance: + errors.append(f"{instance_path}: invalid RFC 3339 full-date") + elif "format" in schema: + raise S220Error( + "ARTIFACT_SCHEMA_KEYWORD_UNSUPPORTED", + f"unsupported string format at {instance_path}: {schema['format']}", + ) + + if isinstance(instance, (int, float)) and not isinstance(instance, bool): + if "minimum" in schema and instance < schema["minimum"]: + errors.append(f"{instance_path}: below minimum") + if "maximum" in schema and instance > schema["maximum"]: + errors.append(f"{instance_path}: above maximum") + + if isinstance(instance, dict): + required = schema.get("required", []) + if not isinstance(required, list): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"required must be an array at {instance_path}") + for key in required: + if key not in instance: + errors.append(f"{instance_path}: missing required property {key!r}") + properties = schema.get("properties", {}) + if not isinstance(properties, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"properties must be an object at {instance_path}") + additional = schema.get("additionalProperties", True) + for key, value in instance.items(): + child_path = f"{instance_path}.{key}" + if key in properties: + errors.extend( + _schema_errors(value, properties[key], root_schema, child_path, nested_stack) + ) + elif additional is False: + errors.append(f"{child_path}: additional property is forbidden") + elif isinstance(additional, dict): + errors.extend( + _schema_errors(value, additional, root_schema, child_path, nested_stack) + ) + if len(instance) < int(schema.get("minProperties", 0)): + errors.append(f"{instance_path}: fewer than minProperties") + + if isinstance(instance, list): + if len(instance) < int(schema.get("minItems", 0)): + errors.append(f"{instance_path}: fewer than minItems") + if "maxItems" in schema and len(instance) > int(schema["maxItems"]): + errors.append(f"{instance_path}: more than maxItems") + if schema.get("uniqueItems") is True: + identities = [_schema_identity(item) for item in instance] + if len(identities) != len(set(identities)): + errors.append(f"{instance_path}: array items are not unique") + prefix_items = schema.get("prefixItems", []) + if not isinstance(prefix_items, list): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"prefixItems must be an array at {instance_path}") + for index, child_schema in enumerate(prefix_items[: len(instance)]): + errors.extend( + _schema_errors( + instance[index], child_schema, root_schema, f"{instance_path}[{index}]", nested_stack + ) + ) + if "items" in schema: + for index in range(len(prefix_items), len(instance)): + errors.extend( + _schema_errors( + instance[index], schema["items"], root_schema, f"{instance_path}[{index}]", nested_stack + ) + ) + if "contains" in schema: + matched = sum( + not _schema_errors(item, schema["contains"], root_schema, f"{instance_path}[{index}]", nested_stack) + for index, item in enumerate(instance) + ) + if matched < 1: + errors.append(f"{instance_path}: contains matched no item") + return errors + + + def validate_artifact_payload( + payload: bytes, + schema_ref: str, + path: str, + schema_documents: Mapping[str, Any], + ) -> None: + value = load_json(payload, label=path) + schema_path, separator, fragment = schema_ref.partition("#") + root_schema = schema_documents.get(schema_path) + if not isinstance(root_schema, dict): + raise S220Error("ARTIFACT_SCHEMA_REF_UNRESOLVED", f"release-bound schema is unavailable: {schema_path}") + schema = _schema_pointer(root_schema, f"#{fragment}" if separator else "") + errors = _schema_errors(value, schema, root_schema, "$", set()) + if errors: + raise S220Error( + "ARTIFACT_SCHEMA_VALIDATION_FAILED", + f"artifact violates release-bound schema: {path}", + details=errors[:20], + ) + + + def publish(localdocs: McpClient, request: Mapping[str, Any], result: Mapping[str, Any]) -> dict[str, Any]: + root = request["plan_output_root_ref"] + payloads = artifact_payloads(result, request) + for relative, (payload, schema_ref, _) in sorted(payloads.items()): + validate_artifact_payload(payload, schema_ref, relative, result["schema_documents"]) + rows: list[dict[str, Any]] = [] + for relative, (payload, schema_ref, producer) in sorted(payloads.items()): + try: + localdocs.write_immutable(join_path(root, relative), payload) + except Exception as exc: + raise S220Error( + "PUBLISH_PARTIAL_WRITE", + f"artifact publication stopped before barrier: {relative}", + details={ + "written_paths": [row["path"] for row in rows], + "written_artifact_rows": [dict(row) for row in rows], + "failed_path": f"plan/{relative}", + "cause": str(exc), + }, + ) from exc + rows.append({"path": f"plan/{relative}", "schema_ref": schema_ref, "raw_sha256": digest(payload), "byte_size": len(payload), "producer_component": producer}) + groups = result["groups"] + barrier_without_hash = { + "schema_version": "stage2_plan_publish_status.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": result["plan"]["input_snapshot_digest"], + "parent_stage2_release_sha256": EXPECTED_STAGE2_RELEASE_SHA256, + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "s2_20_agent_sha256": request["expected_s2_20_agent_sha256"], + "s2_20_code_sha256": result["s2_20_code_sha256"], + "plan_status": "PLAN_COMPLETE_WITH_REVIEW" if any(row["drafting_permission"] != "CLEAN_DRAFT" for row in groups) else "PLAN_COMPLETE", + "route": "TO_S2_30", + "consumer_authorized": True, + "artifact_rows": rows, + "artifact_set_digest": digest(rows), + "group_ids": [row["claim_group_id"] for row in groups], + "clean_draft_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "CLEAN_DRAFT"], + "review_draft_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "REVIEW_DRAFT"], + "worknote_only_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "WORKNOTE_ONLY"], + "issue_summary": result["issue_summary"], + } + barrier = {**barrier_without_hash, "barrier_sha256": digest(barrier_without_hash)} + barrier_raw = canonical_bytes(barrier) + try: + localdocs.write_immutable(join_path(root, "plan_publish_status.json"), barrier_raw) + except Exception as exc: + raise S220Error( + "PUBLISH_PARTIAL_WRITE", + "all artifacts were written but the final consumer barrier failed", + details={ + "written_paths": [row["path"] for row in rows], + "written_artifact_rows": [dict(row) for row in rows], + "failed_path": "plan/plan_publish_status.json", + "cause": str(exc), + }, + ) from exc + return {"barrier_sha256": digest(barrier_raw), "artifact_set_digest": barrier["artifact_set_digest"], "route": barrier["route"], "plan_status": barrier["plan_status"]} + + + def publish_diagnostic(localdocs: McpClient, request: Mapping[str, Any], error: Mapping[str, Any]) -> dict[str, Any]: + root = request["plan_output_root_ref"] + details = error.get("details") if isinstance(error.get("details"), dict) else {} + partial_rows_raw = details.get("written_artifact_rows", []) if isinstance(details, dict) else [] + partial_rows: list[dict[str, Any]] = [] + if isinstance(partial_rows_raw, list): + for row in partial_rows_raw: + if not isinstance(row, dict) or set(row) != { + "path", "schema_ref", "raw_sha256", "byte_size", "producer_component" + }: + raise S220Error("DIAGNOSTIC_PARTIAL_ROW", "partial artifact row has an open shape") + safe_path(str(row["path"])) + require_hex(row["raw_sha256"], code="DIAGNOSTIC_PARTIAL_ROW_HASH") + if not isinstance(row["byte_size"], int) or isinstance(row["byte_size"], bool) or row["byte_size"] < 1: + raise S220Error("DIAGNOSTIC_PARTIAL_ROW_SIZE", "partial artifact byte size is invalid") + partial_rows.append(dict(row)) + source_refs = sorted(str(row["path"]) for row in partial_rows) + diagnostic_base = { + "schema_version": "stage2_s2_20_technical_diagnostic.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "attempt_id": request["attempt_id"], + "failure_component": "PUBLISH" if error.get("code") == "PUBLISH_PARTIAL_WRITE" else "PREFLIGHT", + "failure_code": str(error.get("code", "INLINE_RUNTIME_ERROR")), + "failure_scope": "RUN_WIDE", + "message": str(error.get("message", "S2_20 failed")), + "input_snapshot_digest": None, + "source_refs": source_refs, + "retryable": str(error.get("code", "")).startswith(("MCP_", "LOCALDOCS_")), + "operator_action_required": True, + } + diagnostic = {**diagnostic_base, "diagnostic_sha256": digest(diagnostic_base)} + diagnostic_raw = canonical_bytes(diagnostic) + localdocs.write_immutable(join_path(root, "technical_diagnostic.json"), diagnostic_raw) + rows = partial_rows + [{ + "path": "plan/technical_diagnostic.json", + "schema_ref": "schemas/relief_plan.schema.json#/$defs/technical_diagnostic", + "raw_sha256": digest(diagnostic_raw), + "byte_size": len(diagnostic_raw), + "producer_component": "S2_20_DIAGNOSTIC", + }] + rows = sorted(rows, key=lambda row: str(row["path"])) + barrier_base = { + "schema_version": "stage2_plan_publish_status.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": "0" * 64, + "parent_stage2_release_sha256": EXPECTED_STAGE2_RELEASE_SHA256, + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "s2_20_agent_sha256": request["expected_s2_20_agent_sha256"], + "s2_20_code_sha256": digest(b"UNAVAILABLE_BEFORE_PREFLIGHT"), + "plan_status": "TECHNICAL_INCOMPLETE", + "route": "STOP_TECHNICAL_INCOMPLETE", + "consumer_authorized": False, + "artifact_rows": rows, + "artifact_set_digest": digest(rows), + "group_ids": [], + "clean_draft_group_ids": [], + "review_draft_group_ids": [], + "worknote_only_group_ids": [], + "issue_summary": {"total": 1, "legal": 0, "binding": 0, "corpus": 0, "calculation": 0, "technical": 1}, + } + barrier = {**barrier_base, "barrier_sha256": digest(barrier_base)} + barrier_raw = canonical_bytes(barrier) + localdocs.write_immutable(join_path(root, "plan_publish_status.json"), barrier_raw) + return {"barrier_sha256": digest(barrier_raw), "artifact_set_digest": digest(rows), "route": "STOP_TECHNICAL_INCOMPLETE", "plan_status": "TECHNICAL_INCOMPLETE"} + + + def run() -> int: + localdocs: McpClient | None = None + request: dict[str, Any] | None = None + publication_guard = {"external_trust_verified": False} + receipt: dict[str, Any] + exit_code = 0 + try: + with contextlib.redirect_stdout(io.StringIO()): + if HEX64.fullmatch(INLINE_USER_HASH) is None or HEX64.fullmatch(INLINE_WORKSPACE_HASH) is None: + raise S220Error("INLINE_CONTEXT_UNBOUND", "AgentBackend user/workspace substitutions are required") + localdocs = McpClient(LOCALDOCS_URL, "localdocs") + localdocs.initialize() + request = validate_request(localdocs.read_binary(REQUEST_PATH)) + inputs, assets, snapshot_digest = hydrate(localdocs, request, publication_guard) + result = execute_core(inputs, assets, request, snapshot_digest) + publication = publish(localdocs, request, result) + receipt = { + "schema_version": "stage2_s2_20_execution_receipt.v1", + "ok": True, + "workflow_id": WORKFLOW_ID, + "request_id": request["request_id"], + "run_binding_digest": request["run_binding_digest"], + "plan_status": publication["plan_status"], + "route": publication["route"], + "artifact_set_digest": publication["artifact_set_digest"], + "plan_publish_status_path": "plan/plan_publish_status.json", + "plan_publish_status_sha256": publication["barrier_sha256"], + "error": None, + } + except Exception as exc: + error = exc.as_dict() if isinstance(exc, S220Error) else {"code": "INLINE_RUNTIME_ERROR", "message": str(exc)} + diagnostic_publication: dict[str, Any] | None = None + if ( + localdocs is not None + and request is not None + and publication_guard["external_trust_verified"] + ): + try: + diagnostic_publication = publish_diagnostic(localdocs, request, error) + except Exception as diagnostic_exc: + error = {**error, "diagnostic_publish_error": str(diagnostic_exc)} + receipt = { + "schema_version": "stage2_s2_20_execution_receipt.v1", + "ok": False, + "workflow_id": WORKFLOW_ID, + "request_id": request["request_id"] if request is not None else "UNBOUND", + "run_binding_digest": request["run_binding_digest"] if request is not None else "0" * 64, + "plan_status": diagnostic_publication["plan_status"] if diagnostic_publication is not None else "TECHNICAL_INCOMPLETE", + "route": diagnostic_publication["route"] if diagnostic_publication is not None else "STOP_TECHNICAL_INCOMPLETE", + "artifact_set_digest": diagnostic_publication["artifact_set_digest"] if diagnostic_publication is not None else None, + "plan_publish_status_path": "plan/plan_publish_status.json", + "plan_publish_status_sha256": diagnostic_publication["barrier_sha256"] if diagnostic_publication is not None else None, + "error": error, + } + exit_code = 2 + finally: + if localdocs is not None: + localdocs.close() + sys.stdout.buffer.write(canonical_bytes(receipt)) + return exit_code + + + if __name__ == "__main__": + raise SystemExit(run()) + task_procedure: + IN: + nexts: + - Task_S2_20_deterministic_relief_plan + wait_until: [] + Task_S2_20_deterministic_relief_plan: + nexts: + - OUT + wait_until: + - IN + OUT: + nexts: [] + wait_until: + - Task_S2_20_deterministic_relief_plan diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/build_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/build_receipt.json new file mode 100644 index 00000000..e1e858f2 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/build_receipt.json @@ -0,0 +1,65 @@ +{ + "schema_version": "stage2_corpus_build_receipt.v1", + "receipt_id": "S2-CORPUS-BUILD-PENDING", + "build_status": "PENDING_SOURCE_AND_OPERATOR_BUILD", + "build_executed": false, + "created_at": null, + "collection_contract_status": "PENDING_CORPUS_BUILD", + "collection_schema_ref": "corpus/weaviate_collection.schema.json", + "collection_schema_sha256": "f29b287e99d29dbd6d3008d1ad68e3960f9cc2c72459f04316f0311cd3e9a47d", + "collection_name": null, + "tenant": null, + "snapshot_id": null, + "snapshot_sha256": null, + "source_document_count": 0, + "chunk_count": 0, + "approved_case_type_ids": [], + "components": [ + { + "component_id": "CHUNKER", + "status": "PENDING", + "configuration_digest": null, + "evidence_refs": [] + }, + { + "component_id": "EMBEDDING", + "status": "PENDING", + "configuration_digest": null, + "evidence_refs": [] + }, + { + "component_id": "RERANKER", + "status": "PENDING", + "configuration_digest": null, + "evidence_refs": [] + }, + { + "component_id": "INDEX", + "status": "PENDING", + "configuration_digest": null, + "evidence_refs": [] + }, + { + "component_id": "CROSSWALK", + "status": "PENDING", + "configuration_digest": null, + "evidence_refs": [] + } + ], + "legal_review_status": "PENDING_KOREAN_LAWYER_REVIEW", + "live_mcp_admission_status": "PENDING_LIVE_MCP_ADMISSION", + "production_eligible": false, + "unresolved_reason_codes": [ + "CORPUS_BUILD_NOT_EXECUTED", + "LEGAL_REVIEW_NOT_COMPLETED", + "LIVE_MCP_ADMISSION_NOT_COMPLETED", + "RAW_CORPUS_NOT_PROVIDED" + ], + "guards": { + "raw_corpus_runtime_ingest_forbidden": true, + "directory_scan_forbidden": true, + "filter_relaxation_forbidden": true, + "embedded_instruction_execution_forbidden": true, + "unapproved_chunk_use_forbidden": true + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/chunk_manifest.jsonl b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/chunk_manifest.jsonl new file mode 100644 index 00000000..803924f8 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/chunk_manifest.jsonl @@ -0,0 +1 @@ +{"schema_version":"stage2_corpus_chunk_manifest_header.v1","record_type":"MANIFEST_STATUS","manifest_id":"S2-CORPUS-CHUNK-MANIFEST-PENDING","status":"PENDING_SOURCE_AND_OPERATOR_BUILD","execution_eligible":false,"source_document_count":0,"chunk_count":0,"snapshot_id":null,"snapshot_sha256":null,"unresolved_reason_codes":["RAW_CORPUS_NOT_PROVIDED","HEADING_AWARE_CHUNK_BUILD_NOT_EXECUTED","CHUNK_HASHES_NOT_BOUND","CROSSWALK_NOT_BUILT","KOREAN_LAWYER_REVIEW_NOT_COMPLETED"],"guards":{"placeholder_chunk_row_forbidden":true,"unapproved_chunk_runtime_use_forbidden":true,"embedded_instruction_execution_forbidden":true}} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/source_manifest.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/source_manifest.json new file mode 100644 index 00000000..9721faf1 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/source_manifest.json @@ -0,0 +1,24 @@ +{ + "schema_version": "stage2_corpus_source_manifest.v1", + "manifest_id": "S2-CORPUS-SOURCE-MANIFEST-PENDING", + "status": "PENDING_SOURCE_AND_LEGAL_REVIEW", + "execution_eligible": false, + "path_base": "corpus/raw", + "source_document_count": 0, + "approved_source_document_count": 0, + "source_rows": [], + "unresolved_reason_codes": [ + "RAW_CORPUS_NOT_PROVIDED", + "SOURCE_HASHES_NOT_BOUND", + "CASE_TYPE_SUB_RULE_CROSSWALK_NOT_BUILT", + "AUTHORITY_REVIEW_NOT_COMPLETED", + "KOREAN_LAWYER_REVIEW_NOT_COMPLETED" + ], + "guards": { + "placeholder_source_row_forbidden": true, + "unapproved_source_runtime_use_forbidden": true, + "nearest_case_type_fallback_forbidden": true, + "raw_directory_scan_at_runtime_forbidden": true, + "embedded_instruction_execution_forbidden": true + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/weaviate_collection.schema.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/weaviate_collection.schema.json new file mode 100644 index 00000000..795d48db --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/corpus/weaviate_collection.schema.json @@ -0,0 +1,533 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.liti-agent.local/stage2/corpus/weaviate_collection.schema.v1.json", + "title": "Stage 2 requirement-fact Weaviate collection and retrieval contracts", + "description": "Closed metadata, filter, request, response, and build-receipt contracts. It does not assert that a live collection, tenant, credential, snapshot, embedding model, or legal approval exists.", + "schema_version": "stage2_weaviate_collection_contract_bundle.v1", + "oneOf": [ + {"$ref": "#/$defs/collection_contract"}, + {"$ref": "#/$defs/corpus_object_metadata"}, + {"$ref": "#/$defs/hybrid_search_request"}, + {"$ref": "#/$defs/hybrid_search_response"}, + {"$ref": "#/$defs/corpus_build_receipt"} + ], + "$defs": { + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "pending_or_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"const": "PENDING_SEQUENTIAL_BIND"} + ] + }, + "nonempty_string": { + "type": "string", + "minLength": 1 + }, + "nullable_nonempty_string": { + "oneOf": [ + {"$ref": "#/$defs/nonempty_string"}, + {"type": "null"} + ] + }, + "path_ref": { + "type": "string", + "minLength": 1, + "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*[\\x00-\\x1f]).+$" + }, + "string_set": { + "type": "array", + "items": {"$ref": "#/$defs/nonempty_string"}, + "uniqueItems": true + }, + "nonempty_string_set": { + "type": "array", + "items": {"$ref": "#/$defs/nonempty_string"}, + "minItems": 1, + "uniqueItems": true + }, + "date_or_null": { + "oneOf": [ + {"type": "string", "format": "date"}, + {"type": "null"} + ] + }, + "collection_contract": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "contract_id", + "transport", + "endpoint", + "protocol_version", + "tool_name", + "operation_mode", + "collection_name", + "tenant", + "credential_ref", + "object_metadata_schema_ref", + "filter_schema_ref", + "index_configuration_digest", + "snapshot_id", + "snapshot_sha256", + "status" + ], + "properties": { + "schema_version": {"const": "stage2_weaviate_collection_contract.v1"}, + "contract_id": {"$ref": "#/$defs/nonempty_string"}, + "transport": {"const": "MCP_OVER_HTTP_JSON_RPC"}, + "endpoint": {"const": "https://weaviate.eroomai.com/mcp"}, + "protocol_version": {"const": "2025-03-26"}, + "tool_name": {"const": "search_hybrid"}, + "operation_mode": {"const": "READ_ONLY_FILTERED_HYBRID_SEARCH"}, + "collection_name": {"$ref": "#/$defs/nullable_nonempty_string"}, + "tenant": {"$ref": "#/$defs/nullable_nonempty_string"}, + "credential_ref": {"$ref": "#/$defs/nullable_nonempty_string"}, + "object_metadata_schema_ref": { + "const": "corpus/weaviate_collection.schema.json#/$defs/corpus_object_metadata" + }, + "filter_schema_ref": { + "const": "corpus/weaviate_collection.schema.json#/$defs/search_filter" + }, + "index_configuration_digest": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + }, + "snapshot_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "snapshot_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + }, + "status": { + "enum": [ + "PENDING_CORPUS_BUILD", + "BUILT_NOT_LIVE_ADMITTED", + "LIVE_CANARY_ADMITTED", + "PRODUCTION_ADMITTED" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": {"const": "PENDING_CORPUS_BUILD"} + }, + "required": ["status"] + }, + "then": { + "properties": { + "collection_name": {"type": "null"}, + "tenant": {"type": "null"}, + "credential_ref": {"type": "null"}, + "index_configuration_digest": {"type": "null"}, + "snapshot_id": {"type": "null"}, + "snapshot_sha256": {"type": "null"} + } + } + } + ] + }, + "corpus_object_metadata": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "document_id", + "document_sha256", + "chunk_id", + "chunk_sha256", + "source_path", + "source_locator", + "case_type_ids", + "sub_rule_ids", + "rule_branch_keys", + "corpus_scope_ids", + "element_set_ids", + "corpus_element_ids", + "doctrine_roles", + "jurisdiction", + "effective_from", + "effective_to", + "authority_ids", + "proposition_ids", + "approval_status", + "legal_reviewer_receipt_ref", + "legal_reviewer_receipt_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_requirement_fact_chunk_metadata.v1"}, + "document_id": {"$ref": "#/$defs/nonempty_string"}, + "document_sha256": {"$ref": "#/$defs/sha256"}, + "chunk_id": {"$ref": "#/$defs/nonempty_string"}, + "chunk_sha256": {"$ref": "#/$defs/sha256"}, + "source_path": {"$ref": "#/$defs/path_ref"}, + "source_locator": {"$ref": "#/$defs/nonempty_string"}, + "case_type_ids": {"$ref": "#/$defs/nonempty_string_set"}, + "sub_rule_ids": {"$ref": "#/$defs/string_set"}, + "rule_branch_keys": {"$ref": "#/$defs/string_set"}, + "corpus_scope_ids": {"$ref": "#/$defs/nonempty_string_set"}, + "element_set_ids": {"$ref": "#/$defs/nonempty_string_set"}, + "corpus_element_ids": {"$ref": "#/$defs/nonempty_string_set"}, + "doctrine_roles": { + "type": "array", + "items": { + "enum": ["element", "defense", "rebuttal", "burden", "relief_consistency"] + }, + "minItems": 1, + "uniqueItems": true + }, + "jurisdiction": {"const": "KR"}, + "effective_from": {"$ref": "#/$defs/date_or_null"}, + "effective_to": {"$ref": "#/$defs/date_or_null"}, + "authority_ids": {"$ref": "#/$defs/string_set"}, + "proposition_ids": {"$ref": "#/$defs/string_set"}, + "approval_status": { + "enum": ["PENDING_LEGAL_REVIEW", "APPROVED", "REJECTED", "WITHDRAWN"] + }, + "legal_reviewer_receipt_ref": { + "oneOf": [ + {"$ref": "#/$defs/path_ref"}, + {"type": "null"} + ] + }, + "legal_reviewer_receipt_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + } + }, + "allOf": [ + { + "if": { + "properties": {"approval_status": {"const": "APPROVED"}}, + "required": ["approval_status"] + }, + "then": { + "properties": { + "legal_reviewer_receipt_ref": {"$ref": "#/$defs/path_ref"}, + "legal_reviewer_receipt_sha256": {"$ref": "#/$defs/sha256"} + } + } + } + ] + }, + "search_filter": { + "type": "object", + "additionalProperties": false, + "required": [ + "case_type_id", + "sub_rule_id", + "rule_branch_key", + "corpus_scope_id", + "element_set_id", + "doctrine_role", + "jurisdiction", + "snapshot_id", + "approval_status", + "effective_on" + ], + "properties": { + "case_type_id": {"$ref": "#/$defs/nonempty_string"}, + "sub_rule_id": {"$ref": "#/$defs/nonempty_string"}, + "rule_branch_key": {"$ref": "#/$defs/nonempty_string"}, + "corpus_scope_id": {"$ref": "#/$defs/nonempty_string"}, + "element_set_id": {"$ref": "#/$defs/nonempty_string"}, + "doctrine_role": { + "enum": ["element", "defense", "rebuttal", "burden", "relief_consistency"] + }, + "jurisdiction": {"const": "KR"}, + "snapshot_id": {"$ref": "#/$defs/nonempty_string"}, + "approval_status": {"const": "APPROVED"}, + "effective_on": { + "type": "string", + "format": "date" + } + } + }, + "hybrid_search_request": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "request_id", + "query_plan_id", + "atomic_claim_id", + "collection_name", + "tenant", + "query_text", + "query_sha256", + "filter", + "alpha", + "top_k", + "embedding_configuration_digest", + "reranker_configuration_digest", + "index_configuration_digest", + "snapshot_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_weaviate_hybrid_search_request.v1"}, + "request_id": {"$ref": "#/$defs/nonempty_string"}, + "query_plan_id": {"$ref": "#/$defs/nonempty_string"}, + "atomic_claim_id": {"$ref": "#/$defs/nonempty_string"}, + "collection_name": {"$ref": "#/$defs/nonempty_string"}, + "tenant": {"$ref": "#/$defs/nonempty_string"}, + "query_text": {"$ref": "#/$defs/nonempty_string"}, + "query_sha256": {"$ref": "#/$defs/sha256"}, + "filter": {"$ref": "#/$defs/search_filter"}, + "alpha": {"type": "number", "minimum": 0, "maximum": 1}, + "top_k": {"type": "integer", "minimum": 1, "maximum": 100}, + "embedding_configuration_digest": {"$ref": "#/$defs/sha256"}, + "reranker_configuration_digest": {"$ref": "#/$defs/sha256"}, + "index_configuration_digest": {"$ref": "#/$defs/sha256"}, + "snapshot_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "hybrid_hit": { + "type": "object", + "additionalProperties": false, + "required": [ + "chunk_id", + "chunk_sha256", + "source_document_id", + "source_document_sha256", + "source_locator", + "pre_rerank_rank", + "hybrid_score", + "post_rerank_rank", + "reranker_score", + "metadata" + ], + "properties": { + "chunk_id": {"$ref": "#/$defs/nonempty_string"}, + "chunk_sha256": {"$ref": "#/$defs/sha256"}, + "source_document_id": {"$ref": "#/$defs/nonempty_string"}, + "source_document_sha256": {"$ref": "#/$defs/sha256"}, + "source_locator": {"$ref": "#/$defs/nonempty_string"}, + "pre_rerank_rank": {"type": "integer", "minimum": 1}, + "hybrid_score": {"type": "number"}, + "post_rerank_rank": {"type": "integer", "minimum": 1}, + "reranker_score": {"type": ["number", "null"]}, + "metadata": {"$ref": "#/$defs/corpus_object_metadata"} + } + }, + "dropped_hit": { + "type": "object", + "additionalProperties": false, + "required": ["chunk_id", "chunk_sha256", "drop_reason_code"], + "properties": { + "chunk_id": {"$ref": "#/$defs/nonempty_string"}, + "chunk_sha256": {"$ref": "#/$defs/sha256"}, + "drop_reason_code": { + "enum": [ + "FILTER_MISMATCH", + "UNAPPROVED_CHUNK", + "HASH_MISMATCH", + "SNAPSHOT_MISMATCH", + "CROSSWALK_GAP", + "DUPLICATE_CHUNK" + ] + } + } + }, + "hybrid_search_response": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "request_id", + "request_sha256", + "response_status", + "raw_hit_count", + "selected_hits", + "dropped_hits", + "deterministic_order_digest", + "retry_count" + ], + "properties": { + "schema_version": {"const": "stage2_weaviate_hybrid_search_response.v1"}, + "request_id": {"$ref": "#/$defs/nonempty_string"}, + "request_sha256": {"$ref": "#/$defs/sha256"}, + "response_status": { + "enum": ["COMPLETE", "NO_HIT", "TECHNICAL_INCOMPLETE"] + }, + "raw_hit_count": {"type": "integer", "minimum": 0}, + "selected_hits": { + "type": "array", + "items": {"$ref": "#/$defs/hybrid_hit"} + }, + "dropped_hits": { + "type": "array", + "items": {"$ref": "#/$defs/dropped_hit"} + }, + "deterministic_order_digest": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + }, + "retry_count": {"type": "integer", "minimum": 0, "maximum": 2} + } + }, + "build_component_status": { + "type": "object", + "additionalProperties": false, + "required": ["component_id", "status", "configuration_digest", "evidence_refs"], + "properties": { + "component_id": { + "enum": ["CHUNKER", "EMBEDDING", "RERANKER", "INDEX", "CROSSWALK"] + }, + "status": { + "enum": ["PENDING", "BUILT_NOT_ADMITTED", "ADMITTED"] + }, + "configuration_digest": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + }, + "evidence_refs": {"$ref": "#/$defs/string_set"} + } + }, + "corpus_build_receipt": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "receipt_id", + "build_status", + "build_executed", + "created_at", + "collection_contract_status", + "collection_schema_ref", + "collection_schema_sha256", + "collection_name", + "tenant", + "snapshot_id", + "snapshot_sha256", + "source_document_count", + "chunk_count", + "approved_case_type_ids", + "components", + "legal_review_status", + "live_mcp_admission_status", + "production_eligible", + "unresolved_reason_codes", + "guards" + ], + "properties": { + "schema_version": {"const": "stage2_corpus_build_receipt.v1"}, + "receipt_id": {"$ref": "#/$defs/nonempty_string"}, + "build_status": { + "enum": [ + "PENDING_SOURCE_AND_OPERATOR_BUILD", + "BUILT_NOT_LEGALLY_REVIEWED", + "BUILT_NOT_LIVE_ADMITTED", + "ADMITTED" + ] + }, + "build_executed": {"type": "boolean"}, + "created_at": { + "oneOf": [ + {"type": "string", "format": "date-time"}, + {"type": "null"} + ] + }, + "collection_contract_status": { + "enum": ["PENDING_CORPUS_BUILD", "BUILT_NOT_LIVE_ADMITTED", "LIVE_CANARY_ADMITTED", "PRODUCTION_ADMITTED"] + }, + "collection_schema_ref": { + "const": "corpus/weaviate_collection.schema.json" + }, + "collection_schema_sha256": {"$ref": "#/$defs/pending_or_sha256"}, + "collection_name": {"$ref": "#/$defs/nullable_nonempty_string"}, + "tenant": {"$ref": "#/$defs/nullable_nonempty_string"}, + "snapshot_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "snapshot_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + }, + "source_document_count": {"type": "integer", "minimum": 0}, + "chunk_count": {"type": "integer", "minimum": 0}, + "approved_case_type_ids": {"$ref": "#/$defs/string_set"}, + "components": { + "type": "array", + "items": {"$ref": "#/$defs/build_component_status"}, + "minItems": 5, + "maxItems": 5 + }, + "legal_review_status": { + "enum": ["PENDING_KOREAN_LAWYER_REVIEW", "APPROVED", "REJECTED"] + }, + "live_mcp_admission_status": { + "enum": ["PENDING_LIVE_MCP_ADMISSION", "LIVE_CANARY_ADMITTED", "PRODUCTION_ADMITTED"] + }, + "production_eligible": {"type": "boolean"}, + "unresolved_reason_codes": {"$ref": "#/$defs/string_set"}, + "guards": { + "type": "object", + "additionalProperties": false, + "required": [ + "raw_corpus_runtime_ingest_forbidden", + "directory_scan_forbidden", + "filter_relaxation_forbidden", + "embedded_instruction_execution_forbidden", + "unapproved_chunk_use_forbidden" + ], + "properties": { + "raw_corpus_runtime_ingest_forbidden": {"const": true}, + "directory_scan_forbidden": {"const": true}, + "filter_relaxation_forbidden": {"const": true}, + "embedded_instruction_execution_forbidden": {"const": true}, + "unapproved_chunk_use_forbidden": {"const": true} + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "build_status": {"const": "PENDING_SOURCE_AND_OPERATOR_BUILD"} + }, + "required": ["build_status"] + }, + "then": { + "properties": { + "build_executed": {"const": false}, + "created_at": {"type": "null"}, + "collection_contract_status": {"const": "PENDING_CORPUS_BUILD"}, + "collection_name": {"type": "null"}, + "tenant": {"type": "null"}, + "snapshot_id": {"type": "null"}, + "snapshot_sha256": {"type": "null"}, + "source_document_count": {"const": 0}, + "chunk_count": {"const": 0}, + "approved_case_type_ids": {"maxItems": 0}, + "legal_review_status": {"const": "PENDING_KOREAN_LAWYER_REVIEW"}, + "live_mcp_admission_status": {"const": "PENDING_LIVE_MCP_ADMISSION"}, + "production_eligible": {"const": false} + } + } + } + ] + } + }, + "x-runtime-boundary": { + "mcp_endpoint": "https://weaviate.eroomai.com/mcp", + "mcp_protocol_version": "2025-03-26", + "tool_allowlist": ["search_hybrid"], + "write_operations_allowed": false, + "filter_relaxation_allowed": false, + "live_admission_status": "PENDING_EXTERNAL_EVIDENCE" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_code_executor_binding.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_code_executor_binding.yml index cca4af64..3769b434 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_code_executor_binding.yml +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_code_executor_binding.yml @@ -1,87 +1,255 @@ -schema_version: stage2_code_executor_binding.v2 -binding_id: S2-BINDING-S2_00-CODE-EXECUTOR-V2 -workflow_id: S2_00 -execution_class: NON-LLM-DETERMINISTIC -active_runtime_authority: true +schema_version: stage2_code_executor_binding.v3 +binding_id: S2-BINDING-DETERMINISTIC-STAGES-V3 +stage_bindings: + - stage_id: S2_00 + binding_id: S2-BINDING-S2_00-CODE-EXECUTOR-V2 + workflow_id: S2_00 + execution_class: NON-LLM-DETERMINISTIC + active_runtime_authority: true -agent_script_ref: - asset_id: AGENT-S2_00-INLINE - path: agent_scripts/Stage_2_S2_00.yml - sha256: 04877f5459c8a579d793ed34ac936cc366a7e5b1c77a1230c93284036196fabe - schema_id: liti_agent_yaml.v1 - binding_status: BOUND + agent_script_ref: + asset_id: AGENT-S2_00-INLINE + path: agent_scripts/Stage_2_S2_00.yml + sha256: 45ca76de0c352745f8111ed036a001d053bdc9b43c1a72292d49d9b5f3bcd2c7 + schema_id: liti_agent_yaml.v1 + binding_status: BOUND -workflow_contract_ref: - asset_id: WF-S2_00 - path: workflows/S2_00_stage1_ingress_normalize_and_bundle_compile.yml - sha256: 3d7ceb86b236668c8c372136f1d1b43a5d0a79d3fb05fd1ef4342bb638190f68 - schema_id: stage2_workflow_contract.v1.2 - binding_status: BOUND + workflow_contract_ref: + asset_id: WF-S2_00 + path: workflows/S2_00_stage1_ingress_normalize_and_bundle_compile.yml + sha256: 3d7ceb86b236668c8c372136f1d1b43a5d0a79d3fb05fd1ef4342bb638190f68 + schema_id: stage2_workflow_contract.v1.2 + binding_status: BOUND -inline_code_receipt_ref: - asset_id: RECEIPT-S2_00-INLINE-CODE - path: manifest/s2_00_inline_code_receipt.json - sha256: a0839fae2d30091ee3a6d41b96f324a530d0dc569cd23e0b7f6ad6a1445dd130 - schema_id: stage2_s2_00_inline_code_receipt.v2 - binding_status: BOUND + inline_code_receipt_ref: + asset_id: RECEIPT-S2_00-INLINE-CODE + path: manifest/s2_00_inline_code_receipt.json + sha256: 1a0044a0f1e0fec13a5d4870df6147b8c4c671d843d4b634b4249442fdcfdfb8 + schema_id: stage2_s2_00_inline_code_receipt.v2 + binding_status: BOUND -stage2_release_ref: - asset_id: RELEASE-STAGE2-CLEAN - path: manifest/stage2_release.json - sha256: 0f21af3ddca538d9b6a5853dac23222c0b632d0e573de371a8de2709e691699d - schema_id: stage2_release.v2 - binding_status: BOUND + stage2_release_ref: + asset_id: RELEASE-STAGE2-CLEAN + path: manifest/stage2_release.json + sha256: 70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92 + schema_id: stage2_release.v2 + binding_status: BOUND -expected_release_sha256: 0f21af3ddca538d9b6a5853dac23222c0b632d0e573de371a8de2709e691699d -agent_script_sha256: 04877f5459c8a579d793ed34ac936cc366a7e5b1c77a1230c93284036196fabe -canonical_code_sha256: 92f13cf3ac113c68e646fcf3b8d6bf9b8a7f41962eef809af91ebf133d50bc99 + expected_release_sha256: 70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92 + agent_script_sha256: 45ca76de0c352745f8111ed036a001d053bdc9b43c1a72292d49d9b5f3bcd2c7 + canonical_code_sha256: b2c56fde707664903158f7d73073fc4a4913fb21234df455bdf5fe2bd1e5fafa -mcp_server_id: code-executor -tool_name: run_code -language: python -network: agent-network -timeout_seconds: 300 -runtime_image_digest: PENDING_SEQUENTIAL_BIND -runtime_image_status: PENDING_BACKEND_EVIDENCE + mcp_server_id: code-executor + tool_name: run_code + language: python + network: agent-network + timeout_seconds: 300 + runtime_image_digest: PENDING_SEQUENTIAL_BIND + runtime_image_status: PENDING_BACKEND_EVIDENCE -dependency_lock: - requirements: "httpx==0.28.1" - requirements_sha256: 5fadf5f6ea5bd1b141ea05745cb52449bdccde939c22e76231e7993c2afc91d0 - lock_status: LIVE_BACKEND_PENDING + dependency_lock: + requirements: "httpx==0.28.1" + requirements_sha256: 5fadf5f6ea5bd1b141ea05745cb52449bdccde939c22e76231e7993c2afc91d0 + lock_status: LIVE_BACKEND_PENDING -localdocs_contract: - endpoint: http://mcp-localdocs:8012/mcp - user_id_template: "{{__user_hash__}}" - workspace_id_template: "{{__workspace_hash__}}" - tool_allowlist: - - read_binary_doc - - write_binary_file - fixed_request_path: stage2_control/s2_00_request.json - read_path_allowlist: - - stage2_control/s2_00_request.json - - Default_Agent/Stage_2_Clean/manifest/stage2_release.json - - Default_Agent/Stage_2_Clean/manifest/module_manifest.json - - Default_Agent/Stage_2_Clean/schemas/ingress.schema.json - - Default_Agent/Stage_2_Clean/schemas/context.schema.json - - Default_Agent/Stage_2_Clean/schemas/review_status.schema.json - - Default_Agent/Stage_2_Clean/ - - / - - / - - stage2_runs/by-binding// - write_root_rule: stage2_runs/by-binding// + localdocs_contract: + endpoint: http://mcp-localdocs:8012/mcp + user_id_template: "{{__user_hash__}}" + workspace_id_template: "{{__workspace_hash__}}" + tool_allowlist: + - read_binary_doc + - write_binary_file + fixed_request_path: stage2_control/s2_00_request.json + read_path_allowlist: + - stage2_control/s2_00_request.json + - Default_Agent/Stage_2_Clean/manifest/stage2_release.json + - Default_Agent/Stage_2_Clean/manifest/module_manifest.json + - Default_Agent/Stage_2_Clean/schemas/ingress.schema.json + - Default_Agent/Stage_2_Clean/schemas/context.schema.json + - Default_Agent/Stage_2_Clean/schemas/review_status.schema.json + - Default_Agent/Stage_2_Clean/ + - / + - / + - stage2_runs/by-binding// + write_root_rule: stage2_runs/by-binding// -egress_profile_id: S2_00_LOCALDOCS_ONLY_V1 -egress_profile_status: PENDING_LIVE_VERIFICATION + external_mcp_contract: null + egress_profile_id: S2_00_LOCALDOCS_ONLY_V1 + egress_profile_status: PENDING_LIVE_VERIFICATION -downstream_handoff_owner: - workflow_id: S2_10 - ownership_scope: DOWNSTREAM_TASK_CONSTRUCTION_BINDING_AND_INVOCATION - agent_path: agent_scripts/Stage_2_S2_10.yml - s2_10_agent_sha256: 0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513 - llm_binding_path: deployment/stage2_s2_10_llm_binding.yml - s2_10_llm_binding_sha256: b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1 - owner_binding_status: HASH_BOUND_LIVE_ADMISSION_PENDING - s2_00_override_allowed: false + downstream_handoff_owner: + workflow_id: S2_10 + ownership_scope: DOWNSTREAM_TASK_CONSTRUCTION_BINDING_AND_INVOCATION + agent_path: agent_scripts/Stage_2_S2_10.yml + s2_10_agent_sha256: 0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513 + llm_binding_path: deployment/stage2_s2_10_llm_binding.yml + s2_10_llm_binding_sha256: 90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95 + owner_binding_status: HASH_BOUND_LIVE_ADMISSION_PENDING + s2_00_override_allowed: false + + live_admission_status: PENDING_SECRET_BINDING + legacy_fallbacks: [] + + - stage_id: S2_20 + binding_id: S2-BINDING-S2_20-CODE-EXECUTOR-V1 + workflow_id: S2_20 + execution_class: NON-LLM-DETERMINISTIC + active_runtime_authority: false + + agent_script_ref: + asset_id: AGENT-S2_20-INLINE + path: agent_scripts/Stage_2_S2_20.yml + sha256: 4d7642bb8b63dc37fc0ca7808676c816ba43623384b0f745f9f4af17f0f8553b + schema_id: liti_agent_yaml.v1 + binding_status: BOUND + + workflow_contract_ref: + asset_id: WF-S2_20 + path: workflows/S2_20_canonical_relief_plan_reduce.yml + sha256: da56152af502fe432cb2ebaa593a4e389b7f2cd4d0b8f3be29e49717cff0544a + schema_id: stage2_workflow_contract.v1 + binding_status: BOUND + + inline_code_receipt_ref: + asset_id: RECEIPT-S2_20-INLINE-CODE + path: manifest/s2_20_inline_code_receipt.json + sha256: 3693ce7d99d64458f2dbf80f9535aad55ad1dec22ab95898f3c05b204afb801b + schema_id: stage2_s2_20_inline_code_receipt.v1 + binding_status: BOUND + + stage2_release_ref: + asset_id: RELEASE-STAGE2-CLEAN + path: manifest/stage2_release.json + sha256: 70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92 + schema_id: stage2_release.v2 + binding_status: BOUND + + expected_release_sha256: 70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92 + agent_script_sha256: 4d7642bb8b63dc37fc0ca7808676c816ba43623384b0f745f9f4af17f0f8553b + canonical_code_sha256: 140a83b71aa1007dd275c91a0aa8c837bd2d72a41136ae5bfa0c007da8dd701e + + mcp_server_id: code-executor + tool_name: run_code + language: python + network: agent-network + timeout_seconds: 300 + runtime_image_digest: PENDING_SEQUENTIAL_BIND + runtime_image_status: PENDING_BACKEND_EVIDENCE + + dependency_lock: + requirements: "httpx==0.28.1" + requirements_sha256: 5fadf5f6ea5bd1b141ea05745cb52449bdccde939c22e76231e7993c2afc91d0 + lock_status: LIVE_BACKEND_PENDING + + localdocs_contract: + endpoint: http://mcp-localdocs:8012/mcp + user_id_template: "{{__user_hash__}}" + workspace_id_template: "{{__workspace_hash__}}" + tool_allowlist: + - read_binary_doc + - write_binary_file + fixed_request_path: stage2_control/s2_20_request.json + read_path_allowlist: + - stage2_control/s2_20_request.json + - stage2_control/host_cas//S2_20.json + - Default_Agent/Stage_2_Clean/manifest/stage2_release.json + - Default_Agent/Stage_2_Clean/manifest/module_manifest.json + - Default_Agent/Stage_2_Clean/manifest/s2_10_release.json + - Default_Agent/Stage_2_Clean/manifest/authority_release.json + - Default_Agent/Stage_2_Clean/manifest/case_type_coverage.json + - Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json + - Default_Agent/Stage_2_Clean/manifest/case_type_registry.yml + - Default_Agent/Stage_2_Clean/manifest/case_type_rule_registry.yml + - Default_Agent/Stage_2_Clean/rules/relief/.md + - Default_Agent/Stage_2_Clean/legal_review/.json + - Default_Agent/Stage_2_Clean/manifest/corpus_release.json + - Default_Agent/Stage_2_Clean/manifest/s2_20_inline_code_receipt.json + - Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_20.yml + - Default_Agent/Stage_2_Clean/deployment/stage2_code_executor_binding.yml + - Default_Agent/Stage_2_Clean/deployment/stage2_s2_10_llm_binding.yml + - Default_Agent/Stage_2_Clean/registry/binding/binding_signature_projection.yml + - Default_Agent/Stage_2_Clean/registry/planning/option_disposition_policy.yml + - Default_Agent/Stage_2_Clean/registry/party/party_set_rules.yml + - Default_Agent/Stage_2_Clean/registry/drafting/case_type_relief_rules.yml + - Default_Agent/Stage_2_Clean/registry/drafting/forbidden_relief_rules.yml + - Default_Agent/Stage_2_Clean/registry/drafting/counter_performance_rules.yml + - Default_Agent/Stage_2_Clean/registry/drafting/procedural_declaration_rules.yml + - Default_Agent/Stage_2_Clean/registry/review/review_policy_registry.yml + - Default_Agent/Stage_2_Clean/registry/authority/authority_registry.yml + - Default_Agent/Stage_2_Clean/registry/corpus/requirement_fact_scope.yml + - Default_Agent/Stage_2_Clean/registry/temporal/inheritance_reserved_share.yml + - Default_Agent/Stage_2_Clean/registry/calculations/calculation_activation_registry.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-01_interest_delay.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-02_allocation_setoff_balance.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-03_limitation_deadline.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-04_valuation.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-05_personal_injury.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-06_construction_defect.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-07_lease_use_gain.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-08_inheritance_reserved_share.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-09_wage_severance.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-10_actio_insolvency_value.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-11_distribution_share_division.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-12_insurance.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-13_court_value_cost.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-R1_ip_damage.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-R2_org_liquidation.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-R3_transport_maritime.yml + - Default_Agent/Stage_2_Clean/registry/calculations/CE-R4_financial_instrument.yml + - Default_Agent/Stage_2_Clean/law_values/general_law_values.yml + - Default_Agent/Stage_2_Clean/law_values/court_fee_values.yml + - Default_Agent/Stage_2_Clean/schemas/relief_plan.schema.json + - Default_Agent/Stage_2_Clean/schemas/binding_retrieval.schema.json + - Default_Agent/Stage_2_Clean/schemas/calculation.schema.json + - Default_Agent/Stage_2_Clean/corpus/weaviate_collection.schema.json + - Default_Agent/Stage_2_Clean/schemas/ingress.schema.json + - Default_Agent/Stage_2_Clean/schemas/context.schema.json + - Default_Agent/Stage_2_Clean/schemas/s2_10.schema.json + - Default_Agent/Stage_2_Clean/schemas/review_status.schema.json + - Default_Agent/Stage_2_Clean/schemas/deployment.schema.json + - Default_Agent/Stage_2_Clean/routing/actio_pauliana_route_registry.yml + - Default_Agent/Stage_2_Clean/routing/actio_pauliana_mortgage_route.yml + - Default_Agent/Stage_2_Clean/validators/actio_value_compensation_invariants.yml + - Default_Agent/Stage_2_Clean/renderers/R01_money_payment.yml + - Default_Agent/Stage_2_Clean/renderers/R02_delivery_possession.yml + - Default_Agent/Stage_2_Clean/renderers/R03_declaration_registry.yml + - Default_Agent/Stage_2_Clean/renderers/R04_special_nonmoney_performance.yml + - Default_Agent/Stage_2_Clean/renderers/R05_declaratory.yml + - Default_Agent/Stage_2_Clean/renderers/R06_constitutive_judgment_challenge.yml + - stage2_runs/by-binding//ingress/ingress_status.json + - stage2_runs/by-binding//context/case_context.json + - stage2_runs/by-binding//context/evidence_inventory.json + - stage2_runs/by-binding//context/object_registry.json + - stage2_runs/by-binding//context/cluster_plan.json + - stage2_runs/by-binding//context/party_and_title_context.json + - stage2_runs/by-binding//context/slot_crosswalk.json + - stage2_runs/by-binding//context/bundle_plan.json + - stage2_runs/by-binding//review/issue_ledger.base.json + - stage2_runs/by-binding//context/cluster_slices/.json + - stage2_runs/by-binding//map_s2_10/s2_10_publish_status.json + - stage2_runs/by-binding//map_s2_10/domain_verdicts/.json + - stage2_runs/by-binding//map_s2_10/item_receipts/.json + - stage2_runs/by-binding//map_s2_10/issue_patches/.json + - stage2_runs/by-binding//map_s2_10/assumption_parts/.json + - stage2_runs/by-binding//map_s2_10/usage_parts/.json + - stage2_runs/by-binding//map_s2_10/wave_receipts/wave-.json + write_root_rule: stage2_runs/by-binding// + + external_mcp_contract: + endpoint: https://weaviate.eroomai.com/mcp + protocol_version: "2025-03-26" + tool_allowlist: + - search_hybrid + collection: PENDING_CORPUS_RELEASE_BIND + tenant: PENDING_CORPUS_RELEASE_BIND + credential_ref: agentbackend://stage2/s2_20/weaviate-readonly + operation_mode: READ_ONLY_EXACT_FILTER + admission_status: PENDING_LIVE_VERIFICATION + + egress_profile_id: S2_20_LOCALDOCS_WEAVIATE_READONLY_V1 + egress_profile_status: PENDING_LIVE_VERIFICATION + downstream_handoff_owner: null + live_admission_status: PENDING_SECRET_BINDING + legacy_fallbacks: [] -live_admission_status: PENDING_SECRET_BINDING legacy_fallbacks: [] diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_loader_binding.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_loader_binding.yml index e88f851f..392979e3 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_loader_binding.yml +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_loader_binding.yml @@ -8,7 +8,7 @@ fallback_allowed: false superseded_by: asset_id: BINDING-S2_00-CODE-EXECUTOR path: deployment/stage2_code_executor_binding.yml - schema_id: stage2_code_executor_binding.v2 + schema_id: stage2_code_executor_binding.v3 binding_status: EXTERNAL_TRUST_ROOT_PENDING_LIVE_ADMISSION retained_legacy_refs: - release_ops/stage2_loader.py diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_s2_10_llm_binding.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_s2_10_llm_binding.yml index bb9e311c..9fcf8461 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_s2_10_llm_binding.yml +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/deployment/stage2_s2_10_llm_binding.yml @@ -51,7 +51,7 @@ prompt_contract: selected_context_wrapper_sha256: c43c88cad59f2e9c88d944d4997551063e133207398dee0633af53e2bc5bcab6 cluster_payload_wrapper_sha256: aaffb47bb21a975a13075901f3152d7a41b6e1661fa1626f42e678ba8abfdda8 projection_receipt_path: manifest/s2_10_inline_prompt_projection_receipt.json - projection_receipt_sha256: c0e388e56f52ee4fef2b825307ef41b204a80dd740cbc8f1562b30c3cd1d3696 + projection_receipt_sha256: 4c95f3b3884900d1701f955e109f8927b9c5d818aa5c97623dba567faf7f61e9 canonical_source_paths: - prompts/P00_system_and_safety_contract.md - prompts/P10_legal_resolution_contract.md diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_coverage.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_coverage.json index 5a0b4376..63178ada 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_coverage.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_coverage.json @@ -16,16 +16,16 @@ }, "registry_dependency": { "path": "manifest/case_type_registry.yml", - "sha256": null, - "implementation_status": "NOT_IMPLEMENTED", - "catalog_exact_equality_status": "UNVERIFIED" + "sha256": "6462217f08ced21693e8cfc812182cfc1da593d52e719e5cd11ef30409ffd00e", + "implementation_status": "STRUCTURAL_REGISTRY_IMPLEMENTED_LEGAL_CONTENT_PENDING", + "catalog_exact_equality_status": "OFFLINE_EXACT_137_VERIFIED" }, "summary": { "total_case_type_rows": 137, "named_catalog_entries": 134, "generic_source_rows": 3, - "implementation_status": "NOT_IMPLEMENTED", - "verification_status": "UNVERIFIED" + "implementation_status": "STRUCTURAL_REGISTRY_IMPLEMENTED_LEGAL_CONTENT_PENDING", + "verification_status": "OFFLINE_EXACT_137_VERIFIED" }, "coverage_rows": [ { diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_registry.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_registry.yml new file mode 100644 index 00000000..4f1250fa --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_registry.yml @@ -0,0 +1,2208 @@ +{ + "schema_version": "stage2_case_type_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_137_V1", + "catalog_source_ref": "Case_02_Comparison_Research/case_kinds.md", + "catalog_source_sha256": "a5349c45576a44cbdf440a5d09a982b33c05aac154180a1e5412962e67d6b24a", + "catalog_row_ids": [ + "CAT-001", + "CAT-002", + "CAT-003", + "CAT-004", + "CAT-005", + "CAT-006", + "CAT-007", + "CAT-008", + "CAT-009", + "CAT-010", + "CAT-011", + "CAT-012", + "CAT-013", + "CAT-014", + "CAT-015", + "CAT-016", + "CAT-017", + "CAT-018", + "CAT-019", + "CAT-020", + "CAT-021", + "CAT-022", + "CAT-023", + "CAT-024", + "CAT-025", + "CAT-026", + "CAT-027", + "CAT-028", + "CAT-029", + "CAT-030", + "CAT-031", + "CAT-032", + "CAT-033", + "CAT-034", + "CAT-035", + "CAT-036", + "CAT-037", + "CAT-038", + "CAT-039", + "CAT-040", + "CAT-041", + "CAT-042", + "CAT-043", + "CAT-044", + "CAT-045", + "CAT-046", + "CAT-047", + "CAT-048", + "CAT-049", + "CAT-050", + "CAT-051", + "CAT-052", + "CAT-053", + "CAT-054", + "CAT-055", + "CAT-056", + "CAT-057", + "CAT-058", + "CAT-059", + "CAT-060", + "CAT-061", + "CAT-062", + "CAT-063", + "CAT-064", + "CAT-065", + "CAT-066", + "CAT-067", + "CAT-068", + "CAT-069", + "CAT-070", + "CAT-071", + "CAT-072", + "CAT-073", + "CAT-074", + "CAT-075", + "CAT-076", + "CAT-077", + "CAT-078", + "CAT-079", + "CAT-080", + "CAT-081", + "CAT-082", + "CAT-083", + "CAT-084", + "CAT-085", + "CAT-086", + "CAT-087", + "CAT-088", + "CAT-089", + "CAT-090", + "CAT-091", + "CAT-092", + "CAT-093", + "CAT-094", + "CAT-095", + "CAT-096", + "CAT-097", + "CAT-098", + "CAT-099", + "CAT-100", + "CAT-101", + "CAT-102", + "CAT-103", + "CAT-104", + "CAT-105", + "CAT-106", + "CAT-107", + "CAT-108", + "CAT-109", + "CAT-110", + "CAT-111", + "CAT-112", + "CAT-113", + "CAT-114", + "CAT-115", + "CAT-116", + "CAT-117", + "CAT-118", + "CAT-119", + "CAT-120", + "CAT-121", + "CAT-122", + "CAT-123", + "CAT-124", + "CAT-125", + "CAT-126", + "CAT-127", + "CAT-128", + "CAT-129", + "CAT-130", + "CAT-131", + "CAT-132", + "CAT-133", + "CAT-134", + "CAT-135", + "CAT-136", + "CAT-137" + ], + "rows": [ + { + "catalog_row_id": "CAT-001", + "source_ordinal": 1, + "source_label": "대여금 청구", + "case_type_id": "CT-001", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-002", + "source_ordinal": 2, + "source_label": "계금 청구", + "case_type_id": "CT-002", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-003", + "source_ordinal": 3, + "source_label": "계약금 청구", + "case_type_id": "CT-003", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-004", + "source_ordinal": 4, + "source_label": "공사대금 청구", + "case_type_id": "CT-004", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-005", + "source_ordinal": 5, + "source_label": "공제금 청구", + "case_type_id": "CT-005", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-006", + "source_ordinal": 6, + "source_label": "관리비 청구", + "case_type_id": "CT-006", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-007", + "source_ordinal": 7, + "source_label": "구상금 청구", + "case_type_id": "CT-007", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-008", + "source_ordinal": 8, + "source_label": "담보금 청구", + "case_type_id": "CT-008", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-009", + "source_ordinal": 9, + "source_label": "동업반환금 청구", + "case_type_id": "CT-009", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-010", + "source_ordinal": 10, + "source_label": "매매대금 청구", + "case_type_id": "CT-010", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-011", + "source_ordinal": 11, + "source_label": "배당금・배분금 청구", + "case_type_id": "CT-011", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-012", + "source_ordinal": 12, + "source_label": "배상금 청구", + "case_type_id": "CT-012", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-013", + "source_ordinal": 13, + "source_label": "변상금 청구", + "case_type_id": "CT-013", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-014", + "source_ordinal": 14, + "source_label": "보관금 청구", + "case_type_id": "CT-014", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-015", + "source_ordinal": 15, + "source_label": "보상금 청구", + "case_type_id": "CT-015", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-016", + "source_ordinal": 16, + "source_label": "보증금 청구", + "case_type_id": "CT-016", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-017", + "source_ordinal": 17, + "source_label": "보증채무금 청구", + "case_type_id": "CT-017", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-018", + "source_ordinal": 18, + "source_label": "보험금 및 보험금수익자변경 청구", + "case_type_id": "CT-018", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-019", + "source_ordinal": 19, + "source_label": "부금・불입금 청구", + "case_type_id": "CT-019", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-020", + "source_ordinal": 20, + "source_label": "부당이득금・이득상환금 청구", + "case_type_id": "CT-020", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-021", + "source_ordinal": 21, + "source_label": "분담금 청구", + "case_type_id": "CT-021", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-022", + "source_ordinal": 22, + "source_label": "분양대금 청구", + "case_type_id": "CT-022", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-023", + "source_ordinal": 23, + "source_label": "사용료 청구", + "case_type_id": "CT-023", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-024", + "source_ordinal": 24, + "source_label": "선급금・선수금 청구", + "case_type_id": "CT-024", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-025", + "source_ordinal": 25, + "source_label": "손해배상(자) 청구", + "case_type_id": "CT-025", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-026", + "source_ordinal": 26, + "source_label": "손해배상(산) 청구", + "case_type_id": "CT-026", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-027", + "source_ordinal": 27, + "source_label": "손해배상(의) 청구", + "case_type_id": "CT-027", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-028", + "source_ordinal": 28, + "source_label": "손해배상(지) 청구", + "case_type_id": "CT-028", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-029", + "source_ordinal": 29, + "source_label": "손해배상(저) 청구", + "case_type_id": "CT-029", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-030", + "source_ordinal": 30, + "source_label": "손해배상(언) 청구", + "case_type_id": "CT-030", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-031", + "source_ordinal": 31, + "source_label": "손해배상(건) 청구", + "case_type_id": "CT-031", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-032", + "source_ordinal": 32, + "source_label": "손해배상(국) 청구", + "case_type_id": "CT-032", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-033", + "source_ordinal": 33, + "source_label": "손해배상(해) 청구", + "case_type_id": "CT-033", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-034", + "source_ordinal": 34, + "source_label": "손해배상(기) 청구", + "case_type_id": "CT-034", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-035", + "source_ordinal": 35, + "source_label": "수표금 청구", + "case_type_id": "CT-035", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-036", + "source_ordinal": 36, + "source_label": "설계비・시설대여금 청구", + "case_type_id": "CT-036", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-037", + "source_ordinal": 37, + "source_label": "신용장대금 청구", + "case_type_id": "CT-037", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-038", + "source_ordinal": 38, + "source_label": "신용카드이용대금 청구", + "case_type_id": "CT-038", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-039", + "source_ordinal": 39, + "source_label": "압류채권대금 청구", + "case_type_id": "CT-039", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-040", + "source_ordinal": 40, + "source_label": "약속어음금 청구", + "case_type_id": "CT-040", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-041", + "source_ordinal": 41, + "source_label": "약정금 청구", + "case_type_id": "CT-041", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-042", + "source_ordinal": 42, + "source_label": "양도채권금・양수금 청구", + "case_type_id": "CT-042", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-043", + "source_ordinal": 43, + "source_label": "예금(예치금・예탁금・인출금) 청구", + "case_type_id": "CT-043", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-044", + "source_ordinal": 44, + "source_label": "운임(운송대금) 청구", + "case_type_id": "CT-044", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-045", + "source_ordinal": 45, + "source_label": "위약금 청구", + "case_type_id": "CT-045", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-046", + "source_ordinal": 46, + "source_label": "위탁금 청구", + "case_type_id": "CT-046", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-047", + "source_ordinal": 47, + "source_label": "유류분반환・유증 등 청구", + "case_type_id": "CT-047", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-048", + "source_ordinal": 48, + "source_label": "임대료 청구", + "case_type_id": "CT-048", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-049", + "source_ordinal": 49, + "source_label": "임차 및 전세보증금 청구", + "case_type_id": "CT-049", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-050", + "source_ordinal": 50, + "source_label": "재산상속회복 청구", + "case_type_id": "CT-050", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-051", + "source_ordinal": 51, + "source_label": "정산금 청구", + "case_type_id": "CT-051", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-052", + "source_ordinal": 52, + "source_label": "전부금 청구", + "case_type_id": "CT-052", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-053", + "source_ordinal": 53, + "source_label": "지료 청구", + "case_type_id": "CT-053", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-054", + "source_ordinal": 54, + "source_label": "진료비・치료비・의료비 청구", + "case_type_id": "CT-054", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-055", + "source_ordinal": 55, + "source_label": "청산금・채무인수금・체당금・추심금・출자금 청구 등", + "case_type_id": "CT-055", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-056", + "source_ordinal": 56, + "source_label": "근로자의 임금 및 퇴직금 청구", + "case_type_id": "CT-056", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-057", + "source_ordinal": 57, + "source_label": "투자금반환 청구", + "case_type_id": "CT-057", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-058", + "source_ordinal": 58, + "source_label": "특허권전용실시대금・실용신안권실시대금 청구", + "case_type_id": "CT-058", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-059", + "source_ordinal": 59, + "source_label": "하자보수비・할부대금・화해금・확약금・환급금・회원가입비 등", + "case_type_id": "CT-059", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-060", + "source_ordinal": 60, + "source_label": "종류물(대체물)의 지급 또는 인도를 구하는 소", + "case_type_id": "CT-060", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-061", + "source_ordinal": 61, + "source_label": "토지의 인도를 구하는 소", + "case_type_id": "CT-061", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-062", + "source_ordinal": 62, + "source_label": "임대인의 건물철거청구와 임차인의 건물매수청구권 행사", + "case_type_id": "CT-062", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-063", + "source_ordinal": 63, + "source_label": "건물의 명도(인도)의 소", + "case_type_id": "CT-063", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-064", + "source_ordinal": 64, + "source_label": "점유회수(반환)청구의 소", + "case_type_id": "CT-064", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-065", + "source_ordinal": 65, + "source_label": "동산 등의 인도 청구", + "case_type_id": "CT-065", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-066", + "source_ordinal": 66, + "source_label": "매매를 원인으로 한 소유권이전등기 청구", + "case_type_id": "CT-066", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-067", + "source_ordinal": 67, + "source_label": "매수인의 잔대금지급의무와 매도인의 소유권이전등기 청구", + "case_type_id": "CT-067", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-068", + "source_ordinal": 68, + "source_label": "계약해제로 인한 원상회복의무와 손해배상의무의 관계", + "case_type_id": "CT-068", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-069", + "source_ordinal": 69, + "source_label": "교환・교환약정을 원인으로 한 소유권이전등기 청구", + "case_type_id": "CT-069", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-070", + "source_ordinal": 70, + "source_label": "대물반환・대물변제 등을 원인으로 한 소유권이전등기 청구", + "case_type_id": "CT-070", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-071", + "source_ordinal": 71, + "source_label": "명의신탁해지를 원인으로 한 소유권이전등기 청구", + "case_type_id": "CT-071", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-072", + "source_ordinal": 72, + "source_label": "취득시효완성을 원인으로 한 소유권이전등기 청구", + "case_type_id": "CT-072", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-073", + "source_ordinal": 73, + "source_label": "양도약정・담보계약해지・이관을 원인으로 한 소유권이전등기", + "case_type_id": "CT-073", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-074", + "source_ordinal": 74, + "source_label": "재산분할・재산승계를 원인으로 한 소유권이전등기 청구", + "case_type_id": "CT-074", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-075", + "source_ordinal": 75, + "source_label": "증여・유증・유류분반환을 원인으로 한 소유권이전 청구", + "case_type_id": "CT-075", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-076", + "source_ordinal": 76, + "source_label": "화해·환매를 원인으로 한 소유권이전등기 청구", + "case_type_id": "CT-076", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-077", + "source_ordinal": 77, + "source_label": "가등기에 기한 본등기 청구", + "case_type_id": "CT-077", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-078", + "source_ordinal": 78, + "source_label": "대위에 의한 소유권이전등기 청구", + "case_type_id": "CT-078", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-079", + "source_ordinal": 79, + "source_label": "소유권 이외의 권리 설정등기 및 이전등기 청구", + "case_type_id": "CT-079", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-080", + "source_ordinal": 80, + "source_label": "말소등기(소유권이전등기말소) 청구", + "case_type_id": "CT-080", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-081", + "source_ordinal": 81, + "source_label": "말소등기(소유권보존등기말소) 청구", + "case_type_id": "CT-081", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-082", + "source_ordinal": 82, + "source_label": "말소등기(근저당권설정등기말소) 청구", + "case_type_id": "CT-082", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-083", + "source_ordinal": 83, + "source_label": "말소등기(가등기말소) 청구", + "case_type_id": "CT-083", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-084", + "source_ordinal": 84, + "source_label": "말소등기(지상권설정등기말소) 청구", + "case_type_id": "CT-084", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-085", + "source_ordinal": 85, + "source_label": "말소등기(전세권설정등기말소) 청구", + "case_type_id": "CT-085", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-086", + "source_ordinal": 86, + "source_label": "말소등기(대위에의한소유권이전등기말소) 청구", + "case_type_id": "CT-086", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-087", + "source_ordinal": 87, + "source_label": "말소등기(토지의일부만에관하여말소등기) 청구", + "case_type_id": "CT-087", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-088", + "source_ordinal": 88, + "source_label": "말소등기(폐쇄한등기기록에기록된등기의말소) 청구", + "case_type_id": "CT-088", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-089", + "source_ordinal": 89, + "source_label": "말소등기(사위(詐僞)판결에의한등기의말소) 청구", + "case_type_id": "CT-089", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-090", + "source_ordinal": 90, + "source_label": "말소등기(기타등기에관한말소) 청구", + "case_type_id": "CT-090", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-091", + "source_ordinal": 91, + "source_label": "말소등기의 회복등기 청구", + "case_type_id": "CT-091", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-092", + "source_ordinal": 92, + "source_label": "경정등기 청구", + "case_type_id": "CT-092", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-093", + "source_ordinal": 93, + "source_label": "등기상 이해관계 있는 제3자의 승낙의 의사표시", + "case_type_id": "CT-093", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-094", + "source_ordinal": 94, + "source_label": "진정명의 회복을 등기원인으로 하는 소유권이전등기 청구", + "case_type_id": "CT-094", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-095", + "source_ordinal": 95, + "source_label": "명의변경절차에 관한 소송", + "case_type_id": "CT-095", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-096", + "source_ordinal": 96, + "source_label": "장래이행의 소", + "case_type_id": "CT-096", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-097", + "source_ordinal": 97, + "source_label": "소유물방해제거·방해예방청구", + "case_type_id": "CT-097", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-098", + "source_ordinal": 98, + "source_label": "정정보도·반론보도·사과광고 청구", + "case_type_id": "CT-098", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-099", + "source_ordinal": 99, + "source_label": "토지거래허가신청의 협력의무 이행청구", + "case_type_id": "CT-099", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-100", + "source_ordinal": 100, + "source_label": "채권존재확인 청구", + "case_type_id": "CT-100", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-101", + "source_ordinal": 101, + "source_label": "채권부존재확인 청구", + "case_type_id": "CT-101", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-102", + "source_ordinal": 102, + "source_label": "채무부존재확인 청구", + "case_type_id": "CT-102", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-103", + "source_ordinal": 103, + "source_label": "임차권 확인 청구", + "case_type_id": "CT-103", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-104", + "source_ordinal": 104, + "source_label": "부동산 소유권 확인", + "case_type_id": "CT-104", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-105", + "source_ordinal": 105, + "source_label": "도메인 소유권", + "case_type_id": "CT-105", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-106", + "source_ordinal": 106, + "source_label": "입목 소유권", + "case_type_id": "CT-106", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-107", + "source_ordinal": 107, + "source_label": "통상실시권 확인", + "case_type_id": "CT-107", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-108", + "source_ordinal": 108, + "source_label": "불상·신탁·차등차 등 소유권 확인", + "case_type_id": "CT-108", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-109", + "source_ordinal": 109, + "source_label": "유치권확인 또는 부존재확인", + "case_type_id": "CT-109", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-110", + "source_ordinal": 110, + "source_label": "증서의 진정여부를 확인하는 소", + "case_type_id": "CT-110", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-111", + "source_ordinal": 111, + "source_label": "대의원회결의무효 등", + "case_type_id": "CT-111", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-112", + "source_ordinal": 112, + "source_label": "이사회결의무효", + "case_type_id": "CT-112", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-113", + "source_ordinal": 113, + "source_label": "총회결의무효확인", + "case_type_id": "CT-113", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-114", + "source_ordinal": 114, + "source_label": "이사회결의부존재", + "case_type_id": "CT-114", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-115", + "source_ordinal": 115, + "source_label": "임시주주총회결의부존재 등", + "case_type_id": "CT-115", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-116", + "source_ordinal": 116, + "source_label": "기타 결의부존재확인", + "case_type_id": "CT-116", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-117", + "source_ordinal": 117, + "source_label": "교원의 지위", + "case_type_id": "CT-117", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-118", + "source_ordinal": 118, + "source_label": "근로자의 지위 등", + "case_type_id": "CT-118", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-119", + "source_ordinal": 119, + "source_label": "분양권·매수인의 지위 등", + "case_type_id": "CT-119", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-120", + "source_ordinal": 120, + "source_label": "기타 지위확인", + "case_type_id": "CT-120", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-121", + "source_ordinal": 121, + "source_label": "공탁금출급청구권", + "case_type_id": "CT-121", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-122", + "source_ordinal": 122, + "source_label": "보상금·수용금수령권", + "case_type_id": "CT-122", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-123", + "source_ordinal": 123, + "source_label": "분양권 확인 청구", + "case_type_id": "CT-123", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-124", + "source_ordinal": 124, + "source_label": "시설출입이용권", + "case_type_id": "CT-124", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-125", + "source_ordinal": 125, + "source_label": "주위토지통행권", + "case_type_id": "CT-125", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-126", + "source_ordinal": 126, + "source_label": "주주권·주식질권 등", + "case_type_id": "CT-126", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-127", + "source_ordinal": 127, + "source_label": "기타 권리확인", + "case_type_id": "CT-127", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-128", + "source_ordinal": 128, + "source_label": "기타 확인청구", + "case_type_id": "CT-128", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-129", + "source_ordinal": 129, + "source_label": "경계확정 청구", + "case_type_id": "CT-129", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-130", + "source_ordinal": 130, + "source_label": "공유물분할 청구", + "case_type_id": "CT-130", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-131", + "source_ordinal": 131, + "source_label": "청구이의", + "case_type_id": "CT-131", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-132", + "source_ordinal": 132, + "source_label": "제3자이의", + "case_type_id": "CT-132", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-133", + "source_ordinal": 133, + "source_label": "재심 청구", + "case_type_id": "CT-133", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-134", + "source_ordinal": 134, + "source_label": "준재심 청구", + "case_type_id": "CT-134", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-135", + "source_ordinal": 135, + "source_label": "제권판결에 대한 불복의 소", + "case_type_id": "CT-135", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-136", + "source_ordinal": 136, + "source_label": "사해행위취소 청구", + "case_type_id": "CT-136", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "catalog_row_id": "CAT-137", + "source_ordinal": 137, + "source_label": "주주총회결의취소의 소", + "case_type_id": "CT-137", + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + } + ], + "claim_capable_case_type_ids": [], + "pending_legal_classification_case_type_ids": [ + "CT-001", + "CT-002", + "CT-003", + "CT-004", + "CT-005", + "CT-006", + "CT-007", + "CT-008", + "CT-009", + "CT-010", + "CT-011", + "CT-012", + "CT-013", + "CT-014", + "CT-015", + "CT-016", + "CT-017", + "CT-018", + "CT-019", + "CT-020", + "CT-021", + "CT-022", + "CT-023", + "CT-024", + "CT-025", + "CT-026", + "CT-027", + "CT-028", + "CT-029", + "CT-030", + "CT-031", + "CT-032", + "CT-033", + "CT-034", + "CT-035", + "CT-036", + "CT-037", + "CT-038", + "CT-039", + "CT-040", + "CT-041", + "CT-042", + "CT-043", + "CT-044", + "CT-045", + "CT-046", + "CT-047", + "CT-048", + "CT-049", + "CT-050", + "CT-051", + "CT-052", + "CT-053", + "CT-054", + "CT-055", + "CT-056", + "CT-057", + "CT-058", + "CT-059", + "CT-060", + "CT-061", + "CT-062", + "CT-063", + "CT-064", + "CT-065", + "CT-066", + "CT-067", + "CT-068", + "CT-069", + "CT-070", + "CT-071", + "CT-072", + "CT-073", + "CT-074", + "CT-075", + "CT-076", + "CT-077", + "CT-078", + "CT-079", + "CT-080", + "CT-081", + "CT-082", + "CT-083", + "CT-084", + "CT-085", + "CT-086", + "CT-087", + "CT-088", + "CT-089", + "CT-090", + "CT-091", + "CT-092", + "CT-093", + "CT-094", + "CT-095", + "CT-096", + "CT-097", + "CT-098", + "CT-099", + "CT-100", + "CT-101", + "CT-102", + "CT-103", + "CT-104", + "CT-105", + "CT-106", + "CT-107", + "CT-108", + "CT-109", + "CT-110", + "CT-111", + "CT-112", + "CT-113", + "CT-114", + "CT-115", + "CT-116", + "CT-117", + "CT-118", + "CT-119", + "CT-120", + "CT-121", + "CT-122", + "CT-123", + "CT-124", + "CT-125", + "CT-126", + "CT-127", + "CT-128", + "CT-129", + "CT-130", + "CT-131", + "CT-132", + "CT-133", + "CT-134", + "CT-135", + "CT-136", + "CT-137" + ], + "fuzzy_match_allowed": false, + "catch_all_fallback_allowed": false, + "registry_status": "PENDING_LEGAL_CLASSIFICATION" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_rule_registry.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_rule_registry.yml new file mode 100644 index 00000000..7cdd541e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/case_type_rule_registry.yml @@ -0,0 +1,1519 @@ +{ + "schema_version": "stage2_case_type_rule_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_RULE_REGISTRY_V1", + "case_type_registry_ref": "manifest/case_type_registry.yml", + "case_type_registry_sha256": "6462217f08ced21693e8cfc812182cfc1da593d52e719e5cd11ef30409ffd00e", + "rows": [ + { + "case_type_id": "CT-001", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-002", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-003", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-004", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-005", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-006", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-007", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-008", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-009", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-010", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-011", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-012", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-013", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-014", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-015", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-016", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-017", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-018", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-019", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-020", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-021", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-022", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-023", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-024", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-025", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-026", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-027", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-028", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-029", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-030", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-031", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-032", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-033", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-034", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-035", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-036", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-037", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-038", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-039", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-040", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-041", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-042", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-043", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-044", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-045", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-046", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-047", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-048", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-049", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-050", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-051", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-052", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-053", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-054", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-055", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-056", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-057", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-058", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-059", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-060", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-061", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-062", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-063", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-064", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-065", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-066", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-067", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-068", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-069", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-070", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-071", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-072", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-073", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-074", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-075", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-076", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-077", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-078", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-079", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-080", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-081", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-082", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-083", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-084", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-085", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-086", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-087", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-088", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-089", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-090", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-091", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-092", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-093", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-094", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-095", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-096", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-097", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-098", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-099", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-100", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-101", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-102", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-103", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-104", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-105", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-106", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-107", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-108", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-109", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-110", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-111", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-112", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-113", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-114", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-115", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-116", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-117", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-118", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-119", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-120", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-121", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-122", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-123", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-124", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-125", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-126", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-127", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-128", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-129", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-130", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-131", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-132", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-133", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-134", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-135", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-136", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + }, + { + "case_type_id": "CT-137", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": null, + "companion_case_type_ids": [], + "legal_review_receipt_ref": null, + "legal_review_receipt_sha256": null + } + ], + "runtime_branch_selector": "EXACT_TARGET_SIGNATURE_TO_CASE_TYPE_ID_TO_SUB_RULE_ID", + "fuzzy_fallback_allowed": false, + "nearest_rule_fallback_allowed": false, + "registry_status": "PENDING_LEGAL_CONTENT" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/module_manifest.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/module_manifest.json index dc020eeb..fbfd8c40 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/module_manifest.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/module_manifest.json @@ -1 +1 @@ -{"closure_summary":{"build_tool_module_count":1,"cycle_breaking_contract":"AGENT_CODE_RECEIPT_AND_EXECUTOR_BINDING_EXTERNALLY_BIND_RELEASE_RAW_HASH","documentation_mirror_count":8,"executable_agent_hash_included":false,"executor_binding_hash_included":false,"fixture_closure_ref":"tests/fixtures/regression_manifest.json","fixture_descriptor_count":60,"module_count":76,"profile_module_count":45,"prompt_module_count":2,"runtime_code_hash_included":false,"self_hash_included":false,"stage2_release_hash_included":false,"test_module_count":12},"documentation_mirrors":[{"byte_identical":true,"mirror_path":"offline_build/build_s2_00_inline_projection.txt","mirror_sha256":"5da9e66f3c8d2bff7a2d99d17f9e481a249f3e2587a2547be094c8dab8584928","runtime_import_allowed":false,"source_path":"offline_build/build_s2_00_inline_projection.py","source_sha256":"5da9e66f3c8d2bff7a2d99d17f9e481a249f3e2587a2547be094c8dab8584928"},{"byte_identical":true,"mirror_path":"tests/s2_00/test_canonical_ids.txt","mirror_sha256":"c463506a90859daeef7eafddc4a6c08c563e2ae73200d921c1f00efb7bc24361","runtime_import_allowed":false,"source_path":"tests/s2_00/test_canonical_ids.py","source_sha256":"c463506a90859daeef7eafddc4a6c08c563e2ae73200d921c1f00efb7bc24361"},{"byte_identical":true,"mirror_path":"tests/s2_00/test_cluster_bundle_compile.txt","mirror_sha256":"9d9254af239abef75393698676a8b158de828f42406a85c1472399f6fa8cb17d","runtime_import_allowed":false,"source_path":"tests/s2_00/test_cluster_bundle_compile.py","source_sha256":"9d9254af239abef75393698676a8b158de828f42406a85c1472399f6fa8cb17d"},{"byte_identical":true,"mirror_path":"tests/s2_00/test_conservation.txt","mirror_sha256":"c906dff7e11678a60556818d1baf24e66844d9c982c9d333ec339d2ed9ca6061","runtime_import_allowed":false,"source_path":"tests/s2_00/test_conservation.py","source_sha256":"c906dff7e11678a60556818d1baf24e66844d9c982c9d333ec339d2ed9ca6061"},{"byte_identical":true,"mirror_path":"tests/s2_00/test_failure_and_atomic_publish.txt","mirror_sha256":"47c8ed00d713ba0be53aed3ac76b4d2396bd5557cfd5d4bdd93a5e9f0fbe2ac6","runtime_import_allowed":false,"source_path":"tests/s2_00/test_failure_and_atomic_publish.py","source_sha256":"47c8ed00d713ba0be53aed3ac76b4d2396bd5557cfd5d4bdd93a5e9f0fbe2ac6"},{"byte_identical":true,"mirror_path":"tests/s2_00/test_inline_code_parity.txt","mirror_sha256":"00b592fa94a9b7ea393f3a3504530f0602c4efc9bd54c84a272fc9ba3ca3a3b4","runtime_import_allowed":false,"source_path":"tests/s2_00/test_inline_code_parity.py","source_sha256":"00b592fa94a9b7ea393f3a3504530f0602c4efc9bd54c84a272fc9ba3ca3a3b4"},{"byte_identical":true,"mirror_path":"tests/s2_00/test_resolver_source_lock.txt","mirror_sha256":"51bc1b310d40f5068df0a66446e5b96b00ea9b5b0137ddaf563caadf9f8fc51e","runtime_import_allowed":false,"source_path":"tests/s2_00/test_resolver_source_lock.py","source_sha256":"51bc1b310d40f5068df0a66446e5b96b00ea9b5b0137ddaf563caadf9f8fc51e"},{"byte_identical":true,"mirror_path":"tests/s2_00/test_schema_hash_and_signals.txt","mirror_sha256":"71a9a15e5910d99c8e8e7b8b2f607b41244b3fd83a8c6e07d03bc0a0b986193c","runtime_import_allowed":false,"source_path":"tests/s2_00/test_schema_hash_and_signals.py","source_sha256":"71a9a15e5910d99c8e8e7b8b2f607b41244b3fd83a8c6e07d03bc0a0b986193c"}],"forbidden_contract_registry":[{"code":"LEGACY-STAGE2-V0-V3","legacy_runtime_dependency_allowed":false,"meaning":"The clean S2_00 workflow and runtime must not read or execute legacy Stage 2 v.0-v.3 specifications."},{"code":"LEGACY-STAGE2-LOADER","legacy_runtime_dependency_allowed":false,"meaning":"The historical host loader and loader binding are reference-only and cannot authorize, execute, or provide fallback for S2_00."},{"code":"RAW-STAGE1-REREAD","legacy_runtime_dependency_allowed":false,"meaning":"S2_10 must consume immutable bounded cluster projections and must not rediscover raw Stage 1 inputs."},{"code":"S2_40-PLEADING-RENDER-IN-STATUS-ONLY","legacy_runtime_dependency_allowed":false,"meaning":"The S2_40 status-only entrypoint must not render or commit pleading text."}],"implementation_status":"OFFLINE_HYBRID_RESEAL_COMPLETE_LIVE_ADMISSION_PENDING","manifest_digest":"5f971e3d6332d52cfa5f2ba839435a168ae188a5faa7be30926add9aef08a2d2","manifest_digest_contract":{"algorithm_id":"STAGE2-MODULE-MANIFEST-DIGEST-V1","array_order":"PRESERVE_DECLARED_ORDER","canonicalization_algorithm_id":"STAGE2-CANONICAL-JSON-V1","digest_algorithm":"sha256","digest_scope":"ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_MANIFEST_DIGEST","encoding":"UTF-8","line_termination":"LF_ONE_TRAILING_NEWLINE","non_finite_numbers_allowed":false,"object_key_order":"SORT_CODEPOINT","verification_status":"DEV_VERIFIED"},"manifest_id":"STAGE2-CLEAN-S2_00-STRUCTURED-HANDOFF-V2-INLINE","manifest_scope":"S2_00_STRUCTURED_CONTEXT_HANDOFF_AND_SHARED_CONTRACTS","manifest_status":"DEV_HASH_BOUND_DOWNSTREAM_HYBRID_RELEASE_PENDING","modules":[{"asset_version":"s2_00.1.2","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json","https://schemas.liti-agent.local/stage2/s2_00/context.schema.v2.json","https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json"],"dependency_module_ids":["SCHEMA-INGRESS","SCHEMA-CONTEXT","SCHEMA-REVIEW-STATUS"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"IMPLEMENTED_DECLARATIVE_CONTRACT","incompatible_module_ids":[],"inputs":["STAGE1-ALLOWLIST-V1","STAGE1-DEPLOYMENT-CLOSURE-2026-08-29"],"module_id":"WF-S2_00","module_kind":"WORKFLOW","outputs":["S2_00-NORMAL-BRANCH","S2_00-DIAGNOSTIC-BRANCH"],"owner":"Stage_2_workflow_maintainer","path":"workflows/S2_00_stage1_ingress_normalize_and_bundle_compile.yml","produced_schema_ids":[],"schema_version":"stage2_workflow_contract.v1.2","scope_predicate":"workflow_id == S2_00","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"3d7ceb86b236668c8c372136f1d1b43a5d0a79d3fb05fd1ef4342bb638190f68"},{"asset_version":"s2_00.schema.1.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"SCHEMA-INGRESS","module_kind":"JSON_SCHEMA","outputs":["INGRESS-SCHEMA-DEFS"],"owner":"Stage_2_schema_owner","path":"schemas/ingress.schema.json","produced_schema_ids":["https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json"],"schema_version":"stage2_s2_00_ingress.v1.1","scope_predicate":"artifact_family == ingress","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"8fd7b76015b7d7dfbbe056e08999dda7ea8e1012bb16cec50634b08f7ea97302"},{"asset_version":"s2_00.schema.2","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json"],"dependency_module_ids":["SCHEMA-REVIEW-STATUS"],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"SCHEMA-CONTEXT","module_kind":"JSON_SCHEMA","outputs":["CONTEXT-SCHEMA-DEFS"],"owner":"Stage_2_schema_owner","path":"schemas/context.schema.json","produced_schema_ids":["https://schemas.liti-agent.local/stage2/s2_00/context.schema.v2.json"],"schema_version":"stage2_s2_00_context.v2","scope_predicate":"artifact_family == context","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"29073ffa847d74304228d61306503c0c3798da7db8b5717b1ad6d5b2da98aa92"},{"asset_version":"stage2.shared.2","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"SCHEMA-DEPLOYMENT","module_kind":"JSON_SCHEMA","outputs":["DEPLOYMENT-SCHEMA-DEFS"],"owner":"Stage_2_deployment_schema_owner","path":"schemas/deployment.schema.json","produced_schema_ids":["https://schemas.liti-agent.local/stage2/shared/deployment.schema.v2.json"],"schema_version":"stage2_deployment.v2","scope_predicate":"artifact_family == deployment","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"2938b2e6fc2b414ca1839d58ac75f25f62ae571dfa5e99405448f4d5890b5bf5"},{"asset_version":"stage2.shared.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"SCHEMA-REVIEW-STATUS","module_kind":"JSON_SCHEMA","outputs":["REVIEW-STATUS-SCHEMA-DEFS"],"owner":"Stage_2_review_schema_owner","path":"schemas/review_status.schema.json","produced_schema_ids":["https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json"],"schema_version":"stage2_review_status.v1","scope_predicate":"artifact_family == review_status","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"edcdd127e648c6b045abb852146a63f19936d300a350023b8987700afa05fde7"},{"asset_version":"stage2.cache.2","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["SCHEMA-CONTEXT"],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["RELEASE-SELECTED-CONTEXT-ORDERED-REFS","S2_00-COHORT-MEMBERSHIP","S2_00-MEMBER-SLICE-REF-SET"],"module_id":"CACHE-POLICY-STRUCTURED-CONTEXT-HANDOFF","module_kind":"CACHE_POLICY","outputs":["S2_00-CONTEXT-MATERIALIZATION-RECEIPT","S2_10-CACHE-IDENTITY-INPUT"],"owner":"Stage_2_cache_policy_owner","path":"registry/cache/prompt_cache_policy.yml","produced_schema_ids":[],"schema_version":"stage2_prompt_cache_policy.v2","scope_predicate":"consumer == S2_10","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"b459cc271e2f51d783cdd89872d24dc7635ac237e76cd6ad03253569190b7b90"},{"asset_version":"s2_00.test.1.2","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"module_id":"REGRESSION-MANIFEST-S2_00","module_kind":"TEST_MANIFEST","outputs":["FIXTURE-RESULT-DIGEST"],"owner":"Stage_2_regression_owner","path":"tests/fixtures/regression_manifest.json","produced_schema_ids":[],"schema_version":"stage2_s2_00_regression_manifest.v1","scope_predicate":"workflow_id == S2_00","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"77ee3998c503797c034acf97629b5ebcbc4aec5042b52d0efbc38bc5d3a36171"},{"asset_version":"s2_40.status_only.1","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json","https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json"],"dependency_module_ids":["WF-S2_00","SCHEMA-REVIEW-STATUS"],"forbidden_contract_codes":["S2_40-PLEADING-RENDER-IN-STATUS-ONLY"],"implementation_status":"DRAFT_HANDOFF_STUB_HASH_BOUND","incompatible_module_ids":[],"inputs":["ingress/ingress_status.json","ingress/technical_diagnostic.json","ingress/stage1_input_manifest.json","ingress/intake_report.json","review/issue_ledger.base.json"],"module_id":"WF-S2_40-STATUS-ONLY","module_kind":"WORKFLOW","outputs":["control/run_status.json"],"owner":"Stage_2_S2_40_owner","path":"workflows/S2_40_final_review_render_and_commit.yml","produced_schema_ids":["stage2_status_only_commit.v1"],"schema_version":"stage2_workflow_contract.v1","scope_predicate":"entrypoint_id == S2_40_STATUS_ONLY","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"b7d83b56771d005e6185746c3469d42f7781927522b8a8d42648301eceaadadd"},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"module_id":"TEST-S2_00-RESOLVER-SOURCE-LOCK","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_resolver_source_lock.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == resolver_source_lock","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"51bc1b310d40f5068df0a66446e5b96b00ea9b5b0137ddaf563caadf9f8fc51e"},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"module_id":"TEST-S2_00-SCHEMA-HASH-SIGNALS","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_schema_hash_and_signals.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == schema_hash_signals","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"71a9a15e5910d99c8e8e7b8b2f607b41244b3fd83a8c6e07d03bc0a0b986193c"},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"module_id":"TEST-S2_00-CONSERVATION","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_conservation.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == conservation","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"c906dff7e11678a60556818d1baf24e66844d9c982c9d333ec339d2ed9ca6061"},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"module_id":"TEST-S2_00-CANONICAL-IDS","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_canonical_ids.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == canonical_ids","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"c463506a90859daeef7eafddc4a6c08c563e2ae73200d921c1f00efb7bc24361"},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"module_id":"TEST-S2_00-CLUSTER-BUNDLE","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_cluster_bundle_compile.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == cluster_bundle_compile","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"9d9254af239abef75393698676a8b158de828f42406a85c1472399f6fa8cb17d"},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"module_id":"TEST-S2_00-FAILURE-ATOMIC-PUBLISH","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_failure_and_atomic_publish.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == failure_atomic_publish","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"47c8ed00d713ba0be53aed3ac76b4d2396bd5557cfd5d4bdd93a5e9f0fbe2ac6"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"P00","module_kind":"LLM_PROMPT_CONTRACT","outputs":["STATIC_PREFIX_SEGMENT"],"owner":"Stage_2_prompt_owner","path":"prompts/P00_system_and_safety_contract.md","produced_schema_ids":[],"schema_version":"stage2.prompt_contract.v1","scope_predicate":"static_prefix_segment == P00","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P00"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P00"],"module_id":"P10","module_kind":"LLM_PROMPT_CONTRACT","outputs":["STATIC_PREFIX_SEGMENT"],"owner":"Stage_2_prompt_owner","path":"prompts/P10_legal_resolution_contract.md","produced_schema_ids":[],"schema_version":"stage2.prompt_contract.v1","scope_predicate":"static_prefix_segment == P10","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-EC-00_CONTRACT_GENERAL","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/EC-00_contract_general.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/EC-00_contract_general.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"a4f7abd9957b3a9e58d05dd559c3c869bf96fd88ea18968a363c6e606e3c0aa8"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-01_JURISTIC_ACT_VALIDITY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-01_juristic_act_validity.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-01_juristic_act_validity.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"0a0d2676f96be321a7bb974b8e4fb1981ab43773f271c99dc86a70cb17d27c99"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-02_CONTRACT_MONEY_CLAIM","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-02_contract_money_claim.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-02_contract_money_claim.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"08e252bccc718ccc44a258bc540afc2567e509239399f4017550df32bac97824"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-03_PARTIES_LIABILITY_SUCCESSION","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-03_parties_liability_succession.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-03_parties_liability_succession.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"4e841d5f640b06a1b52060b82dda39605369cbe26d81655b7918673437e4519c"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-04_UNJUST_ENRICHMENT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-04_unjust_enrichment.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-04_unjust_enrichment.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"31a5966d4c14940e810f870bf4382f1be642f7716ebaaa35193ef32e9b9d95c7"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-05_TORT_GENERAL","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-05_tort_general.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-05_tort_general.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fe6ea92fc10c4196e0ac83b990bf2606e24024030e7b0002fecdae9f8f50221a"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-06_PROFESSIONAL_LIABILITY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-06_professional_liability.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-06_professional_liability.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"efa7a4f6cda90c04a06558a9d0e1efe13b25169d55d03e36d55d72fcdd096a15"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-07_CONSTRUCTION_DEFECT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-07_construction_defect.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-07_construction_defect.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"78c76cb0485a4acf741109aee05f634f51c3a6904044c613080b21c3978a09f9"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-08_LEASE_DEPOSIT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-08_lease_deposit.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-08_lease_deposit.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"dbe1886671edff49b8b0251eed567d1b3e4e5d748456434262286ca7acf990db"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-09_REGISTRY_TRANSFER_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-09_registry_transfer_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-09_registry_transfer_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"d18946322a8cb528146ac0aa0a2880c41ac2056671fd237b928341ceb8561002"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-10_SECURED_REGISTRY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-10_secured_registry.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-10_secured_registry.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"5819c26ec8478e12a12e340c93a9d23cc410833e1192c2315a1986c9d48a1eba"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-11_POSSESSION_VINDICATION","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-11_possession_vindication.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-11_possession_vindication.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"5e6d6460be4ec0f2ba0096305b9ae39f5f430d107337d0d7e07aac1a9d599aaf"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-12_CO_OWNERSHIP_BOUNDARY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-12_co_ownership_boundary.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-12_co_ownership_boundary.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"62cfbf6a6c5c5edc8b912e348be1c4d4f7dd4c20c722e57e5554058f0caafb4a"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-13_CREDITOR_PRESERVATION","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-13_creditor_preservation.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-13_creditor_preservation.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"c60e6d67c7e3985d1f6ce44027726282f931934e3e66b44f06ccd26845ca2f45"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-14_EXECUTION_LINKED_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-14_execution_linked_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-14_execution_linked_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"45d4b3ab46e1c8834c3b3eec72a28d05e39cb78a9ec2c2de07b5e55e81aeebd7"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-15_SUCCESSION_FAMILY_PROPERTY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-15_succession_family_property.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-15_succession_family_property.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"f691b67295ad8e634b34519cf71156caf6eaf73cef54577615ab79462f8aa766"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-16_NEGOTIABLE_INSTRUMENTS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-16_negotiable_instruments.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-16_negotiable_instruments.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"7720d83b64e7a44720788f6ff3c7cc8ceaf5195377a1e61b52de2144989a1194"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-17_LABOR_WAGE_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-17_labor_wage_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-17_labor_wage_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"168ce5e41104937cf7ab946944f060b82cfe16066425897f07bb9e837b8689ea"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-18_ORG_RESOLUTION_STATUS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-18_org_resolution_status.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-18_org_resolution_status.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"59cf689f886dfaf4ad00dbe6142f5b11c37d0062660f7467aa43427462aa075f"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-19_INSURANCE_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-19_insurance_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-19_insurance_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"0fb4793446c155e7a0b4469c9b8c1be1323112eef55ca4c495307c1af56e754c"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-20_IP_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-20_ip_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-20_ip_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fe07f20d149a2c4deac2e46de299cd78f0bf829f58c615a86c45f542d30804e1"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-21_MEDIA_PERSONALITY_RIGHTS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-21_media_personality_rights.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-21_media_personality_rights.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"951260bbe8ef116d682f49f3679106c7144ca68f8078f5c10b3459667c1f5b2c"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-00_RESIDUAL_UNROUTED","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/E-00_residual_unrouted.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/E-00_residual_unrouted.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"639e507147a6132f9e41ccd0b00d1b5450d6a9629991629b173d5bcaf5b6df39"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-X1_NOTICE_LIFECYCLE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/X1_notice_lifecycle.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/X1_notice_lifecycle.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"9af8b5b5f3a8196a10308d1ee34e0788d9adeac7a99e142e449490d14316faef"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-X2_ASSET_IDENTITY_LINEAGE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/X2_asset_identity_lineage.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/X2_asset_identity_lineage.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"6c1e437944b3a62b60ee7acad5ec9177fefac2991fe970338acc27efcc8a13ad"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-X3_PROCEDURE_STANDING_RELIEF","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/X3_procedure_standing_relief.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/X3_procedure_standing_relief.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"f3b794bef82566e569232ba02c63ae148099143d8d1ab84de1c4fb76105e65df"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-X4_RESPONSE_ADMISSION_DEFENSE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/X4_response_admission_defense.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/X4_response_admission_defense.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"57851ed55b3351a8139df29224d74b7cb8427708bbf6de00ecd27c44089df32e"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-AUTO_MOTOR_VEHICLE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-AUTO_motor_vehicle.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-AUTO_motor_vehicle.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"7234a7ab890f242e6b7aa811528e34c0c85779007cffbf135da0bfff36d61d91"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-INDUSTRIAL_ACCIDENT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-INDUSTRIAL_ACCIDENT.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-INDUSTRIAL_ACCIDENT.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"10d78b8031df7d89f835afc9ca60fab36648e561c729780204c80885f4dd097f"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-PRODUCT_LIABILITY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-PRODUCT_LIABILITY.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-PRODUCT_LIABILITY.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"1d5ee8eddcf005fdb33df58f591a8948aa3e891a3a5656d919394ff0b39eea53"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-RESIDENTIAL_LEASE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-RESIDENTIAL_LEASE.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-RESIDENTIAL_LEASE.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"3e2efd1b3c0524a305eafedc5ec91e05be989a6e55c1501ed161a9b8f8151fd7"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-COMMERCIAL_LEASE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-COMMERCIAL_LEASE.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-COMMERCIAL_LEASE.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"5aebed9ab56bc00eb7e0511f9fe5db8c3a42f1e81b28d58c38d487aa91cbab3e"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-LABOR","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-LABOR.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-LABOR.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"6b2542bb64f4c1d164b1ccd2ce5099fad29bcea4333d9e34bd0fb1667b9ced4a"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-STATE_LIABILITY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-STATE_LIABILITY.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-STATE_LIABILITY.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"5b2fe6d57b918384a619efbd04360ddfaf7720d384136f69d359ccb0b6c853d3"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-IP-PATENT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-IP-PATENT.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-IP-PATENT.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"d0651a8a50d0b18d25931063314ecec284988c5a18fab68d08a317d725bb3066"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-IP-COPYRIGHT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-IP-COPYRIGHT.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-IP-COPYRIGHT.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"d4349677f68346f79709ea23a05957262f6426f976388bba45f05c617035eb78"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-IP-OTHER","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-IP-OTHER.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-IP-OTHER.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fc3e2772252a3a90571663ebcfbc8e18dbf526977f4a3d77c4705ef8aa8dd6a8"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-MEDIA","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-MEDIA.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-MEDIA.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"e0d5d287befd1578a06c64a265d5c4426af5772b6764018f6a15f00aac3b498c"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-TRANSPORT_MARITIME","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-TRANSPORT_MARITIME.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-TRANSPORT_MARITIME.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"6e87a77c25f8c358f1d7d35ca165b1229c2cb422a440c91768ad80e520d35456"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-CONSUMER_CONTRACT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-CONSUMER_CONTRACT.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-CONSUMER_CONTRACT.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"afd8bbba102614d8d08f2e7a5384f6ca24e1309c1f52feb13906613f2ced51b7"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-MORTGAGE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-MORTGAGE.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-MORTGAGE.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"7e584f7b4e4d19132e9575071f87d05965fb77fa2ca870911fe9974a7c511c4d"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-MORTGAGE-CREATION","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-MORTGAGE-CREATION.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-MORTGAGE-CREATION.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"e727690ee23883e011d49eba01cfd87abc5482641c2bda519c0a39518e1769ee"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-ENCUMBERED-TRANSFER","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-ENCUMBERED-TRANSFER.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-ENCUMBERED-TRANSFER.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fa950393a572829ba7e296c20cd08ff19a063b0f8441f950130fd677b1e38179"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-PRESERVED-CLAIM-BUNDLE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-PRESERVED-CLAIM-BUNDLE.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-PRESERVED-CLAIM-BUNDLE.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fbd99f029438c1c278f93d88d18a703db72e78f20c7049f344e5748c108f94c5"},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-DEFENSE-MAP","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-DEFENSE-MAP.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-DEFENSE-MAP.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"05348c493e6046a3e5c55f9b516c0e862fe5aa753e1b2fc71624bb2b11f0c191"},{"asset_version":"s2_00.build.1.2","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/shared/deployment.schema.v2.json"],"dependency_module_ids":["SCHEMA-DEPLOYMENT"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":["AUTHORING-AGENT-YAML"],"module_id":"BUILD-S2_00-INLINE-PROJECTION","module_kind":"BUILD_TOOL","outputs":["DEPLOYMENT-AGENT-PROJECTION","FULL-CODE-MIRRORS","INLINE-CODE-RECEIPT"],"owner":"Stage_2_release_build_owner","path":"offline_build/build_s2_00_inline_projection.py","produced_schema_ids":["stage2_s2_00_inline_code_receipt.v2"],"schema_version":"offline_projection_builder.v1","scope_predicate":"build_target == S2_00_INLINE_AGENT","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"5da9e66f3c8d2bff7a2d99d17f9e481a249f3e2587a2547be094c8dab8584928"},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/shared/deployment.schema.v2.json"],"dependency_module_ids":["BUILD-S2_00-INLINE-PROJECTION","SCHEMA-DEPLOYMENT","WF-S2_00"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["AUTHORING-AGENT-YAML","DEPLOYMENT-AGENT-PROJECTION","FULL-CODE-MIRRORS"],"module_id":"TEST-S2_00-INLINE-CODE-PARITY","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_inline_code_parity.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == inline_code_parity","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"00b592fa94a9b7ea393f3a3504530f0602c4efc9bd54c84a272fc9ba3ca3a3b4"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"WF-S2_10","module_kind":"WORKFLOW","outputs":["WF-S2_10"],"owner":"Stage_2_S2_10_owner","path":"workflows/S2_10_domain_relief_resolution_map.yml","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f0fba48f1760cf4e233d7d698fd19090f96ffb89cabe4b2e2cae9af7c616be0f"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["WF-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"AGENT-S2_10-HYBRID","module_kind":"LLM_AGENT","outputs":["AGENT-S2_10-HYBRID"],"owner":"Stage_2_S2_10_owner","path":"agent_scripts/Stage_2_S2_10.yml","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"SCHEMA-S2_10","module_kind":"JSON_SCHEMA","outputs":["SCHEMA-S2_10"],"owner":"Stage_2_S2_10_owner","path":"schemas/s2_10.schema.json","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["AGENT-S2_10-HYBRID","SCHEMA-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"BINDING-S2_10-HYBRID","module_kind":"DEPLOYMENT_BINDING","outputs":["BINDING-S2_10-HYBRID"],"owner":"Stage_2_S2_10_owner","path":"deployment/stage2_s2_10_llm_binding.yml","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["AGENT-S2_10-HYBRID","P00","P10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"PROMPT-PROJECTION-S2_10","module_kind":"BUILD_RECEIPT","outputs":["PROMPT-PROJECTION-S2_10"],"owner":"Stage_2_S2_10_owner","path":"manifest/s2_10_inline_prompt_projection_receipt.json","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"c0e388e56f52ee4fef2b825307ef41b204a80dd740cbc8f1562b30c3cd1d3696"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"offline_build/build_s2_10_agent_projection.txt","mirror_sha256":"f89acf6ddb307515006ed904865c05df1d38051bacb5f13e9f928142f8d72020","module_id":"BUILD-S2_10-HYBRID","module_kind":"BUILD_ONLY","outputs":["BUILD-S2_10-HYBRID"],"owner":"Stage_2_S2_10_owner","path":"offline_build/build_s2_10_agent_projection.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f89acf6ddb307515006ed904865c05df1d38051bacb5f13e9f928142f8d72020"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["SCHEMA-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"offline_build/validate_s2_10_contract.txt","mirror_sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b","module_id":"VALIDATOR-S2_10","module_kind":"BUILD_ONLY","outputs":["VALIDATOR-S2_10"],"owner":"Stage_2_S2_10_owner","path":"offline_build/validate_s2_10_contract.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_agent_contract.txt","mirror_sha256":"016e5bf62398ec211b68f0b3aa987dfcfa5f8b8e20587dd8aae15bbf2bfba3e3","module_id":"TEST-S2_10-AGENT","module_kind":"TEST","outputs":["TEST-S2_10-AGENT"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_agent_contract.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"016e5bf62398ec211b68f0b3aa987dfcfa5f8b8e20587dd8aae15bbf2bfba3e3"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["SCHEMA-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_wave_dispatch.txt","mirror_sha256":"f7e59189ed93585c16c55943f7cc3ed464091fb23a9ea086e6e209cf14677a48","module_id":"TEST-S2_10-DISPATCH","module_kind":"TEST","outputs":["TEST-S2_10-DISPATCH"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_wave_dispatch.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f7e59189ed93585c16c55943f7cc3ed464091fb23a9ea086e6e209cf14677a48"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["SCHEMA-S2_10","VALIDATOR-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_domain_verdict_contract.txt","mirror_sha256":"9f2547a8c590b7df2aa09d0c3808766bf16be321a448742020d9a3476e7d837f","module_id":"TEST-S2_10-DOMAIN","module_kind":"TEST","outputs":["TEST-S2_10-DOMAIN"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_domain_verdict_contract.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"9f2547a8c590b7df2aa09d0c3808766bf16be321a448742020d9a3476e7d837f"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["PROMPT-PROJECTION-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_prompt_cache_and_boundaries.txt","mirror_sha256":"87d8657219070e6bc71e9352d6e84b94f957d490daecb62cc73826a9e021e786","module_id":"TEST-S2_10-PROMPT","module_kind":"TEST","outputs":["TEST-S2_10-PROMPT"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_prompt_cache_and_boundaries.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"87d8657219070e6bc71e9352d6e84b94f957d490daecb62cc73826a9e021e786"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["BINDING-S2_10-HYBRID"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_release_adapter_retry.txt","mirror_sha256":"16b7b1419550b553bbbd42dbcba7e8418124ba5c41adfcae59639d3db9347c0b","module_id":"TEST-S2_10-RELEASE","module_kind":"TEST","outputs":["TEST-S2_10-RELEASE"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_release_adapter_retry.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"16b7b1419550b553bbbd42dbcba7e8418124ba5c41adfcae59639d3db9347c0b"},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"REGRESSION-MANIFEST-S2_10","module_kind":"TEST_MANIFEST","outputs":["REGRESSION-MANIFEST-S2_10"],"owner":"Stage_2_S2_10_owner","path":"tests/fixtures/s2_10/regression_manifest.json","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"804844930d6ff4bff536748c8da23ab3ca2a27303f052614562cd910d885a64e"}],"producer_aliases":[{"alias_id":"PA-SG-COMPILER-001","bidirectional_match_allowed":true,"contract_status":"CLOSED_DECISION_FIXTURE_PENDING","global_alias_allowed":false,"orchestration_producer_id":"Task_C_BO_S0_signal_bundle_writer","schema_writer_id":"Task_C_BO_S0_canonical_signal_compiler","scope":"STAGE1_PART2_SIGNAL_TRANSACTION_ONLY"}],"schema_version":"stage2_module_manifest.v1"} +{"closure_summary":{"build_tool_module_count":1,"cycle_breaking_contract":"AGENT_CODE_RECEIPT_AND_EXECUTOR_BINDING_EXTERNALLY_BIND_RELEASE_RAW_HASH","documentation_mirror_count":39,"executable_agent_hash_included":false,"executor_binding_hash_included":false,"fixture_closure_ref":"tests/fixtures/regression_manifest.json","fixture_descriptor_count":60,"inline_runtime_code_hash_included":false,"module_count":166,"profile_module_count":45,"prompt_module_count":2,"runtime_code_hash_included":false,"s2_20_generic_module_count":90,"self_hash_included":false,"stage2_release_hash_included":false,"test_module_count":29},"documentation_mirrors":[{"byte_identical":true,"mirror_path":"offline_build/build_authority_law_value_release.txt","mirror_sha256":"8c1d9b2f05fa752d50349ed1a4479aafcb17c2f574fa1ebe6b19d2bedc21ffeb","mirror_size_bytes":7213,"runtime_import_allowed":false,"source_path":"offline_build/build_authority_law_value_release.py","source_sha256":"8c1d9b2f05fa752d50349ed1a4479aafcb17c2f574fa1ebe6b19d2bedc21ffeb","source_size_bytes":7213},{"byte_identical":true,"mirror_path":"offline_build/build_corpus_snapshot.txt","mirror_sha256":"96feead66f24d375b9ac379f459de040a9ae1f368e9c05d2911c5b0da54c02c2","mirror_size_bytes":9781,"runtime_import_allowed":false,"source_path":"offline_build/build_corpus_snapshot.py","source_sha256":"96feead66f24d375b9ac379f459de040a9ae1f368e9c05d2911c5b0da54c02c2","source_size_bytes":9781},{"byte_identical":true,"mirror_path":"offline_build/build_release_manifests.txt","mirror_sha256":"fedda0f09d598b19fee83f5ef515a05c162bc09a2a72b7337bb2529d222eedf3","mirror_size_bytes":18956,"runtime_import_allowed":false,"source_path":"offline_build/build_release_manifests.py","source_sha256":"fedda0f09d598b19fee83f5ef515a05c162bc09a2a72b7337bb2529d222eedf3","source_size_bytes":18956},{"byte_identical":true,"mirror_path":"offline_build/build_s2_00_inline_projection.txt","mirror_sha256":"31c96b6abe2eb0ed1f9c9e571e1f4f326307225dfddf40a6670e2d3073ed2ab4","mirror_size_bytes":35198,"runtime_import_allowed":false,"source_path":"offline_build/build_s2_00_inline_projection.py","source_sha256":"31c96b6abe2eb0ed1f9c9e571e1f4f326307225dfddf40a6670e2d3073ed2ab4","source_size_bytes":35198},{"byte_identical":true,"mirror_path":"offline_build/build_s2_10_agent_projection.txt","mirror_sha256":"4359364816c8a58eb414bafae8ccf24fc3766dc611d15f1aa09e48c8fedc8f88","mirror_size_bytes":61930,"runtime_import_allowed":false,"source_path":"offline_build/build_s2_10_agent_projection.py","source_sha256":"4359364816c8a58eb414bafae8ccf24fc3766dc611d15f1aa09e48c8fedc8f88","source_size_bytes":61930},{"byte_identical":true,"mirror_path":"offline_build/build_s2_20_inline_projection.txt","mirror_sha256":"b5c035251621920dd848d672dc6cf840500ce2b60e1e6656f021855681f6fc58","mirror_size_bytes":27865,"runtime_import_allowed":false,"source_path":"offline_build/build_s2_20_inline_projection.py","source_sha256":"b5c035251621920dd848d672dc6cf840500ce2b60e1e6656f021855681f6fc58","source_size_bytes":27865},{"byte_identical":true,"mirror_path":"offline_build/compile_case_type_registry.txt","mirror_sha256":"e415b5179e53f5a7e35e5e1b7749706c783fbf296bf0bf40103e9450df0b2f0c","mirror_size_bytes":9658,"runtime_import_allowed":false,"source_path":"offline_build/compile_case_type_registry.py","source_sha256":"e415b5179e53f5a7e35e5e1b7749706c783fbf296bf0bf40103e9450df0b2f0c","source_size_bytes":9658},{"byte_identical":true,"mirror_path":"offline_build/compile_relief_rule_projection.txt","mirror_sha256":"8412f2ad7b8de4738fbb86a7596def90dbc9d34d4e2f24854e964df476589c2f","mirror_size_bytes":42682,"runtime_import_allowed":false,"source_path":"offline_build/compile_relief_rule_projection.py","source_sha256":"8412f2ad7b8de4738fbb86a7596def90dbc9d34d4e2f24854e964df476589c2f","source_size_bytes":42682},{"byte_identical":true,"mirror_path":"offline_build/validate_s2_10_contract.txt","mirror_sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b","mirror_size_bytes":63811,"runtime_import_allowed":false,"source_path":"offline_build/validate_s2_10_contract.py","source_sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b","source_size_bytes":63811},{"byte_identical":true,"mirror_path":"release_ops/canary_rollback.txt","mirror_sha256":"327356b4f59bb850fcfb680d5caf13038025e3042689f11e1018731058d15241","mirror_size_bytes":1345,"runtime_import_allowed":false,"source_path":"release_ops/canary_rollback.py","source_sha256":"327356b4f59bb850fcfb680d5caf13038025e3042689f11e1018731058d15241","source_size_bytes":1345},{"byte_identical":true,"mirror_path":"release_ops/release_validator.txt","mirror_sha256":"2cb2e287123bf2f58f6b869d2076f67e59b1cb3b830029e11d20e4bc30b3835a","mirror_size_bytes":27546,"runtime_import_allowed":false,"source_path":"release_ops/release_validator.py","source_sha256":"2cb2e287123bf2f58f6b869d2076f67e59b1cb3b830029e11d20e4bc30b3835a","source_size_bytes":27546},{"byte_identical":true,"mirror_path":"runtime/authority/authority_preflight.txt","mirror_sha256":"ffa513bdc7ddb1de895b4e813de9e107c94c36a5955166c7d5cee745584d0dda","mirror_size_bytes":2115,"runtime_import_allowed":false,"source_path":"runtime/authority/authority_preflight.py","source_sha256":"ffa513bdc7ddb1de895b4e813de9e107c94c36a5955166c7d5cee745584d0dda","source_size_bytes":2115},{"byte_identical":true,"mirror_path":"runtime/binding_signature_projection.txt","mirror_sha256":"0ea048355b4abf8cf261bc60735a557ab09d84e325aa42bd4d7acb58f589c6b5","mirror_size_bytes":5843,"runtime_import_allowed":false,"source_path":"runtime/binding_signature_projection.py","source_sha256":"0ea048355b4abf8cf261bc60735a557ab09d84e325aa42bd4d7acb58f589c6b5","source_size_bytes":5843},{"byte_identical":true,"mirror_path":"runtime/c25_case_type_bind.txt","mirror_sha256":"7cda9812225379ef92c34b8ca383aa35bdcbac6afc30b3a21245f5e3ad6aa3af","mirror_size_bytes":3471,"runtime_import_allowed":false,"source_path":"runtime/c25_case_type_bind.py","source_sha256":"7cda9812225379ef92c34b8ca383aa35bdcbac6afc30b3a21245f5e3ad6aa3af","source_size_bytes":3471},{"byte_identical":true,"mirror_path":"runtime/c26_rule_branch_bind.txt","mirror_sha256":"f6d6fb87b005b0a155e24ae93c7550640bfdaa221063af034c3d3e20b46cc6b6","mirror_size_bytes":5089,"runtime_import_allowed":false,"source_path":"runtime/c26_rule_branch_bind.py","source_sha256":"f6d6fb87b005b0a155e24ae93c7550640bfdaa221063af034c3d3e20b46cc6b6","source_size_bytes":5089},{"byte_identical":true,"mirror_path":"runtime/c27_query_plan.txt","mirror_sha256":"d7fe404438e185b2e0351b8b5ac8fc7bd911a63544e71c7c16ffb313dc566777","mirror_size_bytes":6553,"runtime_import_allowed":false,"source_path":"runtime/c27_query_plan.py","source_sha256":"d7fe404438e185b2e0351b8b5ac8fc7bd911a63544e71c7c16ffb313dc566777","source_size_bytes":6553},{"byte_identical":true,"mirror_path":"runtime/c28_weaviate_retrieve.txt","mirror_sha256":"9634c80dd1b77ab652185ef5bd2b67ccbb7d355d2b4661f67d86fa78d0ee6395","mirror_size_bytes":12083,"runtime_import_allowed":false,"source_path":"runtime/c28_weaviate_retrieve.py","source_sha256":"9634c80dd1b77ab652185ef5bd2b67ccbb7d355d2b4661f67d86fa78d0ee6395","source_size_bytes":12083},{"byte_identical":true,"mirror_path":"runtime/c29_pack_verify.txt","mirror_sha256":"4318f95c9c38391a455613b7411cee923288d19748f3c0946e86c588be718b9d","mirror_size_bytes":5721,"runtime_import_allowed":false,"source_path":"runtime/c29_pack_verify.py","source_sha256":"4318f95c9c38391a455613b7411cee923288d19748f3c0946e86c588be718b9d","source_size_bytes":5721},{"byte_identical":true,"mirror_path":"runtime/calculators/registry_engine.txt","mirror_sha256":"173da5185caa6d4a82a7735018c9121ef42481cad8d83b9574c9f432912c556d","mirror_size_bytes":11417,"runtime_import_allowed":false,"source_path":"runtime/calculators/registry_engine.py","source_sha256":"173da5185caa6d4a82a7735018c9121ef42481cad8d83b9574c9f432912c556d","source_size_bytes":11417},{"byte_identical":true,"mirror_path":"tests/s2_00/test_canonical_ids.txt","mirror_sha256":"c463506a90859daeef7eafddc4a6c08c563e2ae73200d921c1f00efb7bc24361","mirror_size_bytes":2275,"runtime_import_allowed":false,"source_path":"tests/s2_00/test_canonical_ids.py","source_sha256":"c463506a90859daeef7eafddc4a6c08c563e2ae73200d921c1f00efb7bc24361","source_size_bytes":2275},{"byte_identical":true,"mirror_path":"tests/s2_00/test_cluster_bundle_compile.txt","mirror_sha256":"aa31cc87246a5c8f6b82e98b9f86dd654bc9306dfc7849af19ec16cfd5137a2a","mirror_size_bytes":25557,"runtime_import_allowed":false,"source_path":"tests/s2_00/test_cluster_bundle_compile.py","source_sha256":"aa31cc87246a5c8f6b82e98b9f86dd654bc9306dfc7849af19ec16cfd5137a2a","source_size_bytes":25557},{"byte_identical":true,"mirror_path":"tests/s2_00/test_conservation.txt","mirror_sha256":"c906dff7e11678a60556818d1baf24e66844d9c982c9d333ec339d2ed9ca6061","mirror_size_bytes":5562,"runtime_import_allowed":false,"source_path":"tests/s2_00/test_conservation.py","source_sha256":"c906dff7e11678a60556818d1baf24e66844d9c982c9d333ec339d2ed9ca6061","source_size_bytes":5562},{"byte_identical":true,"mirror_path":"tests/s2_00/test_failure_and_atomic_publish.txt","mirror_sha256":"128a82b4e413132c0ff4a61ae2dc7f85b6c1c050ea33ccf4e53f23ccc993aba6","mirror_size_bytes":25922,"runtime_import_allowed":false,"source_path":"tests/s2_00/test_failure_and_atomic_publish.py","source_sha256":"128a82b4e413132c0ff4a61ae2dc7f85b6c1c050ea33ccf4e53f23ccc993aba6","source_size_bytes":25922},{"byte_identical":true,"mirror_path":"tests/s2_00/test_inline_code_parity.txt","mirror_sha256":"8f0f55e8e1b970de572129946ab8bf34967c7fd84e316a9193509cab07a455ed","mirror_size_bytes":15928,"runtime_import_allowed":false,"source_path":"tests/s2_00/test_inline_code_parity.py","source_sha256":"8f0f55e8e1b970de572129946ab8bf34967c7fd84e316a9193509cab07a455ed","source_size_bytes":15928},{"byte_identical":true,"mirror_path":"tests/s2_00/test_resolver_source_lock.txt","mirror_sha256":"51bc1b310d40f5068df0a66446e5b96b00ea9b5b0137ddaf563caadf9f8fc51e","mirror_size_bytes":14423,"runtime_import_allowed":false,"source_path":"tests/s2_00/test_resolver_source_lock.py","source_sha256":"51bc1b310d40f5068df0a66446e5b96b00ea9b5b0137ddaf563caadf9f8fc51e","source_size_bytes":14423},{"byte_identical":true,"mirror_path":"tests/s2_00/test_schema_hash_and_signals.txt","mirror_sha256":"fc19c57dce1545590a52cc863d25a0175d0b2df30fbe499d6f57e7d30b8b36d8","mirror_size_bytes":12529,"runtime_import_allowed":false,"source_path":"tests/s2_00/test_schema_hash_and_signals.py","source_sha256":"fc19c57dce1545590a52cc863d25a0175d0b2df30fbe499d6f57e7d30b8b36d8","source_size_bytes":12529},{"byte_identical":true,"mirror_path":"tests/s2_10/test_agent_contract.txt","mirror_sha256":"016e5bf62398ec211b68f0b3aa987dfcfa5f8b8e20587dd8aae15bbf2bfba3e3","mirror_size_bytes":7717,"runtime_import_allowed":false,"source_path":"tests/s2_10/test_agent_contract.py","source_sha256":"016e5bf62398ec211b68f0b3aa987dfcfa5f8b8e20587dd8aae15bbf2bfba3e3","source_size_bytes":7717},{"byte_identical":true,"mirror_path":"tests/s2_10/test_domain_verdict_contract.txt","mirror_sha256":"9f2547a8c590b7df2aa09d0c3808766bf16be321a448742020d9a3476e7d837f","mirror_size_bytes":10242,"runtime_import_allowed":false,"source_path":"tests/s2_10/test_domain_verdict_contract.py","source_sha256":"9f2547a8c590b7df2aa09d0c3808766bf16be321a448742020d9a3476e7d837f","source_size_bytes":10242},{"byte_identical":true,"mirror_path":"tests/s2_10/test_prompt_cache_and_boundaries.txt","mirror_sha256":"87d8657219070e6bc71e9352d6e84b94f957d490daecb62cc73826a9e021e786","mirror_size_bytes":10798,"runtime_import_allowed":false,"source_path":"tests/s2_10/test_prompt_cache_and_boundaries.py","source_sha256":"87d8657219070e6bc71e9352d6e84b94f957d490daecb62cc73826a9e021e786","source_size_bytes":10798},{"byte_identical":true,"mirror_path":"tests/s2_10/test_release_adapter_retry.txt","mirror_sha256":"16b7b1419550b553bbbd42dbcba7e8418124ba5c41adfcae59639d3db9347c0b","mirror_size_bytes":7757,"runtime_import_allowed":false,"source_path":"tests/s2_10/test_release_adapter_retry.py","source_sha256":"16b7b1419550b553bbbd42dbcba7e8418124ba5c41adfcae59639d3db9347c0b","source_size_bytes":7757},{"byte_identical":true,"mirror_path":"tests/s2_10/test_wave_dispatch.txt","mirror_sha256":"f7e59189ed93585c16c55943f7cc3ed464091fb23a9ea086e6e209cf14677a48","mirror_size_bytes":9894,"runtime_import_allowed":false,"source_path":"tests/s2_10/test_wave_dispatch.py","source_sha256":"f7e59189ed93585c16c55943f7cc3ed464091fb23a9ea086e6e209cf14677a48","source_size_bytes":9894},{"byte_identical":true,"mirror_path":"tests/s2_20/test_agent_and_inline_parity.txt","mirror_sha256":"0f73c112b28088be191b6717cb24cac468762cc286f8dc806bf65d98a95bf262","mirror_size_bytes":2773,"runtime_import_allowed":false,"source_path":"tests/s2_20/test_agent_and_inline_parity.py","source_sha256":"0f73c112b28088be191b6717cb24cac468762cc286f8dc806bf65d98a95bf262","source_size_bytes":2773},{"byte_identical":true,"mirror_path":"tests/s2_20/test_calculation_and_reports.txt","mirror_sha256":"c1bd36c351173d25e6d8e68d1a90d738b90aabb51b201ca13f4370654e584e00","mirror_size_bytes":3199,"runtime_import_allowed":false,"source_path":"tests/s2_20/test_calculation_and_reports.py","source_sha256":"c1bd36c351173d25e6d8e68d1a90d738b90aabb51b201ca13f4370654e584e00","source_size_bytes":3199},{"byte_identical":true,"mirror_path":"tests/s2_20/test_inline_output_schema.txt","mirror_sha256":"e2cb938dd639874a4b150997d66eacd89b22016c8fd3385a243edf781cd3e85b","mirror_size_bytes":43574,"runtime_import_allowed":false,"source_path":"tests/s2_20/test_inline_output_schema.py","source_sha256":"e2cb938dd639874a4b150997d66eacd89b22016c8fd3385a243edf781cd3e85b","source_size_bytes":43574},{"byte_identical":true,"mirror_path":"tests/s2_20/test_offline_compilers.txt","mirror_sha256":"5e75f659a9e5add1345f1596c92c0b143cb1e48110c746a8785fb5b38f1371af","mirror_size_bytes":20252,"runtime_import_allowed":false,"source_path":"tests/s2_20/test_offline_compilers.py","source_sha256":"5e75f659a9e5add1345f1596c92c0b143cb1e48110c746a8785fb5b38f1371af","source_size_bytes":20252},{"byte_identical":true,"mirror_path":"tests/s2_20/test_option_group_and_binding.txt","mirror_sha256":"6021d93fb47377cf6c00ccb5a880a7e85987ec5a1ed7976a84baca2d7fb39985","mirror_size_bytes":6171,"runtime_import_allowed":false,"source_path":"tests/s2_20/test_option_group_and_binding.py","source_sha256":"6021d93fb47377cf6c00ccb5a880a7e85987ec5a1ed7976a84baca2d7fb39985","source_size_bytes":6171},{"byte_identical":true,"mirror_path":"tests/s2_20/test_plan_publish_and_release.txt","mirror_sha256":"6373571eb1123389bf5d585507a1f694d75ecfa9eb572cff2a0c5ad1d21751aa","mirror_size_bytes":12949,"runtime_import_allowed":false,"source_path":"tests/s2_20/test_plan_publish_and_release.py","source_sha256":"6373571eb1123389bf5d585507a1f694d75ecfa9eb572cff2a0c5ad1d21751aa","source_size_bytes":12949},{"byte_identical":true,"mirror_path":"tests/s2_20/test_regression_manifest_closure.txt","mirror_sha256":"886ae584a7846acdb5499d52e1ae8ec177e8d1dc34aa1d43e99dbebafa255516","mirror_size_bytes":8493,"runtime_import_allowed":false,"source_path":"tests/s2_20/test_regression_manifest_closure.py","source_sha256":"886ae584a7846acdb5499d52e1ae8ec177e8d1dc34aa1d43e99dbebafa255516","source_size_bytes":8493},{"byte_identical":true,"mirror_path":"tests/s2_20/test_retrieval_and_pack.txt","mirror_sha256":"c314cf27511b0fe065b8961196084a28b1c7c5cb2fed3dd5c8765172ba40462f","mirror_size_bytes":6654,"runtime_import_allowed":false,"source_path":"tests/s2_20/test_retrieval_and_pack.py","source_sha256":"c314cf27511b0fe065b8961196084a28b1c7c5cb2fed3dd5c8765172ba40462f","source_size_bytes":6654}],"forbidden_contract_registry":[{"code":"LEGACY-STAGE2-V0-V3","legacy_runtime_dependency_allowed":false,"meaning":"The clean S2_00 workflow and runtime must not read or execute legacy Stage 2 v.0-v.3 specifications."},{"code":"LEGACY-STAGE2-LOADER","legacy_runtime_dependency_allowed":false,"meaning":"The historical host loader and loader binding are reference-only and cannot authorize, execute, or provide fallback for S2_00."},{"code":"RAW-STAGE1-REREAD","legacy_runtime_dependency_allowed":false,"meaning":"S2_10 must consume immutable bounded cluster projections and must not rediscover raw Stage 1 inputs."},{"code":"S2_40-PLEADING-RENDER-IN-STATUS-ONLY","legacy_runtime_dependency_allowed":false,"meaning":"The S2_40 status-only entrypoint must not render or commit pleading text."}],"implementation_status":"OFFLINE_HYBRID_RESEAL_COMPLETE_LIVE_ADMISSION_PENDING","manifest_digest":"27b8b3203965f70f1b139360e5d8d0e6c7872a3246f5974ad0d9f6b605225407","manifest_digest_contract":{"algorithm_id":"STAGE2-MODULE-MANIFEST-DIGEST-V1","array_order":"PRESERVE_DECLARED_ORDER","canonicalization_algorithm_id":"STAGE2-CANONICAL-JSON-V1","digest_algorithm":"sha256","digest_scope":"ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_MANIFEST_DIGEST","encoding":"UTF-8","line_termination":"LF_ONE_TRAILING_NEWLINE","non_finite_numbers_allowed":false,"object_key_order":"SORT_CODEPOINT","verification_status":"DEV_VERIFIED"},"manifest_id":"STAGE2-CLEAN-S2_00-STRUCTURED-HANDOFF-V2-INLINE","manifest_scope":"S2_00_STRUCTURED_CONTEXT_HANDOFF_AND_SHARED_CONTRACTS","manifest_status":"DEV_HASH_BOUND_DOWNSTREAM_HYBRID_RELEASE_PENDING","modules":[{"asset_version":"s2_00.1.2","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json","https://schemas.liti-agent.local/stage2/s2_00/context.schema.v2.json","https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json"],"dependency_module_ids":["SCHEMA-INGRESS","SCHEMA-CONTEXT","SCHEMA-REVIEW-STATUS"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"IMPLEMENTED_DECLARATIVE_CONTRACT","incompatible_module_ids":[],"inputs":["STAGE1-ALLOWLIST-V1","STAGE1-DEPLOYMENT-CLOSURE-2026-08-29"],"module_id":"WF-S2_00","module_kind":"WORKFLOW","outputs":["S2_00-NORMAL-BRANCH","S2_00-DIAGNOSTIC-BRANCH"],"owner":"Stage_2_workflow_maintainer","path":"workflows/S2_00_stage1_ingress_normalize_and_bundle_compile.yml","produced_schema_ids":[],"schema_version":"stage2_workflow_contract.v1.2","scope_predicate":"workflow_id == S2_00","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"3d7ceb86b236668c8c372136f1d1b43a5d0a79d3fb05fd1ef4342bb638190f68","size_bytes":17351},{"asset_version":"s2_00.schema.1.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"SCHEMA-INGRESS","module_kind":"JSON_SCHEMA","outputs":["INGRESS-SCHEMA-DEFS"],"owner":"Stage_2_schema_owner","path":"schemas/ingress.schema.json","produced_schema_ids":["https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json"],"schema_version":"stage2_s2_00_ingress.v1.1","scope_predicate":"artifact_family == ingress","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"8fd7b76015b7d7dfbbe056e08999dda7ea8e1012bb16cec50634b08f7ea97302","size_bytes":33444},{"asset_version":"s2_00.schema.2","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json"],"dependency_module_ids":["SCHEMA-REVIEW-STATUS"],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"SCHEMA-CONTEXT","module_kind":"JSON_SCHEMA","outputs":["CONTEXT-SCHEMA-DEFS"],"owner":"Stage_2_schema_owner","path":"schemas/context.schema.json","produced_schema_ids":["https://schemas.liti-agent.local/stage2/s2_00/context.schema.v2.json"],"schema_version":"stage2_s2_00_context.v2","scope_predicate":"artifact_family == context","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"29073ffa847d74304228d61306503c0c3798da7db8b5717b1ad6d5b2da98aa92","size_bytes":39054},{"asset_version":"stage2.shared.2","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"SCHEMA-DEPLOYMENT","module_kind":"JSON_SCHEMA","outputs":["DEPLOYMENT-SCHEMA-DEFS"],"owner":"Stage_2_deployment_schema_owner","path":"schemas/deployment.schema.json","produced_schema_ids":["https://schemas.liti-agent.local/stage2/shared/deployment.schema.v2.json"],"schema_version":"stage2_deployment.v2","scope_predicate":"artifact_family == deployment","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"a328ae9ab11f2fe033be96420ec1386cc3ae2d980500c40a4fd5cc691b59b15e","size_bytes":66724},{"asset_version":"stage2.shared.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"SCHEMA-REVIEW-STATUS","module_kind":"JSON_SCHEMA","outputs":["REVIEW-STATUS-SCHEMA-DEFS"],"owner":"Stage_2_review_schema_owner","path":"schemas/review_status.schema.json","produced_schema_ids":["https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json"],"schema_version":"stage2_review_status.v1","scope_predicate":"artifact_family == review_status","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"13f6f1f06946d4074f3d85959f82763e489b2f87cd7bfadb14b594d6223b02e9","size_bytes":11169},{"asset_version":"stage2.cache.2","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["SCHEMA-CONTEXT"],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["RELEASE-SELECTED-CONTEXT-ORDERED-REFS","S2_00-COHORT-MEMBERSHIP","S2_00-MEMBER-SLICE-REF-SET"],"module_id":"CACHE-POLICY-STRUCTURED-CONTEXT-HANDOFF","module_kind":"CACHE_POLICY","outputs":["S2_00-CONTEXT-MATERIALIZATION-RECEIPT","S2_10-CACHE-IDENTITY-INPUT"],"owner":"Stage_2_cache_policy_owner","path":"registry/cache/prompt_cache_policy.yml","produced_schema_ids":[],"schema_version":"stage2_prompt_cache_policy.v2","scope_predicate":"consumer == S2_10","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"b459cc271e2f51d783cdd89872d24dc7635ac237e76cd6ad03253569190b7b90","size_bytes":5887},{"asset_version":"s2_00.test.1.2","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"module_id":"REGRESSION-MANIFEST-S2_00","module_kind":"TEST_MANIFEST","outputs":["FIXTURE-RESULT-DIGEST"],"owner":"Stage_2_regression_owner","path":"tests/fixtures/regression_manifest.json","produced_schema_ids":[],"schema_version":"stage2_s2_00_regression_manifest.v1","scope_predicate":"workflow_id == S2_00","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"7558535b3332057da5dfba18c67ccd28e8f74c11fc9b40541fd39d9f53114f39","size_bytes":53447},{"asset_version":"s2_40.status_only.1","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/s2_00/ingress.schema.v1.json","https://schemas.liti-agent.local/stage2/shared/review_status.schema.v1.json"],"dependency_module_ids":["WF-S2_00","SCHEMA-REVIEW-STATUS"],"forbidden_contract_codes":["S2_40-PLEADING-RENDER-IN-STATUS-ONLY"],"implementation_status":"DRAFT_HANDOFF_STUB_HASH_BOUND","incompatible_module_ids":[],"inputs":["ingress/ingress_status.json","ingress/technical_diagnostic.json","ingress/stage1_input_manifest.json","ingress/intake_report.json","review/issue_ledger.base.json"],"module_id":"WF-S2_40-STATUS-ONLY","module_kind":"WORKFLOW","outputs":["control/run_status.json"],"owner":"Stage_2_S2_40_owner","path":"workflows/S2_40_final_review_render_and_commit.yml","produced_schema_ids":["stage2_status_only_commit.v1"],"schema_version":"stage2_workflow_contract.v1","scope_predicate":"entrypoint_id == S2_40_STATUS_ONLY","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"b7d83b56771d005e6185746c3469d42f7781927522b8a8d42648301eceaadadd","size_bytes":3685},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"mirror_byte_parity":true,"mirror_path":"tests/s2_00/test_resolver_source_lock.txt","mirror_sha256":"51bc1b310d40f5068df0a66446e5b96b00ea9b5b0137ddaf563caadf9f8fc51e","mirror_size_bytes":14423,"module_id":"TEST-S2_00-RESOLVER-SOURCE-LOCK","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_resolver_source_lock.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == resolver_source_lock","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"51bc1b310d40f5068df0a66446e5b96b00ea9b5b0137ddaf563caadf9f8fc51e","size_bytes":14423},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"mirror_byte_parity":true,"mirror_path":"tests/s2_00/test_schema_hash_and_signals.txt","mirror_sha256":"fc19c57dce1545590a52cc863d25a0175d0b2df30fbe499d6f57e7d30b8b36d8","mirror_size_bytes":12529,"module_id":"TEST-S2_00-SCHEMA-HASH-SIGNALS","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_schema_hash_and_signals.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == schema_hash_signals","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"fc19c57dce1545590a52cc863d25a0175d0b2df30fbe499d6f57e7d30b8b36d8","size_bytes":12529},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"mirror_byte_parity":true,"mirror_path":"tests/s2_00/test_conservation.txt","mirror_sha256":"c906dff7e11678a60556818d1baf24e66844d9c982c9d333ec339d2ed9ca6061","mirror_size_bytes":5562,"module_id":"TEST-S2_00-CONSERVATION","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_conservation.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == conservation","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"c906dff7e11678a60556818d1baf24e66844d9c982c9d333ec339d2ed9ca6061","size_bytes":5562},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"mirror_byte_parity":true,"mirror_path":"tests/s2_00/test_canonical_ids.txt","mirror_sha256":"c463506a90859daeef7eafddc4a6c08c563e2ae73200d921c1f00efb7bc24361","mirror_size_bytes":2275,"module_id":"TEST-S2_00-CANONICAL-IDS","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_canonical_ids.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == canonical_ids","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"c463506a90859daeef7eafddc4a6c08c563e2ae73200d921c1f00efb7bc24361","size_bytes":2275},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"mirror_byte_parity":true,"mirror_path":"tests/s2_00/test_cluster_bundle_compile.txt","mirror_sha256":"aa31cc87246a5c8f6b82e98b9f86dd654bc9306dfc7849af19ec16cfd5137a2a","mirror_size_bytes":25557,"module_id":"TEST-S2_00-CLUSTER-BUNDLE","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_cluster_bundle_compile.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == cluster_bundle_compile","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"aa31cc87246a5c8f6b82e98b9f86dd654bc9306dfc7849af19ec16cfd5137a2a","size_bytes":25557},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["FIXTURE-FAMILY"],"mirror_byte_parity":true,"mirror_path":"tests/s2_00/test_failure_and_atomic_publish.txt","mirror_sha256":"128a82b4e413132c0ff4a61ae2dc7f85b6c1c050ea33ccf4e53f23ccc993aba6","mirror_size_bytes":25922,"module_id":"TEST-S2_00-FAILURE-ATOMIC-PUBLISH","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_failure_and_atomic_publish.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == failure_atomic_publish","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"128a82b4e413132c0ff4a61ae2dc7f85b6c1c050ea33ccf4e53f23ccc993aba6","size_bytes":25922},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":[],"module_id":"P00","module_kind":"LLM_PROMPT_CONTRACT","outputs":["STATIC_PREFIX_SEGMENT"],"owner":"Stage_2_prompt_owner","path":"prompts/P00_system_and_safety_contract.md","produced_schema_ids":[],"schema_version":"stage2.prompt_contract.v1","scope_predicate":"static_prefix_segment == P00","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5","size_bytes":7124},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P00"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P00"],"module_id":"P10","module_kind":"LLM_PROMPT_CONTRACT","outputs":["STATIC_PREFIX_SEGMENT"],"owner":"Stage_2_prompt_owner","path":"prompts/P10_legal_resolution_contract.md","produced_schema_ids":[],"schema_version":"stage2.prompt_contract.v1","scope_predicate":"static_prefix_segment == P10","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","size_bytes":8712},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-EC-00_CONTRACT_GENERAL","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/EC-00_contract_general.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/EC-00_contract_general.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"a4f7abd9957b3a9e58d05dd559c3c869bf96fd88ea18968a363c6e606e3c0aa8","size_bytes":3789},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-01_JURISTIC_ACT_VALIDITY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-01_juristic_act_validity.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-01_juristic_act_validity.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"0a0d2676f96be321a7bb974b8e4fb1981ab43773f271c99dc86a70cb17d27c99","size_bytes":3808},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-02_CONTRACT_MONEY_CLAIM","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-02_contract_money_claim.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-02_contract_money_claim.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"08e252bccc718ccc44a258bc540afc2567e509239399f4017550df32bac97824","size_bytes":3784},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-03_PARTIES_LIABILITY_SUCCESSION","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-03_parties_liability_succession.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-03_parties_liability_succession.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"4e841d5f640b06a1b52060b82dda39605369cbe26d81655b7918673437e4519c","size_bytes":3799},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-04_UNJUST_ENRICHMENT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-04_unjust_enrichment.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-04_unjust_enrichment.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"31a5966d4c14940e810f870bf4382f1be642f7716ebaaa35193ef32e9b9d95c7","size_bytes":3725},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-05_TORT_GENERAL","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-05_tort_general.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-05_tort_general.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fe6ea92fc10c4196e0ac83b990bf2606e24024030e7b0002fecdae9f8f50221a","size_bytes":3694},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-06_PROFESSIONAL_LIABILITY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-06_professional_liability.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-06_professional_liability.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"efa7a4f6cda90c04a06558a9d0e1efe13b25169d55d03e36d55d72fcdd096a15","size_bytes":3724},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-07_CONSTRUCTION_DEFECT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-07_construction_defect.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-07_construction_defect.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"78c76cb0485a4acf741109aee05f634f51c3a6904044c613080b21c3978a09f9","size_bytes":3710},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-08_LEASE_DEPOSIT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-08_lease_deposit.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-08_lease_deposit.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"dbe1886671edff49b8b0251eed567d1b3e4e5d748456434262286ca7acf990db","size_bytes":3678},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-09_REGISTRY_TRANSFER_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-09_registry_transfer_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-09_registry_transfer_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"d18946322a8cb528146ac0aa0a2880c41ac2056671fd237b928341ceb8561002","size_bytes":3800},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-10_SECURED_REGISTRY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-10_secured_registry.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-10_secured_registry.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"5819c26ec8478e12a12e340c93a9d23cc410833e1192c2315a1986c9d48a1eba","size_bytes":3757},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-11_POSSESSION_VINDICATION","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-11_possession_vindication.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-11_possession_vindication.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"5e6d6460be4ec0f2ba0096305b9ae39f5f430d107337d0d7e07aac1a9d599aaf","size_bytes":3724},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-12_CO_OWNERSHIP_BOUNDARY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-12_co_ownership_boundary.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-12_co_ownership_boundary.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"62cfbf6a6c5c5edc8b912e348be1c4d4f7dd4c20c722e57e5554058f0caafb4a","size_bytes":3712},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-13_CREDITOR_PRESERVATION","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-13_creditor_preservation.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-13_creditor_preservation.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"c60e6d67c7e3985d1f6ce44027726282f931934e3e66b44f06ccd26845ca2f45","size_bytes":3826},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-14_EXECUTION_LINKED_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-14_execution_linked_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-14_execution_linked_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"45d4b3ab46e1c8834c3b3eec72a28d05e39cb78a9ec2c2de07b5e55e81aeebd7","size_bytes":3787},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-15_SUCCESSION_FAMILY_PROPERTY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-15_succession_family_property.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-15_succession_family_property.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"f691b67295ad8e634b34519cf71156caf6eaf73cef54577615ab79462f8aa766","size_bytes":3763},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-16_NEGOTIABLE_INSTRUMENTS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-16_negotiable_instruments.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-16_negotiable_instruments.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"7720d83b64e7a44720788f6ff3c7cc8ceaf5195377a1e61b52de2144989a1194","size_bytes":3738},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-17_LABOR_WAGE_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-17_labor_wage_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-17_labor_wage_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"168ce5e41104937cf7ab946944f060b82cfe16066425897f07bb9e837b8689ea","size_bytes":3728},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-18_ORG_RESOLUTION_STATUS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-18_org_resolution_status.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-18_org_resolution_status.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"59cf689f886dfaf4ad00dbe6142f5b11c37d0062660f7467aa43427462aa075f","size_bytes":3763},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-19_INSURANCE_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-19_insurance_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-19_insurance_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"0fb4793446c155e7a0b4469c9b8c1be1323112eef55ca4c495307c1af56e754c","size_bytes":3685},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-20_IP_CLAIMS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-20_ip_claims.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-20_ip_claims.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fe07f20d149a2c4deac2e46de299cd78f0bf829f58c615a86c45f542d30804e1","size_bytes":3654},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-21_MEDIA_PERSONALITY_RIGHTS","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"registry/substantive/E-21_media_personality_rights.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == registry/substantive/E-21_media_personality_rights.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"951260bbe8ef116d682f49f3679106c7144ca68f8078f5c10b3459667c1f5b2c","size_bytes":3803},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-E-00_RESIDUAL_UNROUTED","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/E-00_residual_unrouted.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/E-00_residual_unrouted.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"639e507147a6132f9e41ccd0b00d1b5450d6a9629991629b173d5bcaf5b6df39","size_bytes":3752},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-X1_NOTICE_LIFECYCLE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/X1_notice_lifecycle.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/X1_notice_lifecycle.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"9af8b5b5f3a8196a10308d1ee34e0788d9adeac7a99e142e449490d14316faef","size_bytes":3693},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-X2_ASSET_IDENTITY_LINEAGE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/X2_asset_identity_lineage.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/X2_asset_identity_lineage.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"6c1e437944b3a62b60ee7acad5ec9177fefac2991fe970338acc27efcc8a13ad","size_bytes":3742},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-X3_PROCEDURE_STANDING_RELIEF","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/X3_procedure_standing_relief.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/X3_procedure_standing_relief.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"f3b794bef82566e569232ba02c63ae148099143d8d1ab84de1c4fb76105e65df","size_bytes":3788},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-X4_RESPONSE_ADMISSION_DEFENSE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/crosscut/X4_response_admission_defense.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/crosscut/X4_response_admission_defense.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"57851ed55b3351a8139df29224d74b7cb8427708bbf6de00ecd27c44089df32e","size_bytes":3793},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-AUTO_MOTOR_VEHICLE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-AUTO_motor_vehicle.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-AUTO_motor_vehicle.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"7234a7ab890f242e6b7aa811528e34c0c85779007cffbf135da0bfff36d61d91","size_bytes":3762},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-INDUSTRIAL_ACCIDENT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-INDUSTRIAL_ACCIDENT.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-INDUSTRIAL_ACCIDENT.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"10d78b8031df7d89f835afc9ca60fab36648e561c729780204c80885f4dd097f","size_bytes":3750},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-PRODUCT_LIABILITY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-PRODUCT_LIABILITY.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-PRODUCT_LIABILITY.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"1d5ee8eddcf005fdb33df58f591a8948aa3e891a3a5656d919394ff0b39eea53","size_bytes":3737},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-RESIDENTIAL_LEASE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-RESIDENTIAL_LEASE.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-RESIDENTIAL_LEASE.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"3e2efd1b3c0524a305eafedc5ec91e05be989a6e55c1501ed161a9b8f8151fd7","size_bytes":3739},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-COMMERCIAL_LEASE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-COMMERCIAL_LEASE.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-COMMERCIAL_LEASE.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"5aebed9ab56bc00eb7e0511f9fe5db8c3a42f1e81b28d58c38d487aa91cbab3e","size_bytes":3769},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-LABOR","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-LABOR.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-LABOR.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"6b2542bb64f4c1d164b1ccd2ce5099fad29bcea4333d9e34bd0fb1667b9ced4a","size_bytes":3694},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-STATE_LIABILITY","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-STATE_LIABILITY.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-STATE_LIABILITY.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"5b2fe6d57b918384a619efbd04360ddfaf7720d384136f69d359ccb0b6c853d3","size_bytes":3747},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-IP-PATENT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-IP-PATENT.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-IP-PATENT.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"d0651a8a50d0b18d25931063314ecec284988c5a18fab68d08a317d725bb3066","size_bytes":3709},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-IP-COPYRIGHT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-IP-COPYRIGHT.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-IP-COPYRIGHT.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"d4349677f68346f79709ea23a05957262f6426f976388bba45f05c617035eb78","size_bytes":3728},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-IP-OTHER","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-IP-OTHER.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-IP-OTHER.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fc3e2772252a3a90571663ebcfbc8e18dbf526977f4a3d77c4705ef8aa8dd6a8","size_bytes":3746},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-MEDIA","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-MEDIA.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-MEDIA.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"e0d5d287befd1578a06c64a265d5c4426af5772b6764018f6a15f00aac3b498c","size_bytes":3705},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-TRANSPORT_MARITIME","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-TRANSPORT_MARITIME.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-TRANSPORT_MARITIME.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"6e87a77c25f8c358f1d7d35ca165b1229c2cb422a440c91768ad80e520d35456","size_bytes":3835},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-SL-CONSUMER_CONTRACT","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/special_law/SL-CONSUMER_CONTRACT.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/special_law/SL-CONSUMER_CONTRACT.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"afd8bbba102614d8d08f2e7a5384f6ca24e1309c1f52feb13906613f2ced51b7","size_bytes":3797},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-MORTGAGE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-MORTGAGE.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-MORTGAGE.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"7e584f7b4e4d19132e9575071f87d05965fb77fa2ca870911fe9974a7c511c4d","size_bytes":3739},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-MORTGAGE-CREATION","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-MORTGAGE-CREATION.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-MORTGAGE-CREATION.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"e727690ee23883e011d49eba01cfd87abc5482641c2bda519c0a39518e1769ee","size_bytes":3771},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-ENCUMBERED-TRANSFER","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-ENCUMBERED-TRANSFER.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-ENCUMBERED-TRANSFER.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fa950393a572829ba7e296c20cd08ff19a063b0f8441f950130fd677b1e38179","size_bytes":3828},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-PRESERVED-CLAIM-BUNDLE","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-PRESERVED-CLAIM-BUNDLE.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-PRESERVED-CLAIM-BUNDLE.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"fbd99f029438c1c278f93d88d18a703db72e78f20c7049f344e5748c108f94c5","size_bytes":3816},{"asset_version":"stage2.bundle.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["P10"],"implementation_status":"DRAFT_UNVERIFIED_HASH_BOUND","incompatible_module_ids":[],"inputs":["P10","TYPED_STAGE1_CONTEXT"],"module_id":"PROFILE-ACTIO-DEFENSE-MAP","module_kind":"LLM_CONTEXT_PROFILE","outputs":["ACTIVE_PROFILE_CONTEXT"],"owner":"Stage_2_profile_owner","path":"profiles/overlays/ACTIO-DEFENSE-MAP.yml","produced_schema_ids":[],"schema_version":"stage2.llm_context_profile.v1","scope_predicate":"release_selected_profile_path == profiles/overlays/ACTIO-DEFENSE-MAP.yml","semantic_review_status":"PENDING_LEGAL_REVIEW","sha256":"05348c493e6046a3e5c55f9b516c0e862fe5aa753e1b2fc71624bb2b11f0c191","size_bytes":3810},{"asset_version":"s2_00.build.1.2","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/shared/deployment.schema.v2.json"],"dependency_module_ids":["SCHEMA-DEPLOYMENT"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":["AUTHORING-AGENT-YAML"],"mirror_byte_parity":true,"mirror_path":"offline_build/build_s2_00_inline_projection.txt","mirror_sha256":"31c96b6abe2eb0ed1f9c9e571e1f4f326307225dfddf40a6670e2d3073ed2ab4","mirror_size_bytes":35198,"module_id":"BUILD-S2_00-INLINE-PROJECTION","module_kind":"BUILD_TOOL","outputs":["DEPLOYMENT-AGENT-PROJECTION","FULL-CODE-MIRRORS","INLINE-CODE-RECEIPT"],"owner":"Stage_2_release_build_owner","path":"offline_build/build_s2_00_inline_projection.py","produced_schema_ids":["stage2_s2_00_inline_code_receipt.v2"],"schema_version":"offline_projection_builder.v1","scope_predicate":"build_target == S2_00_INLINE_AGENT","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"31c96b6abe2eb0ed1f9c9e571e1f4f326307225dfddf40a6670e2d3073ed2ab4","size_bytes":35198},{"asset_version":"s2_00.test.1","authority_ids":[],"consumed_schema_ids":["https://schemas.liti-agent.local/stage2/shared/deployment.schema.v2.json"],"dependency_module_ids":["BUILD-S2_00-INLINE-PROJECTION","SCHEMA-DEPLOYMENT","WF-S2_00"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND","incompatible_module_ids":[],"inputs":["AUTHORING-AGENT-YAML","DEPLOYMENT-AGENT-PROJECTION","FULL-CODE-MIRRORS"],"mirror_byte_parity":true,"mirror_path":"tests/s2_00/test_inline_code_parity.txt","mirror_sha256":"8f0f55e8e1b970de572129946ab8bf34967c7fd84e316a9193509cab07a455ed","mirror_size_bytes":15928,"module_id":"TEST-S2_00-INLINE-CODE-PARITY","module_kind":"TEST","outputs":["TEST-RECEIPT"],"owner":"Stage_2_test_owner","path":"tests/s2_00/test_inline_code_parity.py","produced_schema_ids":[],"schema_version":"pytest.v1","scope_predicate":"test_axis == inline_code_parity","semantic_review_status":"PENDING_CROSS_AUDIT","sha256":"8f0f55e8e1b970de572129946ab8bf34967c7fd84e316a9193509cab07a455ed","size_bytes":15928},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"WF-S2_10","module_kind":"WORKFLOW","outputs":["WF-S2_10"],"owner":"Stage_2_S2_10_owner","path":"workflows/S2_10_domain_relief_resolution_map.yml","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f0fba48f1760cf4e233d7d698fd19090f96ffb89cabe4b2e2cae9af7c616be0f","size_bytes":15320},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["WF-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"AGENT-S2_10-HYBRID","module_kind":"LLM_AGENT","outputs":["AGENT-S2_10-HYBRID"],"owner":"Stage_2_S2_10_owner","path":"agent_scripts/Stage_2_S2_10.yml","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"SCHEMA-S2_10","module_kind":"JSON_SCHEMA","outputs":["SCHEMA-S2_10"],"owner":"Stage_2_S2_10_owner","path":"schemas/s2_10.schema.json","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee","size_bytes":82703},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["AGENT-S2_10-HYBRID","SCHEMA-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"BINDING-S2_10-HYBRID","module_kind":"DEPLOYMENT_BINDING","outputs":["BINDING-S2_10-HYBRID"],"owner":"Stage_2_S2_10_owner","path":"deployment/stage2_s2_10_llm_binding.yml","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95","size_bytes":7287},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["AGENT-S2_10-HYBRID","P00","P10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"PROMPT-PROJECTION-S2_10","module_kind":"BUILD_RECEIPT","outputs":["PROMPT-PROJECTION-S2_10"],"owner":"Stage_2_S2_10_owner","path":"manifest/s2_10_inline_prompt_projection_receipt.json","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"4c95f3b3884900d1701f955e109f8927b9c5d818aa5c97623dba567faf7f61e9","size_bytes":2659},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"offline_build/build_s2_10_agent_projection.txt","mirror_sha256":"4359364816c8a58eb414bafae8ccf24fc3766dc611d15f1aa09e48c8fedc8f88","mirror_size_bytes":61930,"module_id":"BUILD-S2_10-HYBRID","module_kind":"BUILD_ONLY","outputs":["BUILD-S2_10-HYBRID"],"owner":"Stage_2_S2_10_owner","path":"offline_build/build_s2_10_agent_projection.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"4359364816c8a58eb414bafae8ccf24fc3766dc611d15f1aa09e48c8fedc8f88","size_bytes":61930},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["SCHEMA-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"offline_build/validate_s2_10_contract.txt","mirror_sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b","mirror_size_bytes":63811,"module_id":"VALIDATOR-S2_10","module_kind":"BUILD_ONLY","outputs":["VALIDATOR-S2_10"],"owner":"Stage_2_S2_10_owner","path":"offline_build/validate_s2_10_contract.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b","size_bytes":63811},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_agent_contract.txt","mirror_sha256":"016e5bf62398ec211b68f0b3aa987dfcfa5f8b8e20587dd8aae15bbf2bfba3e3","mirror_size_bytes":7717,"module_id":"TEST-S2_10-AGENT","module_kind":"TEST","outputs":["TEST-S2_10-AGENT"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_agent_contract.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"016e5bf62398ec211b68f0b3aa987dfcfa5f8b8e20587dd8aae15bbf2bfba3e3","size_bytes":7717},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["SCHEMA-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_wave_dispatch.txt","mirror_sha256":"f7e59189ed93585c16c55943f7cc3ed464091fb23a9ea086e6e209cf14677a48","mirror_size_bytes":9894,"module_id":"TEST-S2_10-DISPATCH","module_kind":"TEST","outputs":["TEST-S2_10-DISPATCH"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_wave_dispatch.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f7e59189ed93585c16c55943f7cc3ed464091fb23a9ea086e6e209cf14677a48","size_bytes":9894},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["SCHEMA-S2_10","VALIDATOR-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_domain_verdict_contract.txt","mirror_sha256":"9f2547a8c590b7df2aa09d0c3808766bf16be321a448742020d9a3476e7d837f","mirror_size_bytes":10242,"module_id":"TEST-S2_10-DOMAIN","module_kind":"TEST","outputs":["TEST-S2_10-DOMAIN"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_domain_verdict_contract.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"9f2547a8c590b7df2aa09d0c3808766bf16be321a448742020d9a3476e7d837f","size_bytes":10242},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["PROMPT-PROJECTION-S2_10"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_prompt_cache_and_boundaries.txt","mirror_sha256":"87d8657219070e6bc71e9352d6e84b94f957d490daecb62cc73826a9e021e786","mirror_size_bytes":10798,"module_id":"TEST-S2_10-PROMPT","module_kind":"TEST","outputs":["TEST-S2_10-PROMPT"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_prompt_cache_and_boundaries.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"87d8657219070e6bc71e9352d6e84b94f957d490daecb62cc73826a9e021e786","size_bytes":10798},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":["BINDING-S2_10-HYBRID"],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"mirror_byte_parity":true,"mirror_path":"tests/s2_10/test_release_adapter_retry.txt","mirror_sha256":"16b7b1419550b553bbbd42dbcba7e8418124ba5c41adfcae59639d3db9347c0b","mirror_size_bytes":7757,"module_id":"TEST-S2_10-RELEASE","module_kind":"TEST","outputs":["TEST-S2_10-RELEASE"],"owner":"Stage_2_S2_10_owner","path":"tests/s2_10/test_release_adapter_retry.py","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"16b7b1419550b553bbbd42dbcba7e8418124ba5c41adfcae59639d3db9347c0b","size_bytes":7757},{"asset_version":"s2_10.hybrid.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3","RAW-STAGE1-REREAD"],"implementation_status":"IMPLEMENTED_OFFLINE_VERIFIED","incompatible_module_ids":[],"inputs":["S2_10-HYBRID-CONTRACT"],"module_id":"REGRESSION-MANIFEST-S2_10","module_kind":"TEST_MANIFEST","outputs":["REGRESSION-MANIFEST-S2_10"],"owner":"Stage_2_S2_10_owner","path":"tests/fixtures/s2_10/regression_manifest.json","produced_schema_ids":[],"schema_version":"stage2_s2_10_hybrid_asset.v1","scope_predicate":"workflow_id == S2_10","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"804844930d6ff4bff536748c8da23ab3ca2a27303f052614562cd910d885a64e","size_bytes":2572},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-F82ABBC2137A8ABF","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"corpus/build_receipt.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"57fbf8c3f5f86f00db668c425c3e07de74e9d5820b71975c4da3a3bff32e036d","size_bytes":1871},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-F8EF2F640FB77AD1","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"corpus/chunk_manifest.jsonl","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"fd3e1ba067b5960e1d3dba1b5107de913d45ba2b3732a28c3870b10105761b67","size_bytes":620},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-787106ABBFAF8931","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"corpus/source_manifest.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"ce3ce207ed274292499630a0813d4bdb6a66baf26710fb0e883c2e405adb9a67","size_bytes":807},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-3CD8C31D78EBF47F","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"corpus/weaviate_collection.schema.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f29b287e99d29dbd6d3008d1ad68e3960f9cc2c72459f04316f0311cd3e9a47d","size_bytes":18104},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-E852EAB9373DF086","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"law_values/court_fee_values.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"d1213e58b0de3b728945134e0a00a71565fee6db9077c932d7ee99da506c1115","size_bytes":912},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-309310FEC8A6178F","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"law_values/general_law_values.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"45ad98e9e93090fbc5e5c7037f404bd86ff89b73a4aaf8c3449d226bd2aacd1c","size_bytes":843},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-060B74A3C012D23C","module_kind":"MANIFEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"manifest/authority_release.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"cc683958377eea44756de6e9c74fc925da66dd60cc84363f5bec36584f5b2967","size_bytes":1068},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-B51F7A86E15908DD","module_kind":"MANIFEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"manifest/case_type_coverage.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"34d6f758fc1486fa7330ede376e65b9448a6eaa971bd21472b0e4b7d9a5b8cba","size_bytes":143780},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-16A7CA30B83DD493","module_kind":"MANIFEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"manifest/case_type_registry.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"6462217f08ced21693e8cfc812182cfc1da593d52e719e5cd11ef30409ffd00e","size_bytes":80525},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-34A6FBBE9101F1E8","module_kind":"MANIFEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"manifest/case_type_rule_registry.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"ef42e873f497e1e2cfdf9440207f7caaa1f55462a3d47dc207b3c20f9c102793","size_bytes":56945},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-3051DBC07D975B39","module_kind":"MANIFEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"manifest/corpus_release.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"27d0c83e7a95342efdbe5be675b6d5b7d0ff8695eeec022ebc5aded3359fc981","size_bytes":920},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-98D409BCD1DE68FA","module_kind":"MANIFEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"manifest/s2_20_parent_member_paths.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"19822af8fd0245c84b303955106c99ebdfcb5bbce06e76602178c90d4ecf6e82","size_bytes":5443},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-672D5EBD9C1F8AE0","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"migration/actio_assets_receipt.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"0d1a3b5eaa7ea670f76db0baa8c30e23b4367904c3f715145737f1dff22f9204","size_bytes":10407},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-2B7093A6A3B291AF","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"migration/legacy_asset_disposition.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"1b0c6b3bafd8bb11ec90e809d0fce057fec83fb12b1f9976471f8952ce073f2c","size_bytes":6039},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"offline_build/build_authority_law_value_release.txt","mirror_sha256":"8c1d9b2f05fa752d50349ed1a4479aafcb17c2f574fa1ebe6b19d2bedc21ffeb","mirror_size_bytes":7213,"module_id":"S2_20-ASSET-E3CE7EC9216DDBD3","module_kind":"BUILD_ONLY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"offline_build/build_authority_law_value_release.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"8c1d9b2f05fa752d50349ed1a4479aafcb17c2f574fa1ebe6b19d2bedc21ffeb","size_bytes":7213},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"offline_build/build_corpus_snapshot.txt","mirror_sha256":"96feead66f24d375b9ac379f459de040a9ae1f368e9c05d2911c5b0da54c02c2","mirror_size_bytes":9781,"module_id":"S2_20-ASSET-5F7DB1273D883DE5","module_kind":"BUILD_ONLY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"offline_build/build_corpus_snapshot.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"96feead66f24d375b9ac379f459de040a9ae1f368e9c05d2911c5b0da54c02c2","size_bytes":9781},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"offline_build/build_release_manifests.txt","mirror_sha256":"fedda0f09d598b19fee83f5ef515a05c162bc09a2a72b7337bb2529d222eedf3","mirror_size_bytes":18956,"module_id":"S2_20-ASSET-6CE2AD14244F5B49","module_kind":"BUILD_ONLY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"offline_build/build_release_manifests.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"fedda0f09d598b19fee83f5ef515a05c162bc09a2a72b7337bb2529d222eedf3","size_bytes":18956},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"offline_build/build_s2_20_inline_projection.txt","mirror_sha256":"b5c035251621920dd848d672dc6cf840500ce2b60e1e6656f021855681f6fc58","mirror_size_bytes":27865,"module_id":"S2_20-ASSET-86DCA2BCA2270252","module_kind":"BUILD_ONLY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"offline_build/build_s2_20_inline_projection.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"b5c035251621920dd848d672dc6cf840500ce2b60e1e6656f021855681f6fc58","size_bytes":27865},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"offline_build/compile_case_type_registry.txt","mirror_sha256":"e415b5179e53f5a7e35e5e1b7749706c783fbf296bf0bf40103e9450df0b2f0c","mirror_size_bytes":9658,"module_id":"S2_20-ASSET-A360A270415B6E16","module_kind":"BUILD_ONLY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"offline_build/compile_case_type_registry.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"e415b5179e53f5a7e35e5e1b7749706c783fbf296bf0bf40103e9450df0b2f0c","size_bytes":9658},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"offline_build/compile_relief_rule_projection.txt","mirror_sha256":"8412f2ad7b8de4738fbb86a7596def90dbc9d34d4e2f24854e964df476589c2f","mirror_size_bytes":42682,"module_id":"S2_20-ASSET-B57CC23FD4D06275","module_kind":"BUILD_ONLY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"offline_build/compile_relief_rule_projection.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"8412f2ad7b8de4738fbb86a7596def90dbc9d34d4e2f24854e964df476589c2f","size_bytes":42682},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-DDC65130DFBDBA55","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/binding/binding_signature_projection.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"b4317d0a5adc3f2bc70214c3e98c4f885ecfd26e36d8242221db4971b6f53142","size_bytes":835},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-AA1E72AB254FE6DC","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-01_interest_delay.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"d0d180a8f8af72d7dda5134b521122827fd2d1f9055c10d35ef5881d7d8cea53","size_bytes":1503},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-45939B38FC8F0B28","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-02_allocation_setoff_balance.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"19a6a32b5879f20ed36df149cdbfb40ec2b5835152579e90a396f745e6299772","size_bytes":1511},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-A4DAC30C3F1AC365","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-03_limitation_deadline.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"1dc66a75034576a41be43058ac421f5c8188288da9928df4d5b9cf616a819ea7","size_bytes":1515},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-D9EEBF8449D85E04","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-04_valuation.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"e83044bfd16167cc6c1028afa06b0dc041e0114cee72379563047cff9fbeba11","size_bytes":1483},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-BC815891D9220145","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-05_personal_injury.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"0f197813d298f87010545d6eb09c4107c13303b6eb83d276e0130e25abaa7b00","size_bytes":1512},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-2A85ED56A7C1242A","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-06_construction_defect.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"1626c6673b7381e00fa61b7724ae63c8b10cb2ddd932040edc5f69889ffe5ab3","size_bytes":1522},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-6E799BF3C28471BC","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-07_lease_use_gain.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f05c94ad80280607664a1ddcbd29367d0164333888c0d744dd674bf21cee3fad","size_bytes":1488},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-3F219FD043EF4AE1","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-08_inheritance_reserved_share.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"330a1e5f72e4a4e4b7da096ceae554312ca2f828363b761ed37af37abf281818","size_bytes":1518},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-BCAEF55DAB2126D1","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-09_wage_severance.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"dba73da63069e89f95550419d989da78f8fe10196b052c49f050466a0dcda5b9","size_bytes":1489},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-81B84BD832C992EF","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-10_actio_insolvency_value.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"c8710d3e9292b95e53d834006e784b13669b9023149e26628d0abaff074c1c85","size_bytes":1532},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-2852DF1EC788C440","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-11_distribution_share_division.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"4b3c8161ae779628575e122f1f602d6bee40b387c402c5ce642692796e1ab369","size_bytes":1512},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-FD4E3378C53FDCB5","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-12_insurance.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"66d58d2043e45966df9db6c89e980f3c91cbdd6fcf4a4d96944fc203abedf532","size_bytes":1503},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-22371F1936240951","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-13_court_value_cost.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"6f914553fd25a9ecb28d85070c2c0ddbf41178906f9075092f9d174977073824","size_bytes":1539},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-711DD22CD3721D81","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-R1_ip_damage.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"d33b7963d68cee10281da86e465caed152e3f69493728a563bca69c6dfb18c2b","size_bytes":1482},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-B585B054934C1C1D","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-R2_org_liquidation.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"3bb03e07ba28a01d8c9e3944df55fbc43b21e17435bfa5e84ec60ef7b1540991","size_bytes":1513},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-E032EAE75B122995","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-R3_transport_maritime.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"e64e808a4d1637c991f1ab512fad25f51637ecba3b2b0dfa66abb7efb8504c7e","size_bytes":1508},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-ED6526C35FE96ACC","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/CE-R4_financial_instrument.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"0198a5d09f9b75dd3b6978a01c9a8a3e468c8dd305c49721ae04ce93954b72d3","size_bytes":1510},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-B0A8A8F08ECBF0AA","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/calculations/calculation_activation_registry.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"1a152af7e0ed4ee2ce10ab93bf3be343122f95e9f276585433449bde27c46e55","size_bytes":5540},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-69ADD915C1858F49","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/corpus/requirement_fact_scope.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"6b38306dd4657df2c929caeb1b71bf78838f628e99018fff4692bacf4d49e2c1","size_bytes":216},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-A416DC6DB820828C","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/drafting/case_type_relief_rules.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"01f95c0c1a95f80bfde1ee9c166ffb3ab1f22339ee035f5c322e2637329b8214","size_bytes":1416},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-B8BE6EC64CED552C","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/drafting/counter_performance_rules.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"5c9e274f38fcfa4e8bfcb0938d2274b8dd83a6f8dac874de3c237b7a2608ab43","size_bytes":857},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-5FDE95EE5314FCA7","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/drafting/forbidden_relief_rules.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"fb51bec7b741e983a5e6f59f8265f3818c49f8067323e9b2090673a832eaae22","size_bytes":852},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-8CDFE6C311F7F7D3","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/drafting/procedural_declaration_rules.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"3328d71a3fb020024fa8a0f8592788d409845d5ca0e2ca9a2b6c63ed3530147c","size_bytes":864},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-4414670A81E95DEA","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/party/party_set_rules.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"92b1b8b76a396008629f534fdcd63995733ccdd71d6b2d9f9c4ad7360daf0c0a","size_bytes":225},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-E1D9BDC792A7848D","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/planning/option_disposition_policy.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f3a656c3c1c0eabd22d26e4d3c9f541459af05727f595646a2f7a59d5c5e7658","size_bytes":426},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-CD373B0725950922","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/regression/finding_fixture_map.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"1b719bf6603a2800d9d7bd97347ac16afdc216be87ca282c6b215dd95512c438","size_bytes":1351},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-809C3C709B4F2295","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/review/review_policy_registry.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"960b52019aa947f19c63101a75105a9e155004e4ea8fdcf5546072a9ddad0b96","size_bytes":939},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-8EC84AB1A8C873B2","module_kind":"DECLARATIVE_REGISTRY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"registry/temporal/inheritance_reserved_share.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"ca37eb894d04dcba604fe4656f941c44d76fafa1c221f4ffcdd988419a81d7a0","size_bytes":928},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"release_ops/canary_rollback.txt","mirror_sha256":"327356b4f59bb850fcfb680d5caf13038025e3042689f11e1018731058d15241","mirror_size_bytes":1345,"module_id":"S2_20-ASSET-2040ED84718FA22C","module_kind":"BUILD_ONLY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"release_ops/canary_rollback.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"327356b4f59bb850fcfb680d5caf13038025e3042689f11e1018731058d15241","size_bytes":1345},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"release_ops/release_validator.txt","mirror_sha256":"2cb2e287123bf2f58f6b869d2076f67e59b1cb3b830029e11d20e4bc30b3835a","mirror_size_bytes":27546,"module_id":"S2_20-ASSET-DBB6C2CCD4AB3FA2","module_kind":"BUILD_ONLY","outputs":[],"owner":"Stage_2_S2_20_owner","path":"release_ops/release_validator.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"2cb2e287123bf2f58f6b869d2076f67e59b1cb3b830029e11d20e4bc30b3835a","size_bytes":27546},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-52C4FB90CB0F9E68","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"renderers/R01_money_payment.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"af1860eab21bd6551bb9570e267df2ddf043b71f57c83b55dfe2a32a1c96b7d0","size_bytes":977},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-B031219CDF7A06DF","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"renderers/R02_delivery_possession.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"a3879c0b43f04b952e8eb326a491836678e4490ad3c0a5ebe2c3d592b8c75162","size_bytes":968},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-7BEB573DD33365A1","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"renderers/R03_declaration_registry.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"239e060a870541166c0b164937ad8b349caf81a4713e1bc53fbcd71a73823843","size_bytes":977},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-769585D81F81F620","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"renderers/R04_special_nonmoney_performance.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"32295e7cdacabf348102e1bbf500457f599513213022c6a70137d6465118958f","size_bytes":989},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-04C3EF62029B522C","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"renderers/R05_declaratory.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"9e265ce00d3fbb245efb2c82264dd39238ca0406efb04562a4c5e09932910833","size_bytes":976},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-BFC589A870A04F07","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"renderers/R06_constitutive_judgment_challenge.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"7d54a7657c49671f79acb7bead6cb33c46c78eab93d9c5927c1b8795913cf24c","size_bytes":996},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-B2C88C2FFDE4487E","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"routing/actio_pauliana_mortgage_route.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"209fce4f610e7eb6c6ddc2995163de59537ca546752187c23dd1b417e2b117ec","size_bytes":721},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-DD9ABC431EB1B8BC","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"routing/actio_pauliana_route_registry.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f30550aca6ce2a453bd39498d81653eb257cffcd6c693ba56b62f698a510427c","size_bytes":906},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"runtime/authority/authority_preflight.txt","mirror_sha256":"ffa513bdc7ddb1de895b4e813de9e107c94c36a5955166c7d5cee745584d0dda","mirror_size_bytes":2115,"module_id":"S2_20-ASSET-2317885DE36C87BB","module_kind":"RUNTIME_CORE","outputs":[],"owner":"Stage_2_S2_20_owner","path":"runtime/authority/authority_preflight.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"ffa513bdc7ddb1de895b4e813de9e107c94c36a5955166c7d5cee745584d0dda","size_bytes":2115},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"runtime/binding_signature_projection.txt","mirror_sha256":"0ea048355b4abf8cf261bc60735a557ab09d84e325aa42bd4d7acb58f589c6b5","mirror_size_bytes":5843,"module_id":"S2_20-ASSET-2B67B561F6C4FD3B","module_kind":"RUNTIME_CORE","outputs":[],"owner":"Stage_2_S2_20_owner","path":"runtime/binding_signature_projection.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"0ea048355b4abf8cf261bc60735a557ab09d84e325aa42bd4d7acb58f589c6b5","size_bytes":5843},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"runtime/c25_case_type_bind.txt","mirror_sha256":"7cda9812225379ef92c34b8ca383aa35bdcbac6afc30b3a21245f5e3ad6aa3af","mirror_size_bytes":3471,"module_id":"S2_20-ASSET-2EABAE08AAE777EA","module_kind":"RUNTIME_CORE","outputs":[],"owner":"Stage_2_S2_20_owner","path":"runtime/c25_case_type_bind.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"7cda9812225379ef92c34b8ca383aa35bdcbac6afc30b3a21245f5e3ad6aa3af","size_bytes":3471},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"runtime/c26_rule_branch_bind.txt","mirror_sha256":"f6d6fb87b005b0a155e24ae93c7550640bfdaa221063af034c3d3e20b46cc6b6","mirror_size_bytes":5089,"module_id":"S2_20-ASSET-FCA7B87C74F0BB44","module_kind":"RUNTIME_CORE","outputs":[],"owner":"Stage_2_S2_20_owner","path":"runtime/c26_rule_branch_bind.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f6d6fb87b005b0a155e24ae93c7550640bfdaa221063af034c3d3e20b46cc6b6","size_bytes":5089},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"runtime/c27_query_plan.txt","mirror_sha256":"d7fe404438e185b2e0351b8b5ac8fc7bd911a63544e71c7c16ffb313dc566777","mirror_size_bytes":6553,"module_id":"S2_20-ASSET-1327E50515C73062","module_kind":"RUNTIME_CORE","outputs":[],"owner":"Stage_2_S2_20_owner","path":"runtime/c27_query_plan.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"d7fe404438e185b2e0351b8b5ac8fc7bd911a63544e71c7c16ffb313dc566777","size_bytes":6553},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"runtime/c28_weaviate_retrieve.txt","mirror_sha256":"9634c80dd1b77ab652185ef5bd2b67ccbb7d355d2b4661f67d86fa78d0ee6395","mirror_size_bytes":12083,"module_id":"S2_20-ASSET-4AE83266D12B501B","module_kind":"RUNTIME_CORE","outputs":[],"owner":"Stage_2_S2_20_owner","path":"runtime/c28_weaviate_retrieve.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"9634c80dd1b77ab652185ef5bd2b67ccbb7d355d2b4661f67d86fa78d0ee6395","size_bytes":12083},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"runtime/c29_pack_verify.txt","mirror_sha256":"4318f95c9c38391a455613b7411cee923288d19748f3c0946e86c588be718b9d","mirror_size_bytes":5721,"module_id":"S2_20-ASSET-0A121BEB45A15E4D","module_kind":"RUNTIME_CORE","outputs":[],"owner":"Stage_2_S2_20_owner","path":"runtime/c29_pack_verify.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"4318f95c9c38391a455613b7411cee923288d19748f3c0946e86c588be718b9d","size_bytes":5721},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"runtime/calculators/registry_engine.txt","mirror_sha256":"173da5185caa6d4a82a7735018c9121ef42481cad8d83b9574c9f432912c556d","mirror_size_bytes":11417,"module_id":"S2_20-ASSET-FE154EB4E8F19440","module_kind":"RUNTIME_CORE","outputs":[],"owner":"Stage_2_S2_20_owner","path":"runtime/calculators/registry_engine.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"173da5185caa6d4a82a7735018c9121ef42481cad8d83b9574c9f432912c556d","size_bytes":11417},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-E82839694073D431","module_kind":"JSON_SCHEMA","outputs":[],"owner":"Stage_2_S2_20_owner","path":"schemas/binding_retrieval.schema.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"25df409822a45fd26d0c5cf46e4afb55af100765c86afa148046354b2f02c8dc","size_bytes":47811},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-C9FC917D8CBF920E","module_kind":"JSON_SCHEMA","outputs":[],"owner":"Stage_2_S2_20_owner","path":"schemas/calculation.schema.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"7036793fe6c8d3550ca22486c281b1ffdbb915995dbe17c92d98a3f9a8f194ce","size_bytes":33698},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-5D843BA99044953C","module_kind":"JSON_SCHEMA","outputs":[],"owner":"Stage_2_S2_20_owner","path":"schemas/domain_verdict.schema.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"5fec56a0661e2ab2351d92939c4fbdedc8ac4c7e8699dc92caae7dd359c7eb16","size_bytes":1262},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-FF1F03B3FC56E579","module_kind":"JSON_SCHEMA","outputs":[],"owner":"Stage_2_S2_20_owner","path":"schemas/relief_plan.schema.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"ef5d271e981390421fa7a188c01e52b390ab138bfc32203056f59d7ab6dfc8f2","size_bytes":31558},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-9A06594587743420","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/fixtures/s2_20/actio_route_cap_matrix.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"f922b3ec10cc1da44c62d6ca5cf9c8ec5a53f5328b1eaabf236db46448907ca8","size_bytes":572},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-3ECDA3121E0AE910","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/fixtures/s2_20/calculation_temporal_boundaries.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"a31af03c418466b1dfc05f47ea05b58109fe142b813da2b3a8863e9d5f71004d","size_bytes":1612},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-5EB4DEF644C02B9D","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/fixtures/s2_20/case_type_rule_binding_matrix.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"45e0f7470e2907d20d6359d192fe8320051bf8a278f8eaa8153fbe964819685c","size_bytes":1042},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-60AF7FED54CBA5C1","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/fixtures/s2_20/case_type_zero_multi_hash_mutations.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"0fbe5f68f4e60dfdd4fd03b9f6f076526f1b3d273b328ec853e69b4f9c315180","size_bytes":1123},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-49D464EEB3D65FFB","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/fixtures/s2_20/minimal_supported_portfolio.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"4bcbcf19019e086e4eb4772cb55e87832c3a68c4829f67d50c6a7318dda79c86","size_bytes":733},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-97F26683DEAEA7F7","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/fixtures/s2_20/mixed_option_dispositions.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"509f3f436c8107c03033324413560b706487279bf91f2e231bd43bbc5c86b8e0","size_bytes":1457},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-8A3BBD97738BC0BF","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/fixtures/s2_20/plan_publish_barrier_failure.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"29afbbdfa609234b06473542c35df7083ca6313155456cc6d1ec311fd50ea275","size_bytes":1640},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-47A96BFDC0754003","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/fixtures/s2_20/regression_manifest.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"2a1a163d62a041990d71feaef54f3c56753ea3aa02666d3dc4c631552f7ee746","size_bytes":15014},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-B42FA1C2AD23D1BA","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/fixtures/s2_20/retrieval_replay_injection.json","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"8fbed161a30d52d8f5018f238dbf2cd01e0dd84162c97e3293df62d82a7ca804","size_bytes":1338},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"tests/s2_20/test_agent_and_inline_parity.txt","mirror_sha256":"0f73c112b28088be191b6717cb24cac468762cc286f8dc806bf65d98a95bf262","mirror_size_bytes":2773,"module_id":"S2_20-ASSET-E1B1709F62DB7D8A","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/s2_20/test_agent_and_inline_parity.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"0f73c112b28088be191b6717cb24cac468762cc286f8dc806bf65d98a95bf262","size_bytes":2773},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"tests/s2_20/test_calculation_and_reports.txt","mirror_sha256":"c1bd36c351173d25e6d8e68d1a90d738b90aabb51b201ca13f4370654e584e00","mirror_size_bytes":3199,"module_id":"S2_20-ASSET-1D1B579AA031EC90","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/s2_20/test_calculation_and_reports.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"c1bd36c351173d25e6d8e68d1a90d738b90aabb51b201ca13f4370654e584e00","size_bytes":3199},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"tests/s2_20/test_inline_output_schema.txt","mirror_sha256":"e2cb938dd639874a4b150997d66eacd89b22016c8fd3385a243edf781cd3e85b","mirror_size_bytes":43574,"module_id":"S2_20-ASSET-22C24679226830D0","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/s2_20/test_inline_output_schema.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"e2cb938dd639874a4b150997d66eacd89b22016c8fd3385a243edf781cd3e85b","size_bytes":43574},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"tests/s2_20/test_offline_compilers.txt","mirror_sha256":"5e75f659a9e5add1345f1596c92c0b143cb1e48110c746a8785fb5b38f1371af","mirror_size_bytes":20252,"module_id":"S2_20-ASSET-6FFF0ED2C88404C2","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/s2_20/test_offline_compilers.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"5e75f659a9e5add1345f1596c92c0b143cb1e48110c746a8785fb5b38f1371af","size_bytes":20252},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"tests/s2_20/test_option_group_and_binding.txt","mirror_sha256":"6021d93fb47377cf6c00ccb5a880a7e85987ec5a1ed7976a84baca2d7fb39985","mirror_size_bytes":6171,"module_id":"S2_20-ASSET-E56589B5CCA0246C","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/s2_20/test_option_group_and_binding.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"6021d93fb47377cf6c00ccb5a880a7e85987ec5a1ed7976a84baca2d7fb39985","size_bytes":6171},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"tests/s2_20/test_plan_publish_and_release.txt","mirror_sha256":"6373571eb1123389bf5d585507a1f694d75ecfa9eb572cff2a0c5ad1d21751aa","mirror_size_bytes":12949,"module_id":"S2_20-ASSET-2868A4B922737A76","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/s2_20/test_plan_publish_and_release.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"6373571eb1123389bf5d585507a1f694d75ecfa9eb572cff2a0c5ad1d21751aa","size_bytes":12949},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"tests/s2_20/test_regression_manifest_closure.txt","mirror_sha256":"886ae584a7846acdb5499d52e1ae8ec177e8d1dc34aa1d43e99dbebafa255516","mirror_size_bytes":8493,"module_id":"S2_20-ASSET-0C03601E14F2D714","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/s2_20/test_regression_manifest_closure.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"886ae584a7846acdb5499d52e1ae8ec177e8d1dc34aa1d43e99dbebafa255516","size_bytes":8493},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"mirror_byte_parity":true,"mirror_path":"tests/s2_20/test_retrieval_and_pack.txt","mirror_sha256":"c314cf27511b0fe065b8961196084a28b1c7c5cb2fed3dd5c8765172ba40462f","mirror_size_bytes":6654,"module_id":"S2_20-ASSET-A24AC597CB6DF548","module_kind":"TEST","outputs":[],"owner":"Stage_2_S2_20_owner","path":"tests/s2_20/test_retrieval_and_pack.py","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"c314cf27511b0fe065b8961196084a28b1c7c5cb2fed3dd5c8765172ba40462f","size_bytes":6654},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-CAB87BA40B475A9D","module_kind":"S2_20_ASSET","outputs":[],"owner":"Stage_2_S2_20_owner","path":"validators/actio_value_compensation_invariants.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"591182a8869f3c0c0e03e9b32f5f0d9b856361b3f5f77f1e6e87eb0188f1ef22","size_bytes":983},{"asset_version":"s2_20.1","authority_ids":[],"consumed_schema_ids":[],"dependency_module_ids":[],"forbidden_contract_codes":["LEGACY-STAGE2-V0-V3"],"implementation_status":"DEV_HASH_BOUND_OFFLINE_ONLY","incompatible_module_ids":[],"inputs":[],"module_id":"S2_20-ASSET-AA40CF5140DE8790","module_kind":"WORKFLOW","outputs":[],"owner":"Stage_2_S2_20_owner","path":"workflows/S2_20_canonical_relief_plan_reduce.yml","produced_schema_ids":[],"schema_version":"stage2_s2_20_generic_asset.v1","scope_predicate":"workflow_id == S2_20","semantic_review_status":"PENDING_LEGAL_AND_LIVE_ADMISSION","sha256":"da56152af502fe432cb2ebaa593a4e389b7f2cd4d0b8f3be29e49717cff0544a","size_bytes":2443}],"producer_aliases":[{"alias_id":"PA-SG-COMPILER-001","bidirectional_match_allowed":true,"contract_status":"CLOSED_DECISION_FIXTURE_PENDING","global_alias_allowed":false,"orchestration_producer_id":"Task_C_BO_S0_signal_bundle_writer","schema_writer_id":"Task_C_BO_S0_canonical_signal_compiler","scope":"STAGE1_PART2_SIGNAL_TRANSACTION_ONLY"}],"schema_version":"stage2_module_manifest.v1"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_00_inline_code_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_00_inline_code_receipt.json index 9b20c026..f18f13f2 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_00_inline_code_receipt.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_00_inline_code_receipt.json @@ -1 +1 @@ -{"authoring":{"path":"Stage_2_S2_00_v.2.yml","sha256":"04877f5459c8a579d793ed34ac936cc366a7e5b1c77a1230c93284036196fabe","size_bytes":367573,"unique_key_parse":"PASS"},"authoring_rewritten":false,"build_kind":"OFFLINE_AUTHORING_PROJECTION","canonical_code":{"ast_status":"PASS","code_sha256":"92f13cf3ac113c68e646fcf3b8d6bf9b8a7f41962eef809af91ebf133d50bc99","code_size_bytes":283196,"compile_status":"PASS","encoding":"UTF-8","external_python_source_ref_count":0,"external_url_count":0,"extraction_transform":"NONE","forbidden_dynamic_call_count":0,"forbidden_import_count":0,"imports":["__future__","argparse","base64","binascii","collections","contextlib","dataclasses","hashlib","httpx","io","itertools","json","math","os","pathlib","re","shutil","stat","sys","tempfile","typing","unicodedata"],"placeholder_count":0,"plaintext_secret_count":0,"yaml_pointer":"/Agent/Stages/0/tasks/0/parameters/code"},"deployment_projection":{"byte_identical_to_authoring":true,"canonical_task_semantics_sha256":"4565b8ea46aac8eab0649e4bff5c17afdc3bc0aaebe77eb6301c3bdede612d0e","path":"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_00.yml","sha256":"04877f5459c8a579d793ed34ac936cc366a7e5b1c77a1230c93284036196fabe","size_bytes":367573},"full_code_mirrors":[{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.py","sha256":"92f13cf3ac113c68e646fcf3b8d6bf9b8a7f41962eef809af91ebf133d50bc99","size_bytes":283196},{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.txt","sha256":"92f13cf3ac113c68e646fcf3b8d6bf9b8a7f41962eef809af91ebf133d50bc99","size_bytes":283196}],"parity_status":"PASS","schema_version":"stage2_s2_00_inline_code_receipt.v2","source_of_truth":"Stage_2_S2_00_v.2.yml","task_contract":{"agent":{"name":"Stage_2_S2_00_v2","version":"1.2.0"},"mcp_servers":{"code-executor":{"type":"streamable-http","url":"https://code-executor.mcp.eroomai.com/mcp"},"localdocs":{"type":"streamable-http","url":"http://mcp-localdocs:8012/mcp"}},"stage":{"name":"S2_00","nexts":[],"prevs":[]},"task":{"code_sha256":"92f13cf3ac113c68e646fcf3b8d6bf9b8a7f41962eef809af91ebf133d50bc99","mcp":"code-executor","parameters":{"language":"python","network":"agent-network","requirements":"httpx==0.28.1","timeout":300},"task_name":"Task_S2_00_deterministic_ingress","tool_name":"run_code"},"task_procedure":{"IN":{"nexts":["Task_S2_00_deterministic_ingress"],"wait_until":[]},"OUT":{"nexts":[],"wait_until":["Task_S2_00_deterministic_ingress"]},"Task_S2_00_deterministic_ingress":{"nexts":["OUT"],"wait_until":["IN"]}}},"workflow_id":"S2_00"} +{"authoring":{"path":"Stage_2_S2_00_v.2.yml","sha256":"45ca76de0c352745f8111ed036a001d053bdc9b43c1a72292d49d9b5f3bcd2c7","size_bytes":367573,"unique_key_parse":"PASS"},"authoring_rewritten":false,"build_kind":"OFFLINE_AUTHORING_PROJECTION","canonical_code":{"ast_status":"PASS","code_sha256":"b2c56fde707664903158f7d73073fc4a4913fb21234df455bdf5fe2bd1e5fafa","code_size_bytes":283196,"compile_status":"PASS","encoding":"UTF-8","external_python_source_ref_count":0,"external_url_count":0,"extraction_transform":"NONE","forbidden_dynamic_call_count":0,"forbidden_import_count":0,"imports":["__future__","argparse","base64","binascii","collections","contextlib","dataclasses","hashlib","httpx","io","itertools","json","math","os","pathlib","re","shutil","stat","sys","tempfile","typing","unicodedata"],"placeholder_count":0,"plaintext_secret_count":0,"yaml_pointer":"/Agent/Stages/0/tasks/0/parameters/code"},"deployment_projection":{"byte_identical_to_authoring":true,"canonical_task_semantics_sha256":"6905affae3adfb2e9b4f28c94f039011161c79ea88eebcd70c6cb623c597c0f4","path":"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_00.yml","sha256":"45ca76de0c352745f8111ed036a001d053bdc9b43c1a72292d49d9b5f3bcd2c7","size_bytes":367573},"full_code_mirrors":[{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.py","sha256":"b2c56fde707664903158f7d73073fc4a4913fb21234df455bdf5fe2bd1e5fafa","size_bytes":283196},{"byte_identical_to_canonical_code":true,"path":"Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.txt","sha256":"b2c56fde707664903158f7d73073fc4a4913fb21234df455bdf5fe2bd1e5fafa","size_bytes":283196}],"parity_status":"PASS","schema_version":"stage2_s2_00_inline_code_receipt.v2","source_of_truth":"Stage_2_S2_00_v.2.yml","task_contract":{"agent":{"name":"Stage_2_S2_00_v2","version":"1.2.0"},"mcp_servers":{"code-executor":{"type":"streamable-http","url":"https://code-executor.mcp.eroomai.com/mcp"},"localdocs":{"type":"streamable-http","url":"http://mcp-localdocs:8012/mcp"}},"stage":{"name":"S2_00","nexts":[],"prevs":[]},"task":{"code_sha256":"b2c56fde707664903158f7d73073fc4a4913fb21234df455bdf5fe2bd1e5fafa","mcp":"code-executor","parameters":{"language":"python","network":"agent-network","requirements":"httpx==0.28.1","timeout":300},"task_name":"Task_S2_00_deterministic_ingress","tool_name":"run_code"},"task_procedure":{"IN":{"nexts":["Task_S2_00_deterministic_ingress"],"wait_until":[]},"OUT":{"nexts":[],"wait_until":["Task_S2_00_deterministic_ingress"]},"Task_S2_00_deterministic_ingress":{"nexts":["OUT"],"wait_until":["IN"]}}},"workflow_id":"S2_00"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_agent_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_agent_receipt.json index 34ef463b..672400a4 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_agent_receipt.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_agent_receipt.json @@ -1 +1 @@ -{"agent_contract":{"agent_name":"Stage_2_S2_10","agent_version":"1.1.0","deterministic_task_count":0,"inline_common_prompt_sha256":"a3f001acfed764b38e34f12d72f13cdbcab59a2e44150dd1d71c5bf8ba1f7099","inline_static_prompt_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","item_tokens":["{{item.selected_legal_context_json}}","{{item.cluster_case_payload_json}}"],"llm_task_count":1,"prompt_roles":["system","system","system","user"],"reduce_task_count":0,"stage_name":"S2_10","task_name":"Task_S2_10_resolve_cluster","tool_task_count":0},"authoring":{"path":"Stage_2_S2_10_v.1.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},"binding":{"path":"deployment/stage2_s2_10_llm_binding.yml","sha256":"b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1"},"child_release":{"path":"manifest/s2_10_release.json","release_digest":"7f6a9ada26c5b11525fd45c3a955fa491cd6e1f66ae491596d1505ba6a749c53","sha256":"b2574efbc61aadf5882b273c18918d62ca3151b95d697bb35e539abd8594698c"},"deployment":{"byte_parity":"PASS","path":"agent_scripts/Stage_2_S2_10.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},"external_admission_status":"PENDING","inline_prompt_receipt":{"path":"manifest/s2_10_inline_prompt_projection_receipt.json","sha256":"c0e388e56f52ee4fef2b825307ef41b204a80dd740cbc8f1562b30c3cd1d3696","size_bytes":2659},"receipt_digest":"1df0b4482dc5d828593df60d58676ac2a545614b910030589d3c85517043c4ca","receipt_status":"OFFLINE_AGENT_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema":{"path":"schemas/s2_10.schema.json","sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee","size_bytes":82703},"schema_version":"stage2_s2_10_agent_receipt.v2","workflow":{"path":"workflows/S2_10_domain_relief_resolution_map.yml","sha256":"f0fba48f1760cf4e233d7d698fd19090f96ffb89cabe4b2e2cae9af7c616be0f","size_bytes":15320}} +{"agent_contract":{"agent_name":"Stage_2_S2_10","agent_version":"1.1.0","deterministic_task_count":0,"inline_common_prompt_sha256":"a3f001acfed764b38e34f12d72f13cdbcab59a2e44150dd1d71c5bf8ba1f7099","inline_static_prompt_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","item_tokens":["{{item.selected_legal_context_json}}","{{item.cluster_case_payload_json}}"],"llm_task_count":1,"prompt_roles":["system","system","system","user"],"reduce_task_count":0,"stage_name":"S2_10","task_name":"Task_S2_10_resolve_cluster","tool_task_count":0},"authoring":{"path":"Stage_2_S2_10_v.1.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},"binding":{"path":"deployment/stage2_s2_10_llm_binding.yml","sha256":"90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95"},"child_release":{"path":"manifest/s2_10_release.json","release_digest":"1ad0ab3edd035d7ecafa3d148e9ae5fb7980326e4291a88caffcd04099845f5c","sha256":"284315158756d0391263dc808fcc3fff468b2e721d9d83ce7897b4666571a52f"},"deployment":{"byte_parity":"PASS","path":"agent_scripts/Stage_2_S2_10.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},"external_admission_status":"PENDING","inline_prompt_receipt":{"path":"manifest/s2_10_inline_prompt_projection_receipt.json","sha256":"4c95f3b3884900d1701f955e109f8927b9c5d818aa5c97623dba567faf7f61e9","size_bytes":2659},"receipt_digest":"7fb1d388ccab3bc281f93850d25515aac5f655f19f6eb29998c98ad1489b12b9","receipt_status":"OFFLINE_AGENT_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema":{"path":"schemas/s2_10.schema.json","sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee","size_bytes":82703},"schema_version":"stage2_s2_10_agent_receipt.v2","workflow":{"path":"workflows/S2_10_domain_relief_resolution_map.yml","sha256":"f0fba48f1760cf4e233d7d698fd19090f96ffb89cabe4b2e2cae9af7c616be0f","size_bytes":15320}} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_inline_prompt_projection_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_inline_prompt_projection_receipt.json index ebec4e13..2476cf08 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_inline_prompt_projection_receipt.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_inline_prompt_projection_receipt.json @@ -1 +1 @@ -{"authoring_agent":{"path":"Stage_2_S2_10_v.1.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},"builder":{"path":"offline_build/build_s2_10_agent_projection.py","sha256":"f89acf6ddb307515006ed904865c05df1d38051bacb5f13e9f928142f8d72020","size_bytes":56552},"canonicalization_algorithm_id":"S2_10-NFC-LF-RTRIM-CLAUSE-PROJECTION-V1","deployment_agent":{"byte_parity":"PASS","path":"agent_scripts/Stage_2_S2_10.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},"inline_scalars":[{"raw_scalar_sha256":"a3f001acfed764b38e34f12d72f13cdbcab59a2e44150dd1d71c5bf8ba1f7099","role":"system","size_bytes":7184,"wrapper":"stage_2_common_cache_prefix","yaml_pointer":"/Agent/Stages/0/map_reduce/map/tasks/0/prompts/0/content"},{"raw_scalar_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","role":"system","size_bytes":12261,"wrapper":"s2_10_legal_resolution_static_prompt","yaml_pointer":"/Agent/Stages/0/map_reduce/map/tasks/0/prompts/1/content"}],"message_order":["INLINE_COMMON_SYSTEM","INLINE_STATIC_LEGAL_SYSTEM","SELECTED_CONTEXT_SYSTEM_DATA","CLUSTER_CASE_USER_DATA"],"raw_scalar_hash_algorithm_id":"SHA256-UTF8-PARSED-YAML-SCALAR-V1","receipt_digest":"56975bd356a6a8bcf6a72148be8075991c46511bce2ac18c8a2fd4c969ad425f","receipt_status":"OFFLINE_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema_version":"stage2_s2_10_inline_prompt_projection_receipt.v1","source_projections":[{"inline_clause_projection_sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5","inline_wrapper":"stage_2_common_cache_prefix","parity":"PASS","source":{"path":"prompts/P00_system_and_safety_contract.md","sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5","size_bytes":7124},"source_clause_projection_sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5"},{"hybrid_supersession_required":true,"inline_clause_projection_sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","inline_wrapper":"s2_10_legal_resolution_static_prompt","parity":"PASS","source":{"path":"prompts/P10_legal_resolution_contract.md","sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","size_bytes":8712},"source_clause_projection_sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b"}],"validation":{"dynamic_item_tokens":["{{item.selected_legal_context_json}}","{{item.cluster_case_payload_json}}"],"normalized_clause_projection":"PASS","prompt_order":"PASS","static_item_token_count":0,"static_pii_scan":"PASS","wrapper_integrity":"PASS"}} +{"authoring_agent":{"path":"Stage_2_S2_10_v.1.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},"builder":{"path":"offline_build/build_s2_10_agent_projection.py","sha256":"4359364816c8a58eb414bafae8ccf24fc3766dc611d15f1aa09e48c8fedc8f88","size_bytes":61930},"canonicalization_algorithm_id":"S2_10-NFC-LF-RTRIM-CLAUSE-PROJECTION-V1","deployment_agent":{"byte_parity":"PASS","path":"agent_scripts/Stage_2_S2_10.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},"inline_scalars":[{"raw_scalar_sha256":"a3f001acfed764b38e34f12d72f13cdbcab59a2e44150dd1d71c5bf8ba1f7099","role":"system","size_bytes":7184,"wrapper":"stage_2_common_cache_prefix","yaml_pointer":"/Agent/Stages/0/map_reduce/map/tasks/0/prompts/0/content"},{"raw_scalar_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","role":"system","size_bytes":12261,"wrapper":"s2_10_legal_resolution_static_prompt","yaml_pointer":"/Agent/Stages/0/map_reduce/map/tasks/0/prompts/1/content"}],"message_order":["INLINE_COMMON_SYSTEM","INLINE_STATIC_LEGAL_SYSTEM","SELECTED_CONTEXT_SYSTEM_DATA","CLUSTER_CASE_USER_DATA"],"raw_scalar_hash_algorithm_id":"SHA256-UTF8-PARSED-YAML-SCALAR-V1","receipt_digest":"bfefc18c44815e75dd26a71209e74436e1ec59d858ebeaf704bc6d6e8c97eed9","receipt_status":"OFFLINE_PROJECTION_VERIFIED","runtime_admission_effect":"NONE","schema_version":"stage2_s2_10_inline_prompt_projection_receipt.v1","source_projections":[{"inline_clause_projection_sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5","inline_wrapper":"stage_2_common_cache_prefix","parity":"PASS","source":{"path":"prompts/P00_system_and_safety_contract.md","sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5","size_bytes":7124},"source_clause_projection_sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5"},{"hybrid_supersession_required":true,"inline_clause_projection_sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","inline_wrapper":"s2_10_legal_resolution_static_prompt","parity":"PASS","source":{"path":"prompts/P10_legal_resolution_contract.md","sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","size_bytes":8712},"source_clause_projection_sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b"}],"validation":{"dynamic_item_tokens":["{{item.selected_legal_context_json}}","{{item.cluster_case_payload_json}}"],"normalized_clause_projection":"PASS","prompt_order":"PASS","static_item_token_count":0,"static_pii_scan":"PASS","wrapper_integrity":"PASS"}} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_legal_review_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_legal_review_receipt.json index f7e901ef..6ff85afc 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_legal_review_receipt.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_legal_review_receipt.json @@ -1 +1 @@ -{"binding_sha256":"b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1","finding_ids":[],"inline_prompt_projection_sha256":"c0e388e56f52ee4fef2b825307ef41b204a80dd740cbc8f1562b30c3cd1d3696","receipt_digest":"53e1a0c3ba1d944fce9e2dfedb9c99bf4d37bb826add95a040e26ce44a06e8ec","review_id":"S2_10-HYBRID-LEGAL-REVIEW-PENDING","review_scope_digest":"bc5c06f51356ed69a41cd8ffd31ed168efbcecbacd191f9841532a00920e58ef","reviewer_role":"KOREAN_ATTORNEY","s2_10_release_sha256":"b2574efbc61aadf5882b273c18918d62ca3151b95d697bb35e539abd8594698c","schema_version":"stage2_s2_10_legal_review_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null,"status":"PENDING_KOREAN_LAWYER_REVIEW"} +{"binding_sha256":"90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95","finding_ids":[],"inline_prompt_projection_sha256":"4c95f3b3884900d1701f955e109f8927b9c5d818aa5c97623dba567faf7f61e9","receipt_digest":"de3f73d055a1c26c976f746911623f81373233dd50cd5781e7fa6ce462b89b96","review_id":"S2_10-HYBRID-LEGAL-REVIEW-PENDING","review_scope_digest":"1b327aba6aa5bdfd81bde57a76bc1d8255c512c88bace0579e2ccc98c300dfc1","reviewer_role":"KOREAN_ATTORNEY","s2_10_release_sha256":"284315158756d0391263dc808fcc3fff468b2e721d9d83ce7897b4666571a52f","schema_version":"stage2_s2_10_legal_review_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null,"status":"PENDING_KOREAN_LAWYER_REVIEW"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_model_benchmark_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_model_benchmark_receipt.json index 26ce584e..bddde8c2 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_model_benchmark_receipt.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_model_benchmark_receipt.json @@ -1 +1 @@ -{"benchmark_id":"S2_10-HYBRID-MODEL-BENCHMARK-PENDING","binding_sha256":"b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1","cache_telemetry_observed":null,"endpoint":"responses","latency_p95_ms":null,"model":"gpt-5.6-sol","observed_fixture_refs":[],"reasoning_effort":"xhigh","receipt_digest":"acd2043fc18695b61280b099cd388229951ccd59da6dfde024cce58fc8795fdb","s2_10_release_sha256":"b2574efbc61aadf5882b273c18918d62ca3151b95d697bb35e539abd8594698c","schema_pass_rate":null,"schema_version":"stage2_s2_10_model_benchmark_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null,"status":"PENDING_MODEL_BENCHMARK","usage_telemetry_observed":null,"verbosity":"medium"} +{"benchmark_id":"S2_10-HYBRID-MODEL-BENCHMARK-PENDING","binding_sha256":"90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95","cache_telemetry_observed":null,"endpoint":"responses","latency_p95_ms":null,"model":"gpt-5.6-sol","observed_fixture_refs":[],"reasoning_effort":"xhigh","receipt_digest":"8d93041ecebb0d68965b40ac1270ecbc94ebed6ed3f8462af52d7a781127b794","s2_10_release_sha256":"284315158756d0391263dc808fcc3fff468b2e721d9d83ce7897b4666571a52f","schema_pass_rate":null,"schema_version":"stage2_s2_10_model_benchmark_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null,"status":"PENDING_MODEL_BENCHMARK","usage_telemetry_observed":null,"verbosity":"medium"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_platform_adapter_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_platform_adapter_receipt.json index 16e710c4..bad2d83b 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_platform_adapter_receipt.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_platform_adapter_receipt.json @@ -1 +1 @@ -{"adapter_contract_version":"S2_10_NATIVE_RESULT_ADAPTER_V2","binding_sha256":"b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1","capabilities":[{"activation_binding_ref":null,"capability_id":"HOST_ATOMIC_SINGLE_FLIGHT_CAS_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"AGENTBACKEND_MAP_SOURCE_ITEMS_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"AGENTBACKEND_NO_REDUCE_RESULT_ADAPTER_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_STRICT_SCHEMA_AND_REF_VALIDATOR_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_DETERMINISTIC_ID_AND_IMMUTABLE_PERSIST_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_ITEM_RETRY_CONTROLLER_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"}],"overall_status":"PENDING_EXTERNAL_PLATFORM_BINDING","parent_stage2_release_sha256":"0f21af3ddca538d9b6a5853dac23222c0b632d0e573de371a8de2709e691699d","receipt_digest":"b45a1cf57d428bf63159e8de481c0a313a5457dadb667769ccdf290806d41062","receipt_id":"S2_10-HYBRID-PLATFORM-ADAPTER-PENDING","s2_10_release_sha256":"b2574efbc61aadf5882b273c18918d62ca3151b95d697bb35e539abd8594698c","schema_version":"stage2_s2_10_platform_adapter_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null} +{"adapter_contract_version":"S2_10_NATIVE_RESULT_ADAPTER_V2","binding_sha256":"90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95","capabilities":[{"activation_binding_ref":null,"capability_id":"HOST_ATOMIC_SINGLE_FLIGHT_CAS_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"AGENTBACKEND_MAP_SOURCE_ITEMS_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"AGENTBACKEND_NO_REDUCE_RESULT_ADAPTER_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_STRICT_SCHEMA_AND_REF_VALIDATOR_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_DETERMINISTIC_ID_AND_IMMUTABLE_PERSIST_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"},{"activation_binding_ref":null,"capability_id":"S2_10_ITEM_RETRY_CONTROLLER_V1","failure_reason_codes":["PENDING_EXTERNAL_PLATFORM_BINDING"],"handler_digest":null,"handler_version":null,"implementation_ref":null,"live_evidence_refs":[],"status":"PENDING_EXTERNAL_PLATFORM_BINDING"}],"overall_status":"PENDING_EXTERNAL_PLATFORM_BINDING","parent_stage2_release_sha256":"70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92","receipt_digest":"d11458da3cd0d5d85f202ec1ee275b95b8dbe34c588e7580bd21fef211fbd050","receipt_id":"S2_10-HYBRID-PLATFORM-ADAPTER-PENDING","s2_10_release_sha256":"284315158756d0391263dc808fcc3fff468b2e721d9d83ce7897b4666571a52f","schema_version":"stage2_s2_10_platform_adapter_receipt.v2","signature_ref":null,"signed_at":null,"signed_by":null} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_release.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_release.json index c27ccea2..8bc544db 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_release.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_10_release.json @@ -1 +1 @@ -{"admission_status":{"legal_review":"PENDING_KOREAN_LAWYER_REVIEW","model_benchmark":"PENDING_MODEL_BENCHMARK","platform_adapter":"PENDING_EXTERNAL_PLATFORM_BINDING","production_execution":"BLOCKED","release_bound_canary":"SEPARATE_AUTHORIZATION_REQUIRED"},"authorization_status":"OFFLINE_VALIDATION_ONLY","constitution":{"deterministic_task_count":0,"dispatch_item_schema_version":"stage2_s2_10_dispatch_item.v2","llm_task_count":1,"prompt_contract_version":"S2_10_HYBRID_PROMPT_V1","raw_model_output_schema_version":"stage2_s2_10_model_output.v2","reduce_task_count":0,"runtime_python_asset_count":0,"tool_task_count":0},"external_admission_evidence":[{"path":"manifest/s2_10_platform_adapter_receipt.json","reason":"receipt attests this child release; reverse sealing would create a cycle","sealed_by_child_release":false},{"path":"manifest/s2_10_model_benchmark_receipt.json","reason":"generated only after a release-bound live canary","sealed_by_child_release":false},{"path":"manifest/s2_10_legal_review_receipt.json","reason":"generated only after release-bound Korean lawyer review","sealed_by_child_release":false}],"legacy_stage2_v0_v3_runtime_dependency_count":0,"model_binding":{"binding_status":"PENDING_MODEL_BENCHMARK","endpoint":"responses","model_id":"gpt-5.6-sol","provider":"openai","reasoning_effort":"xhigh","verbosity":"medium"},"parent_stage2_release_ref":{"declared_release_digest":"adc76cb9869183ffbda99ad5400f9901bd6808605229de78ba7247a70d153ebd","declared_release_id":"STAGE2-CLEAN-DEV-DRAFT-S2_00-HYBRID-HANDOFF-2026-08-30","declared_release_status":"DRAFT_NOT_EXECUTABLE","path":"manifest/stage2_release.json","sha256":"0f21af3ddca538d9b6a5853dac23222c0b632d0e573de371a8de2709e691699d"},"producer_contract":{"material":{"algorithm_id":"S2_10-PRODUCER-CONTRACT-PROJECTION-V1","claim_option_id_mint_algorithm_id":"S2_10-CLAIM-OPTION-ID-V1","local_relation_rewrite_algorithm_id":"S2_10-TWO-PASS-LOCAL-REF-REWRITE-V1","native_adapter_capability_projection_sha256":"3f08caa2ad872802267fc333069ea2436a9f23ec3d312241d8ed26e3fbbf0525","raw_model_output_schema_closure_algorithm_id":"S2_10-RAW-MODEL-SCHEMA-CLOSURE-BY-FULL-FILE-SHA256-V1","raw_model_output_schema_projection_sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee","strict_validator_algorithm_id":"S2_10-OFFLINE-NATIVE-ADAPTER-ORACLE-V2","strict_validator_sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b"},"s2_10_producer_contract_digest":"950da7b30a9799ad457118c2342854bf5cdc15990f382e1fd42c0e6cbc771dc2","self_referential_digest_fields_excluded":true,"whole_binding_hash_in_material":false},"prompt_binding":{"inline_common_prompt_sha256":"a3f001acfed764b38e34f12d72f13cdbcab59a2e44150dd1d71c5bf8ba1f7099","inline_static_prompt_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","projection_receipt_sha256":"c0e388e56f52ee4fef2b825307ef41b204a80dd740cbc8f1562b30c3cd1d3696","runtime_external_prompt_read_allowed":false},"release_class":"CHILD_OFFLINE_CONTRACT_RELEASE","release_digest":"7f6a9ada26c5b11525fd45c3a955fa491cd6e1f66ae491596d1505ba6a749c53","release_digest_contract":{"algorithm_id":"S2_10-CHILD-RELEASE-DIGEST-V2","canonicalization_algorithm_id":"STAGE2-CANONICAL-JSON-V1","scope":"ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_RELEASE_DIGEST"},"release_id":"STAGE2-S2_10-HYBRID-OFFLINE-CONTRACT-2026-08-30","release_status":"OFFLINE_CONTRACT_COMPLETE_LIVE_ADMISSION_PENDING","schema_version":"stage2_s2_10_release.v2","sealed_asset_count":63,"sealed_assets":[{"path":"Stage_2_S2_10_v.1.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},{"path":"agent_scripts/Stage_2_S2_10.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},{"path":"workflows/S2_10_domain_relief_resolution_map.yml","sha256":"f0fba48f1760cf4e233d7d698fd19090f96ffb89cabe4b2e2cae9af7c616be0f","size_bytes":15320},{"path":"schemas/s2_10.schema.json","sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee","size_bytes":82703},{"path":"deployment/stage2_s2_10_llm_binding.yml","sha256":"b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1","size_bytes":7287},{"path":"manifest/s2_10_inline_prompt_projection_receipt.json","sha256":"c0e388e56f52ee4fef2b825307ef41b204a80dd740cbc8f1562b30c3cd1d3696","size_bytes":2659},{"path":"offline_build/build_s2_10_agent_projection.py","sha256":"f89acf6ddb307515006ed904865c05df1d38051bacb5f13e9f928142f8d72020","size_bytes":56552},{"path":"offline_build/build_s2_10_agent_projection.txt","sha256":"f89acf6ddb307515006ed904865c05df1d38051bacb5f13e9f928142f8d72020","size_bytes":56552},{"path":"offline_build/validate_s2_10_contract.py","sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b","size_bytes":63811},{"path":"offline_build/validate_s2_10_contract.txt","sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b","size_bytes":63811},{"path":"prompts/P00_system_and_safety_contract.md","sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5","size_bytes":7124},{"path":"prompts/P10_legal_resolution_contract.md","sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","size_bytes":8712},{"path":"registry/cache/prompt_cache_policy.yml","sha256":"b459cc271e2f51d783cdd89872d24dc7635ac237e76cd6ad03253569190b7b90","size_bytes":5887},{"path":"schemas/context.schema.json","sha256":"29073ffa847d74304228d61306503c0c3798da7db8b5717b1ad6d5b2da98aa92","size_bytes":39054},{"path":"schemas/review_status.schema.json","sha256":"edcdd127e648c6b045abb852146a63f19936d300a350023b8987700afa05fde7","size_bytes":11103},{"path":"registry/authority/authority_registry.yml","sha256":"b4663610e18098cf650fbd5846f18dcbc96074b3012c68f03f67402509c7d142","size_bytes":1517},{"path":"manifest/authority_release.json","sha256":"cc683958377eea44756de6e9c74fc925da66dd60cc84363f5bec36584f5b2967","size_bytes":1068},{"path":"law_values/general_law_values.yml","sha256":"45ad98e9e93090fbc5e5c7037f404bd86ff89b73a4aaf8c3449d226bd2aacd1c","size_bytes":843},{"path":"registry/substantive/E-01_juristic_act_validity.yml","sha256":"0a0d2676f96be321a7bb974b8e4fb1981ab43773f271c99dc86a70cb17d27c99","size_bytes":3808},{"path":"registry/substantive/E-02_contract_money_claim.yml","sha256":"08e252bccc718ccc44a258bc540afc2567e509239399f4017550df32bac97824","size_bytes":3784},{"path":"registry/substantive/E-03_parties_liability_succession.yml","sha256":"4e841d5f640b06a1b52060b82dda39605369cbe26d81655b7918673437e4519c","size_bytes":3799},{"path":"registry/substantive/E-04_unjust_enrichment.yml","sha256":"31a5966d4c14940e810f870bf4382f1be642f7716ebaaa35193ef32e9b9d95c7","size_bytes":3725},{"path":"registry/substantive/E-05_tort_general.yml","sha256":"fe6ea92fc10c4196e0ac83b990bf2606e24024030e7b0002fecdae9f8f50221a","size_bytes":3694},{"path":"registry/substantive/E-06_professional_liability.yml","sha256":"efa7a4f6cda90c04a06558a9d0e1efe13b25169d55d03e36d55d72fcdd096a15","size_bytes":3724},{"path":"registry/substantive/E-07_construction_defect.yml","sha256":"78c76cb0485a4acf741109aee05f634f51c3a6904044c613080b21c3978a09f9","size_bytes":3710},{"path":"registry/substantive/E-08_lease_deposit.yml","sha256":"dbe1886671edff49b8b0251eed567d1b3e4e5d748456434262286ca7acf990db","size_bytes":3678},{"path":"registry/substantive/E-09_registry_transfer_claims.yml","sha256":"d18946322a8cb528146ac0aa0a2880c41ac2056671fd237b928341ceb8561002","size_bytes":3800},{"path":"registry/substantive/E-10_secured_registry.yml","sha256":"5819c26ec8478e12a12e340c93a9d23cc410833e1192c2315a1986c9d48a1eba","size_bytes":3757},{"path":"registry/substantive/E-11_possession_vindication.yml","sha256":"5e6d6460be4ec0f2ba0096305b9ae39f5f430d107337d0d7e07aac1a9d599aaf","size_bytes":3724},{"path":"registry/substantive/E-12_co_ownership_boundary.yml","sha256":"62cfbf6a6c5c5edc8b912e348be1c4d4f7dd4c20c722e57e5554058f0caafb4a","size_bytes":3712},{"path":"registry/substantive/E-13_creditor_preservation.yml","sha256":"c60e6d67c7e3985d1f6ce44027726282f931934e3e66b44f06ccd26845ca2f45","size_bytes":3826},{"path":"registry/substantive/E-14_execution_linked_claims.yml","sha256":"45d4b3ab46e1c8834c3b3eec72a28d05e39cb78a9ec2c2de07b5e55e81aeebd7","size_bytes":3787},{"path":"registry/substantive/E-15_succession_family_property.yml","sha256":"f691b67295ad8e634b34519cf71156caf6eaf73cef54577615ab79462f8aa766","size_bytes":3763},{"path":"registry/substantive/E-16_negotiable_instruments.yml","sha256":"7720d83b64e7a44720788f6ff3c7cc8ceaf5195377a1e61b52de2144989a1194","size_bytes":3738},{"path":"registry/substantive/E-17_labor_wage_claims.yml","sha256":"168ce5e41104937cf7ab946944f060b82cfe16066425897f07bb9e837b8689ea","size_bytes":3728},{"path":"registry/substantive/E-18_org_resolution_status.yml","sha256":"59cf689f886dfaf4ad00dbe6142f5b11c37d0062660f7467aa43427462aa075f","size_bytes":3763},{"path":"registry/substantive/E-19_insurance_claims.yml","sha256":"0fb4793446c155e7a0b4469c9b8c1be1323112eef55ca4c495307c1af56e754c","size_bytes":3685},{"path":"registry/substantive/E-20_ip_claims.yml","sha256":"fe07f20d149a2c4deac2e46de299cd78f0bf829f58c615a86c45f542d30804e1","size_bytes":3654},{"path":"registry/substantive/E-21_media_personality_rights.yml","sha256":"951260bbe8ef116d682f49f3679106c7144ca68f8078f5c10b3459667c1f5b2c","size_bytes":3803},{"path":"registry/substantive/EC-00_contract_general.yml","sha256":"a4f7abd9957b3a9e58d05dd559c3c869bf96fd88ea18968a363c6e606e3c0aa8","size_bytes":3789},{"path":"profiles/crosscut/E-00_residual_unrouted.yml","sha256":"639e507147a6132f9e41ccd0b00d1b5450d6a9629991629b173d5bcaf5b6df39","size_bytes":3752},{"path":"profiles/crosscut/X1_notice_lifecycle.yml","sha256":"9af8b5b5f3a8196a10308d1ee34e0788d9adeac7a99e142e449490d14316faef","size_bytes":3693},{"path":"profiles/crosscut/X2_asset_identity_lineage.yml","sha256":"6c1e437944b3a62b60ee7acad5ec9177fefac2991fe970338acc27efcc8a13ad","size_bytes":3742},{"path":"profiles/crosscut/X3_procedure_standing_relief.yml","sha256":"f3b794bef82566e569232ba02c63ae148099143d8d1ab84de1c4fb76105e65df","size_bytes":3788},{"path":"profiles/crosscut/X4_response_admission_defense.yml","sha256":"57851ed55b3351a8139df29224d74b7cb8427708bbf6de00ecd27c44089df32e","size_bytes":3793},{"path":"profiles/special_law/SL-AUTO_motor_vehicle.yml","sha256":"7234a7ab890f242e6b7aa811528e34c0c85779007cffbf135da0bfff36d61d91","size_bytes":3762},{"path":"profiles/special_law/SL-COMMERCIAL_LEASE.yml","sha256":"5aebed9ab56bc00eb7e0511f9fe5db8c3a42f1e81b28d58c38d487aa91cbab3e","size_bytes":3769},{"path":"profiles/special_law/SL-CONSUMER_CONTRACT.yml","sha256":"afd8bbba102614d8d08f2e7a5384f6ca24e1309c1f52feb13906613f2ced51b7","size_bytes":3797},{"path":"profiles/special_law/SL-INDUSTRIAL_ACCIDENT.yml","sha256":"10d78b8031df7d89f835afc9ca60fab36648e561c729780204c80885f4dd097f","size_bytes":3750},{"path":"profiles/special_law/SL-IP-COPYRIGHT.yml","sha256":"d4349677f68346f79709ea23a05957262f6426f976388bba45f05c617035eb78","size_bytes":3728},{"path":"profiles/special_law/SL-IP-OTHER.yml","sha256":"fc3e2772252a3a90571663ebcfbc8e18dbf526977f4a3d77c4705ef8aa8dd6a8","size_bytes":3746},{"path":"profiles/special_law/SL-IP-PATENT.yml","sha256":"d0651a8a50d0b18d25931063314ecec284988c5a18fab68d08a317d725bb3066","size_bytes":3709},{"path":"profiles/special_law/SL-LABOR.yml","sha256":"6b2542bb64f4c1d164b1ccd2ce5099fad29bcea4333d9e34bd0fb1667b9ced4a","size_bytes":3694},{"path":"profiles/special_law/SL-MEDIA.yml","sha256":"e0d5d287befd1578a06c64a265d5c4426af5772b6764018f6a15f00aac3b498c","size_bytes":3705},{"path":"profiles/special_law/SL-PRODUCT_LIABILITY.yml","sha256":"1d5ee8eddcf005fdb33df58f591a8948aa3e891a3a5656d919394ff0b39eea53","size_bytes":3737},{"path":"profiles/special_law/SL-RESIDENTIAL_LEASE.yml","sha256":"3e2efd1b3c0524a305eafedc5ec91e05be989a6e55c1501ed161a9b8f8151fd7","size_bytes":3739},{"path":"profiles/special_law/SL-STATE_LIABILITY.yml","sha256":"5b2fe6d57b918384a619efbd04360ddfaf7720d384136f69d359ccb0b6c853d3","size_bytes":3747},{"path":"profiles/special_law/SL-TRANSPORT_MARITIME.yml","sha256":"6e87a77c25f8c358f1d7d35ca165b1229c2cb422a440c91768ad80e520d35456","size_bytes":3835},{"path":"profiles/overlays/ACTIO-DEFENSE-MAP.yml","sha256":"05348c493e6046a3e5c55f9b516c0e862fe5aa753e1b2fc71624bb2b11f0c191","size_bytes":3810},{"path":"profiles/overlays/ACTIO-ENCUMBERED-TRANSFER.yml","sha256":"fa950393a572829ba7e296c20cd08ff19a063b0f8441f950130fd677b1e38179","size_bytes":3828},{"path":"profiles/overlays/ACTIO-MORTGAGE-CREATION.yml","sha256":"e727690ee23883e011d49eba01cfd87abc5482641c2bda519c0a39518e1769ee","size_bytes":3771},{"path":"profiles/overlays/ACTIO-MORTGAGE.yml","sha256":"7e584f7b4e4d19132e9575071f87d05965fb77fa2ca870911fe9974a7c511c4d","size_bytes":3739},{"path":"profiles/overlays/ACTIO-PRESERVED-CLAIM-BUNDLE.yml","sha256":"fbd99f029438c1c278f93d88d18a703db72e78f20c7049f344e5748c108f94c5","size_bytes":3816}]} +{"admission_status":{"legal_review":"PENDING_KOREAN_LAWYER_REVIEW","model_benchmark":"PENDING_MODEL_BENCHMARK","platform_adapter":"PENDING_EXTERNAL_PLATFORM_BINDING","production_execution":"BLOCKED","release_bound_canary":"SEPARATE_AUTHORIZATION_REQUIRED"},"authorization_status":"OFFLINE_VALIDATION_ONLY","constitution":{"deterministic_task_count":0,"dispatch_item_schema_version":"stage2_s2_10_dispatch_item.v2","llm_task_count":1,"prompt_contract_version":"S2_10_HYBRID_PROMPT_V1","raw_model_output_schema_version":"stage2_s2_10_model_output.v2","reduce_task_count":0,"runtime_python_asset_count":0,"tool_task_count":0},"external_admission_evidence":[{"path":"manifest/s2_10_platform_adapter_receipt.json","reason":"receipt attests this child release; reverse sealing would create a cycle","sealed_by_child_release":false},{"path":"manifest/s2_10_model_benchmark_receipt.json","reason":"generated only after a release-bound live canary","sealed_by_child_release":false},{"path":"manifest/s2_10_legal_review_receipt.json","reason":"generated only after release-bound Korean lawyer review","sealed_by_child_release":false}],"legacy_stage2_v0_v3_runtime_dependency_count":0,"model_binding":{"binding_status":"PENDING_MODEL_BENCHMARK","endpoint":"responses","model_id":"gpt-5.6-sol","provider":"openai","reasoning_effort":"xhigh","verbosity":"medium"},"parent_stage2_release_ref":{"declared_release_digest":"0fec6e67baedd7dcc36591348facd769a9c5a8c8e7052bbc395bdb6d2b96f6bc","declared_release_id":"STAGE2-CLEAN-DEV-DRAFT-S2_00-HYBRID-HANDOFF-2026-08-30","declared_release_status":"DRAFT_NOT_EXECUTABLE","path":"manifest/stage2_release.json","sha256":"70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92"},"producer_contract":{"material":{"algorithm_id":"S2_10-PRODUCER-CONTRACT-PROJECTION-V1","claim_option_id_mint_algorithm_id":"S2_10-CLAIM-OPTION-ID-V1","local_relation_rewrite_algorithm_id":"S2_10-TWO-PASS-LOCAL-REF-REWRITE-V1","native_adapter_capability_projection_sha256":"3f08caa2ad872802267fc333069ea2436a9f23ec3d312241d8ed26e3fbbf0525","raw_model_output_schema_closure_algorithm_id":"S2_10-RAW-MODEL-SCHEMA-CLOSURE-BY-FULL-FILE-SHA256-V1","raw_model_output_schema_projection_sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee","strict_validator_algorithm_id":"S2_10-OFFLINE-NATIVE-ADAPTER-ORACLE-V2","strict_validator_sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b"},"s2_10_producer_contract_digest":"950da7b30a9799ad457118c2342854bf5cdc15990f382e1fd42c0e6cbc771dc2","self_referential_digest_fields_excluded":true,"whole_binding_hash_in_material":false},"prompt_binding":{"inline_common_prompt_sha256":"a3f001acfed764b38e34f12d72f13cdbcab59a2e44150dd1d71c5bf8ba1f7099","inline_static_prompt_sha256":"894c30bedaa5f230aed117e81ef4cc06cac832928a1e29cd3ee8804cfd4dba1f","projection_receipt_sha256":"4c95f3b3884900d1701f955e109f8927b9c5d818aa5c97623dba567faf7f61e9","runtime_external_prompt_read_allowed":false},"release_class":"CHILD_OFFLINE_CONTRACT_RELEASE","release_digest":"1ad0ab3edd035d7ecafa3d148e9ae5fb7980326e4291a88caffcd04099845f5c","release_digest_contract":{"algorithm_id":"S2_10-CHILD-RELEASE-DIGEST-V2","canonicalization_algorithm_id":"STAGE2-CANONICAL-JSON-V1","scope":"ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_RELEASE_DIGEST"},"release_id":"STAGE2-S2_10-HYBRID-OFFLINE-CONTRACT-2026-08-30","release_status":"OFFLINE_CONTRACT_COMPLETE_LIVE_ADMISSION_PENDING","schema_version":"stage2_s2_10_release.v2","sealed_asset_count":63,"sealed_assets":[{"path":"Stage_2_S2_10_v.1.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},{"path":"agent_scripts/Stage_2_S2_10.yml","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","size_bytes":30164},{"path":"workflows/S2_10_domain_relief_resolution_map.yml","sha256":"f0fba48f1760cf4e233d7d698fd19090f96ffb89cabe4b2e2cae9af7c616be0f","size_bytes":15320},{"path":"schemas/s2_10.schema.json","sha256":"5233afde9ddb3c5868b9101816e8b3830a90b1399e3e5e89d9d7c7bf5beacaee","size_bytes":82703},{"path":"deployment/stage2_s2_10_llm_binding.yml","sha256":"90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95","size_bytes":7287},{"path":"manifest/s2_10_inline_prompt_projection_receipt.json","sha256":"4c95f3b3884900d1701f955e109f8927b9c5d818aa5c97623dba567faf7f61e9","size_bytes":2659},{"path":"offline_build/build_s2_10_agent_projection.py","sha256":"4359364816c8a58eb414bafae8ccf24fc3766dc611d15f1aa09e48c8fedc8f88","size_bytes":61930},{"path":"offline_build/build_s2_10_agent_projection.txt","sha256":"4359364816c8a58eb414bafae8ccf24fc3766dc611d15f1aa09e48c8fedc8f88","size_bytes":61930},{"path":"offline_build/validate_s2_10_contract.py","sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b","size_bytes":63811},{"path":"offline_build/validate_s2_10_contract.txt","sha256":"17996fb280435146ec0045a69c101c2d2088af27391cafc443a698eca70d5b0b","size_bytes":63811},{"path":"prompts/P00_system_and_safety_contract.md","sha256":"df6e88cf889c739fbfc6829b711c315e4d14ba5dd2a8bb46e1f30ef88b3a7ed5","size_bytes":7124},{"path":"prompts/P10_legal_resolution_contract.md","sha256":"cafaab3991b8bd4ef3c95e55ce4c0649794c6eed9479d46d8c7758a4b823569b","size_bytes":8712},{"path":"registry/cache/prompt_cache_policy.yml","sha256":"b459cc271e2f51d783cdd89872d24dc7635ac237e76cd6ad03253569190b7b90","size_bytes":5887},{"path":"schemas/context.schema.json","sha256":"29073ffa847d74304228d61306503c0c3798da7db8b5717b1ad6d5b2da98aa92","size_bytes":39054},{"path":"schemas/review_status.schema.json","sha256":"13f6f1f06946d4074f3d85959f82763e489b2f87cd7bfadb14b594d6223b02e9","size_bytes":11169},{"path":"registry/authority/authority_registry.yml","sha256":"b4663610e18098cf650fbd5846f18dcbc96074b3012c68f03f67402509c7d142","size_bytes":1517},{"path":"manifest/authority_release.json","sha256":"cc683958377eea44756de6e9c74fc925da66dd60cc84363f5bec36584f5b2967","size_bytes":1068},{"path":"law_values/general_law_values.yml","sha256":"45ad98e9e93090fbc5e5c7037f404bd86ff89b73a4aaf8c3449d226bd2aacd1c","size_bytes":843},{"path":"registry/substantive/E-01_juristic_act_validity.yml","sha256":"0a0d2676f96be321a7bb974b8e4fb1981ab43773f271c99dc86a70cb17d27c99","size_bytes":3808},{"path":"registry/substantive/E-02_contract_money_claim.yml","sha256":"08e252bccc718ccc44a258bc540afc2567e509239399f4017550df32bac97824","size_bytes":3784},{"path":"registry/substantive/E-03_parties_liability_succession.yml","sha256":"4e841d5f640b06a1b52060b82dda39605369cbe26d81655b7918673437e4519c","size_bytes":3799},{"path":"registry/substantive/E-04_unjust_enrichment.yml","sha256":"31a5966d4c14940e810f870bf4382f1be642f7716ebaaa35193ef32e9b9d95c7","size_bytes":3725},{"path":"registry/substantive/E-05_tort_general.yml","sha256":"fe6ea92fc10c4196e0ac83b990bf2606e24024030e7b0002fecdae9f8f50221a","size_bytes":3694},{"path":"registry/substantive/E-06_professional_liability.yml","sha256":"efa7a4f6cda90c04a06558a9d0e1efe13b25169d55d03e36d55d72fcdd096a15","size_bytes":3724},{"path":"registry/substantive/E-07_construction_defect.yml","sha256":"78c76cb0485a4acf741109aee05f634f51c3a6904044c613080b21c3978a09f9","size_bytes":3710},{"path":"registry/substantive/E-08_lease_deposit.yml","sha256":"dbe1886671edff49b8b0251eed567d1b3e4e5d748456434262286ca7acf990db","size_bytes":3678},{"path":"registry/substantive/E-09_registry_transfer_claims.yml","sha256":"d18946322a8cb528146ac0aa0a2880c41ac2056671fd237b928341ceb8561002","size_bytes":3800},{"path":"registry/substantive/E-10_secured_registry.yml","sha256":"5819c26ec8478e12a12e340c93a9d23cc410833e1192c2315a1986c9d48a1eba","size_bytes":3757},{"path":"registry/substantive/E-11_possession_vindication.yml","sha256":"5e6d6460be4ec0f2ba0096305b9ae39f5f430d107337d0d7e07aac1a9d599aaf","size_bytes":3724},{"path":"registry/substantive/E-12_co_ownership_boundary.yml","sha256":"62cfbf6a6c5c5edc8b912e348be1c4d4f7dd4c20c722e57e5554058f0caafb4a","size_bytes":3712},{"path":"registry/substantive/E-13_creditor_preservation.yml","sha256":"c60e6d67c7e3985d1f6ce44027726282f931934e3e66b44f06ccd26845ca2f45","size_bytes":3826},{"path":"registry/substantive/E-14_execution_linked_claims.yml","sha256":"45d4b3ab46e1c8834c3b3eec72a28d05e39cb78a9ec2c2de07b5e55e81aeebd7","size_bytes":3787},{"path":"registry/substantive/E-15_succession_family_property.yml","sha256":"f691b67295ad8e634b34519cf71156caf6eaf73cef54577615ab79462f8aa766","size_bytes":3763},{"path":"registry/substantive/E-16_negotiable_instruments.yml","sha256":"7720d83b64e7a44720788f6ff3c7cc8ceaf5195377a1e61b52de2144989a1194","size_bytes":3738},{"path":"registry/substantive/E-17_labor_wage_claims.yml","sha256":"168ce5e41104937cf7ab946944f060b82cfe16066425897f07bb9e837b8689ea","size_bytes":3728},{"path":"registry/substantive/E-18_org_resolution_status.yml","sha256":"59cf689f886dfaf4ad00dbe6142f5b11c37d0062660f7467aa43427462aa075f","size_bytes":3763},{"path":"registry/substantive/E-19_insurance_claims.yml","sha256":"0fb4793446c155e7a0b4469c9b8c1be1323112eef55ca4c495307c1af56e754c","size_bytes":3685},{"path":"registry/substantive/E-20_ip_claims.yml","sha256":"fe07f20d149a2c4deac2e46de299cd78f0bf829f58c615a86c45f542d30804e1","size_bytes":3654},{"path":"registry/substantive/E-21_media_personality_rights.yml","sha256":"951260bbe8ef116d682f49f3679106c7144ca68f8078f5c10b3459667c1f5b2c","size_bytes":3803},{"path":"registry/substantive/EC-00_contract_general.yml","sha256":"a4f7abd9957b3a9e58d05dd559c3c869bf96fd88ea18968a363c6e606e3c0aa8","size_bytes":3789},{"path":"profiles/crosscut/E-00_residual_unrouted.yml","sha256":"639e507147a6132f9e41ccd0b00d1b5450d6a9629991629b173d5bcaf5b6df39","size_bytes":3752},{"path":"profiles/crosscut/X1_notice_lifecycle.yml","sha256":"9af8b5b5f3a8196a10308d1ee34e0788d9adeac7a99e142e449490d14316faef","size_bytes":3693},{"path":"profiles/crosscut/X2_asset_identity_lineage.yml","sha256":"6c1e437944b3a62b60ee7acad5ec9177fefac2991fe970338acc27efcc8a13ad","size_bytes":3742},{"path":"profiles/crosscut/X3_procedure_standing_relief.yml","sha256":"f3b794bef82566e569232ba02c63ae148099143d8d1ab84de1c4fb76105e65df","size_bytes":3788},{"path":"profiles/crosscut/X4_response_admission_defense.yml","sha256":"57851ed55b3351a8139df29224d74b7cb8427708bbf6de00ecd27c44089df32e","size_bytes":3793},{"path":"profiles/special_law/SL-AUTO_motor_vehicle.yml","sha256":"7234a7ab890f242e6b7aa811528e34c0c85779007cffbf135da0bfff36d61d91","size_bytes":3762},{"path":"profiles/special_law/SL-COMMERCIAL_LEASE.yml","sha256":"5aebed9ab56bc00eb7e0511f9fe5db8c3a42f1e81b28d58c38d487aa91cbab3e","size_bytes":3769},{"path":"profiles/special_law/SL-CONSUMER_CONTRACT.yml","sha256":"afd8bbba102614d8d08f2e7a5384f6ca24e1309c1f52feb13906613f2ced51b7","size_bytes":3797},{"path":"profiles/special_law/SL-INDUSTRIAL_ACCIDENT.yml","sha256":"10d78b8031df7d89f835afc9ca60fab36648e561c729780204c80885f4dd097f","size_bytes":3750},{"path":"profiles/special_law/SL-IP-COPYRIGHT.yml","sha256":"d4349677f68346f79709ea23a05957262f6426f976388bba45f05c617035eb78","size_bytes":3728},{"path":"profiles/special_law/SL-IP-OTHER.yml","sha256":"fc3e2772252a3a90571663ebcfbc8e18dbf526977f4a3d77c4705ef8aa8dd6a8","size_bytes":3746},{"path":"profiles/special_law/SL-IP-PATENT.yml","sha256":"d0651a8a50d0b18d25931063314ecec284988c5a18fab68d08a317d725bb3066","size_bytes":3709},{"path":"profiles/special_law/SL-LABOR.yml","sha256":"6b2542bb64f4c1d164b1ccd2ce5099fad29bcea4333d9e34bd0fb1667b9ced4a","size_bytes":3694},{"path":"profiles/special_law/SL-MEDIA.yml","sha256":"e0d5d287befd1578a06c64a265d5c4426af5772b6764018f6a15f00aac3b498c","size_bytes":3705},{"path":"profiles/special_law/SL-PRODUCT_LIABILITY.yml","sha256":"1d5ee8eddcf005fdb33df58f591a8948aa3e891a3a5656d919394ff0b39eea53","size_bytes":3737},{"path":"profiles/special_law/SL-RESIDENTIAL_LEASE.yml","sha256":"3e2efd1b3c0524a305eafedc5ec91e05be989a6e55c1501ed161a9b8f8151fd7","size_bytes":3739},{"path":"profiles/special_law/SL-STATE_LIABILITY.yml","sha256":"5b2fe6d57b918384a619efbd04360ddfaf7720d384136f69d359ccb0b6c853d3","size_bytes":3747},{"path":"profiles/special_law/SL-TRANSPORT_MARITIME.yml","sha256":"6e87a77c25f8c358f1d7d35ca165b1229c2cb422a440c91768ad80e520d35456","size_bytes":3835},{"path":"profiles/overlays/ACTIO-DEFENSE-MAP.yml","sha256":"05348c493e6046a3e5c55f9b516c0e862fe5aa753e1b2fc71624bb2b11f0c191","size_bytes":3810},{"path":"profiles/overlays/ACTIO-ENCUMBERED-TRANSFER.yml","sha256":"fa950393a572829ba7e296c20cd08ff19a063b0f8441f950130fd677b1e38179","size_bytes":3828},{"path":"profiles/overlays/ACTIO-MORTGAGE-CREATION.yml","sha256":"e727690ee23883e011d49eba01cfd87abc5482641c2bda519c0a39518e1769ee","size_bytes":3771},{"path":"profiles/overlays/ACTIO-MORTGAGE.yml","sha256":"7e584f7b4e4d19132e9575071f87d05965fb77fa2ca870911fe9974a7c511c4d","size_bytes":3739},{"path":"profiles/overlays/ACTIO-PRESERVED-CLAIM-BUNDLE.yml","sha256":"fbd99f029438c1c278f93d88d18a703db72e78f20c7049f344e5748c108f94c5","size_bytes":3816}]} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_20_inline_code_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_20_inline_code_receipt.json new file mode 100644 index 00000000..90d3c5f8 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_20_inline_code_receipt.json @@ -0,0 +1 @@ +{"authoring":{"path":"Stage_2_S2_20.yml","sha256":"4d7642bb8b63dc37fc0ca7808676c816ba43623384b0f745f9f4af17f0f8553b","size_bytes":254364},"canonical_code":{"ast_status":"PASS","code_sha256":"140a83b71aa1007dd275c91a0aa8c837bd2d72a41136ae5bfa0c007da8dd701e","code_size_bytes":199933,"compile_status":"PASS","encoding":"UTF-8","endpoint_literals":["http://mcp-localdocs:8012/mcp","https://weaviate.eroomai.com/mcp"],"expected_parent_stage2_release_sha256":"70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92","external_python_source_ref_count":0,"extraction_transform":"NONE","forbidden_dynamic_call_count":0,"forbidden_import_count":0,"imports":["__future__","base64","binascii","concurrent","contextlib","datetime","decimal","hashlib","httpx","io","itertools","json","pathlib","re","sys","typing","unicodedata"],"plaintext_secret_count":0,"yaml_pointer":"/Agent/Stages/0/tasks/0/parameters/code"},"deployment_projection":{"path":"Default_Agent/Stage_2_Clean/agent_scripts/Stage_2_S2_20.yml","sha256":"4d7642bb8b63dc37fc0ca7808676c816ba43623384b0f745f9f4af17f0f8553b","size_bytes":254364},"full_code_mirrors":["Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.py","Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.txt"],"parity_status":"PASS","schema_version":"stage2_s2_20_inline_code_receipt.v1","task_contract":{"agent":{"name":"Stage_2_S2_20","version":"1.0.0"},"authoring_rewritten":false,"canonical_task_semantics_sha256":"33367b7091a5d1bebe33c1159f00fa46f8766cee52106491d7d6c7e2c9d472b6","code_mirrors_byte_identical":true,"exactly_one_code_executor_run_code":true,"exactly_one_stage":true,"exactly_one_task":true,"expected_parent_stage2_release_sha256":"70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92","internal_dag":"(C20||C21)->C25->C26->(C22||(C27->C28->C29))->C30->C35","mcp_servers":{"code-executor":{"type":"streamable-http","url":"https://code-executor.mcp.eroomai.com/mcp"},"localdocs":{"type":"streamable-http","url":"http://mcp-localdocs:8012/mcp"}},"projection_byte_identical_to_authoring":true,"stage":{"name":"S2_20","nexts":[],"prevs":[],"skip_confirm":true},"task":{"code_sha256":"140a83b71aa1007dd275c91a0aa8c837bd2d72a41136ae5bfa0c007da8dd701e","mcp":"code-executor","parameters":{"language":"python","network":"agent-network","requirements":"httpx==0.28.1","timeout":300},"task_name":"Task_S2_20_deterministic_relief_plan","tool_name":"run_code"},"task_procedure":{"IN":{"nexts":["Task_S2_20_deterministic_relief_plan"],"wait_until":[]},"OUT":{"nexts":[],"wait_until":["Task_S2_20_deterministic_relief_plan"]},"Task_S2_20_deterministic_relief_plan":{"nexts":["OUT"],"wait_until":["IN"]}}},"workflow_id":"S2_20"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_20_parent_member_paths.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_20_parent_member_paths.json new file mode 100644 index 00000000..a4fef9c6 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/s2_20_parent_member_paths.json @@ -0,0 +1,117 @@ +[ + "corpus/build_receipt.json", + "corpus/chunk_manifest.jsonl", + "corpus/source_manifest.json", + "corpus/weaviate_collection.schema.json", + "law_values/court_fee_values.yml", + "law_values/general_law_values.yml", + "manifest/authority_release.json", + "manifest/case_type_coverage.json", + "manifest/case_type_registry.yml", + "manifest/case_type_rule_registry.yml", + "manifest/corpus_release.json", + "manifest/s2_20_parent_member_paths.json", + "migration/actio_assets_receipt.json", + "migration/legacy_asset_disposition.yml", + "offline_build/build_authority_law_value_release.py", + "offline_build/build_authority_law_value_release.txt", + "offline_build/build_corpus_snapshot.py", + "offline_build/build_corpus_snapshot.txt", + "offline_build/build_release_manifests.py", + "offline_build/build_release_manifests.txt", + "offline_build/build_s2_20_inline_projection.py", + "offline_build/build_s2_20_inline_projection.txt", + "offline_build/compile_case_type_registry.py", + "offline_build/compile_case_type_registry.txt", + "offline_build/compile_relief_rule_projection.py", + "offline_build/compile_relief_rule_projection.txt", + "registry/binding/binding_signature_projection.yml", + "registry/calculations/CE-01_interest_delay.yml", + "registry/calculations/CE-02_allocation_setoff_balance.yml", + "registry/calculations/CE-03_limitation_deadline.yml", + "registry/calculations/CE-04_valuation.yml", + "registry/calculations/CE-05_personal_injury.yml", + "registry/calculations/CE-06_construction_defect.yml", + "registry/calculations/CE-07_lease_use_gain.yml", + "registry/calculations/CE-08_inheritance_reserved_share.yml", + "registry/calculations/CE-09_wage_severance.yml", + "registry/calculations/CE-10_actio_insolvency_value.yml", + "registry/calculations/CE-11_distribution_share_division.yml", + "registry/calculations/CE-12_insurance.yml", + "registry/calculations/CE-13_court_value_cost.yml", + "registry/calculations/CE-R1_ip_damage.yml", + "registry/calculations/CE-R2_org_liquidation.yml", + "registry/calculations/CE-R3_transport_maritime.yml", + "registry/calculations/CE-R4_financial_instrument.yml", + "registry/calculations/calculation_activation_registry.yml", + "registry/corpus/requirement_fact_scope.yml", + "registry/drafting/case_type_relief_rules.yml", + "registry/drafting/counter_performance_rules.yml", + "registry/drafting/forbidden_relief_rules.yml", + "registry/drafting/procedural_declaration_rules.yml", + "registry/party/party_set_rules.yml", + "registry/planning/option_disposition_policy.yml", + "registry/regression/finding_fixture_map.yml", + "registry/review/review_policy_registry.yml", + "registry/temporal/inheritance_reserved_share.yml", + "release_ops/canary_rollback.py", + "release_ops/canary_rollback.txt", + "release_ops/release_validator.py", + "release_ops/release_validator.txt", + "renderers/R01_money_payment.yml", + "renderers/R02_delivery_possession.yml", + "renderers/R03_declaration_registry.yml", + "renderers/R04_special_nonmoney_performance.yml", + "renderers/R05_declaratory.yml", + "renderers/R06_constitutive_judgment_challenge.yml", + "routing/actio_pauliana_mortgage_route.yml", + "routing/actio_pauliana_route_registry.yml", + "runtime/authority/authority_preflight.py", + "runtime/authority/authority_preflight.txt", + "runtime/binding_signature_projection.py", + "runtime/binding_signature_projection.txt", + "runtime/c25_case_type_bind.py", + "runtime/c25_case_type_bind.txt", + "runtime/c26_rule_branch_bind.py", + "runtime/c26_rule_branch_bind.txt", + "runtime/c27_query_plan.py", + "runtime/c27_query_plan.txt", + "runtime/c28_weaviate_retrieve.py", + "runtime/c28_weaviate_retrieve.txt", + "runtime/c29_pack_verify.py", + "runtime/c29_pack_verify.txt", + "runtime/calculators/registry_engine.py", + "runtime/calculators/registry_engine.txt", + "schemas/binding_retrieval.schema.json", + "schemas/calculation.schema.json", + "schemas/deployment.schema.json", + "schemas/domain_verdict.schema.json", + "schemas/relief_plan.schema.json", + "tests/fixtures/s2_20/actio_route_cap_matrix.json", + "tests/fixtures/s2_20/calculation_temporal_boundaries.json", + "tests/fixtures/s2_20/case_type_rule_binding_matrix.json", + "tests/fixtures/s2_20/case_type_zero_multi_hash_mutations.json", + "tests/fixtures/s2_20/minimal_supported_portfolio.json", + "tests/fixtures/s2_20/mixed_option_dispositions.json", + "tests/fixtures/s2_20/plan_publish_barrier_failure.json", + "tests/fixtures/s2_20/regression_manifest.json", + "tests/fixtures/s2_20/retrieval_replay_injection.json", + "tests/s2_20/test_agent_and_inline_parity.py", + "tests/s2_20/test_agent_and_inline_parity.txt", + "tests/s2_20/test_calculation_and_reports.py", + "tests/s2_20/test_calculation_and_reports.txt", + "tests/s2_20/test_inline_output_schema.py", + "tests/s2_20/test_inline_output_schema.txt", + "tests/s2_20/test_offline_compilers.py", + "tests/s2_20/test_offline_compilers.txt", + "tests/s2_20/test_option_group_and_binding.py", + "tests/s2_20/test_option_group_and_binding.txt", + "tests/s2_20/test_plan_publish_and_release.py", + "tests/s2_20/test_plan_publish_and_release.txt", + "tests/s2_20/test_regression_manifest_closure.py", + "tests/s2_20/test_regression_manifest_closure.txt", + "tests/s2_20/test_retrieval_and_pack.py", + "tests/s2_20/test_retrieval_and_pack.txt", + "validators/actio_value_compensation_invariants.yml", + "workflows/S2_20_canonical_relief_plan_reduce.yml" +] diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json new file mode 100644 index 00000000..99b81c43 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json @@ -0,0 +1 @@ +{"admission_status":"PENDING","backend_capability_receipt_sha256":"PENDING_SEQUENTIAL_BIND","executor_binding_sha256":"d1e16314e81faea5e6a8dd826e81b9ae01e0536923480ba4fb710a4959385a7c","parent_release_sha256":"70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92","present_deterministic_stage_ids":["S2_00","S2_20"],"schema_version":"stage2_deterministic_admission_receipt.v1","signature":"PENDING_EXTERNAL_SIGNATURE","signature_verification_status":"PENDING_EXTERNAL_SIGNATURE","signed_payload_sha256":"PENDING_SEQUENTIAL_BIND","stage_receipts":[{"asset_id":"RECEIPT-S2_00-INLINE-CODE","binding_status":"BOUND","path":"manifest/s2_00_inline_code_receipt.json","schema_id":"stage2_s2_00_inline_code_receipt.v2","sha256":"1a0044a0f1e0fec13a5d4870df6147b8c4c671d843d4b634b4249442fdcfdfb8"},{"asset_id":"RECEIPT-S2_20-INLINE-CODE","binding_status":"BOUND","path":"manifest/s2_20_inline_code_receipt.json","schema_id":"stage2_s2_20_inline_code_receipt.v1","sha256":"3693ce7d99d64458f2dbf80f9535aad55ad1dec22ab95898f3c05b204afb801b"}]} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/stage2_release.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/stage2_release.json index 984a9a5f..843f5fa3 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/stage2_release.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/manifest/stage2_release.json @@ -1 +1 @@ -{"adapter_decisions":[{"adapter_id":"S2A-SIGNAL-ALL-V1","adapter_kind":"SIGNAL_ALL_EXPANSION","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"file_conservation_equation":"semantic_file_rows + integrity_only_file_rows = Counter(signal_manifest.files[])","global_signal_id_uniqueness_assumed":false,"integrity_only_kinds":["compatibility_view"],"manifest_selector":"/downstream_read_sets/stage2","physical_path_rule":"U/signals/","record_conservation_equation":"used_record_occurrences + unused_record_occurrences + unmapped_record_occurrences = records_from_semantic_files","record_occurrence_key":["manifest_transaction_id","file_path","record_ordinal","signal_id"],"row_order":"PRESERVE_MANIFEST_ORDER","row_source":"/files","semantic_kinds":["canonical","domain_signal"],"sentinel":["ALL"]},"schema_ref":"schemas/ingress.schema.json#/$defs/all_expansion_contract"},{"adapter_id":"S2A-DUAL-SG01-V1","adapter_kind":"DUAL_SG01_PROJECTION","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"comparison":"PARSED_CANONICAL_PROJECTION_EQUAL","payload_root":"/domain_activation_manifest","projection_json_pointers":["/schema_version","/signal_id","/status","/registry_version","/registry_index_sha256","/screening_sha256","/domain_entries","/active_domain_ids","/supporting_domain_ids","/monitor_domain_ids","/expected_runnable_domain_ids","/required_calculation_domains","/unrouted_material","/conservation_gate","/fail_open_policy","/review_items","/contract_guards"],"raw_hash_policy":"PRESERVE_AND_VERIFY_SEPARATELY","routing_path":"routing/domain_activation_manifest.json","set_semantics_json_pointers":["/active_domain_ids","/supporting_domain_ids","/monitor_domain_ids","/expected_runnable_domain_ids","/required_calculation_domains"],"signal_path":"signals/domain_activation_manifest.json"},"schema_ref":"schemas/ingress.schema.json#/$defs/dual_sg01_projection_contract"},{"adapter_id":"S2A-P1-HANDOFF-FLAT-V1","adapter_kind":"HANDOFF","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"count_field_required":false,"logical_input_id":"P1_REVIEW_HANDOFF","p1_digest_keys":["evidence_indexed_sha256","evidence_event_candidates_sha256","b1_gate_sha256","b2_gate_sha256","screening_sha256","activation_manifest_sha256","registry_index_sha256"],"review_items_json_pointer":"/review_items","schema_version":"stage1_part1_soft_gate_handoff.v1","seal_sources":["routing/domain_screening.json","routing/domain_activation_manifest.json","domains/_registry_index.json"],"source_stage":"P1","status_json_pointer":"/handoff_status","wrapper_json_pointer":""},"schema_ref":"schemas/ingress.schema.json#/$defs/handoff_adapter_contract"},{"adapter_id":"S2A-P2-HANDOFF-FLAT-V1","adapter_kind":"HANDOFF","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"count_field_required":false,"logical_input_id":"P2_REVIEW_HANDOFF","review_items_json_pointer":"/review_items","schema_version":"stage1_part2_review_handoff.v1","seal_sources":["BO.json","signals/signal_manifest.json"],"source_stage":"P2","status_json_pointer":"/status","wrapper_json_pointer":""},"schema_ref":"schemas/ingress.schema.json#/$defs/handoff_adapter_contract"},{"adapter_id":"S2A-P3-HANDOFF-WRAPPED-V1","adapter_kind":"HANDOFF","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"count_field_required":true,"logical_input_id":"P3_REVIEW_HANDOFF","review_items_json_pointer":"/review_items","schema_version":"stage1_part3_review_handoff.v1","seal_sources":["legal_effect_structures.json","validation_assets/routing/part3_receipt.json"],"source_stage":"P3","status_json_pointer":"/status","wrapper_json_pointer":"/stage1_part3_review_handoff"},"schema_ref":"schemas/ingress.schema.json#/$defs/handoff_adapter_contract"},{"adapter_id":"S2A-P4-HANDOFF-WRAPPED-V1","adapter_kind":"HANDOFF","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"count_field_required":true,"logical_input_id":"P4_REVIEW_HANDOFF","review_items_json_pointer":"/review_items","schema_version":"stage1_part4_review_handoff.v1","seal_sources":["Fact_Ledger_base.json","validation_assets/routing/part4_receipt.json","stage1_tmp/fact_ledger/fact_ledger_writer_report.json"],"source_stage":"P4","status_json_pointer":"/status","wrapper_json_pointer":"/stage1_part4_review_handoff"},"schema_ref":"schemas/ingress.schema.json#/$defs/handoff_adapter_contract"},{"adapter_id":"S2-REVIEW-MAP-V1","adapter_kind":"REVIEW_NORMALIZATION","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"aggregate_handoff_status_never_resolves_item":true,"handoff_status_mappings":[{"source_stage":"P1","source_value":"READY_NO_REVIEW","technical_disposition":"AVAILABLE"},{"source_stage":"P1","source_value":"READY_WITH_REVIEW","technical_disposition":"AVAILABLE_WITH_ISSUES"},{"source_stage":"P1","source_value":"BLOCKED","technical_disposition":"UNAVAILABLE"},{"source_stage":"P2","source_value":"PENDING_FINALIZE","technical_disposition":"AVAILABLE_WITH_ISSUES"},{"source_stage":"P2","source_value":"FINALIZED","technical_disposition":"AVAILABLE"},{"source_stage":"P3","source_value":"OPEN","technical_disposition":"AVAILABLE_WITH_ISSUES"},{"source_stage":"P3","source_value":"FINALIZED","technical_disposition":"AVAILABLE"},{"source_stage":"P4","source_value":"OPEN","technical_disposition":"AVAILABLE_WITH_ISSUES"},{"source_stage":"P4","source_value":"FINALIZED","technical_disposition":"AVAILABLE"}],"mappings":[{"mapping_id":"S2RM-001","normalized_partition":"SUPPORTED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"SUPPORTED","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-002","normalized_partition":"CONDITIONAL","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"CONDITIONAL","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-003","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"UNRESOLVED","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-004","normalized_partition":"EXCLUDED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"EXCLUDED","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-005","normalized_partition":"SUPPORTED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"observed","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-006","normalized_partition":"CONDITIONAL","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"inferred","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-007","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"contested","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-008","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"missing_required","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-009","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"review","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-010","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"NO_SUPPORT","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-011","normalized_partition":"CONDITIONAL","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"info","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-012","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"review","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-013","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"SOFT_WARNING","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-014","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"hard_warning","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-015","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"HARD_WARNING","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-016","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"block","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-017","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"BLOCK","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"}],"normalized_partitions":["SUPPORTED","CONDITIONAL","UNRESOLVED","EXCLUDED","UNMAPPED"],"resolution_inference_allowed":false,"unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},"schema_ref":"schemas/review_status.schema.json#/$defs/review_mapping_row"},{"adapter_id":"S2A-BO-V8-LIST-V1","adapter_kind":"PRODUCER_SHAPE","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"logical_input_id":"BO","open_source_fields_policy":"PRESERVE_UNMODELED_FIELDS_WITH_RAW_HASH","producer_id":"Task_C_BO_F0_final_bo_compiler_gate_writer","required_item_fields":["BO_ID","id","BOType","ActionType","JuristicAct","Action","Reason","PriorAct","ReasonRefs","Legal_Keywords","core_field_base","amount","EvidenceTitles","Evidence","source_evidence_indexes","provenance","downstream_seed_refs","extensions"],"required_root_fields":[],"root_shape":"ARRAY","schema_contract_version":null},"schema_ref":"schemas/ingress.schema.json#/$defs/producer_shape_adapter"},{"adapter_id":"S2A-EVIDENCE-V3-ENVELOPE-V1","adapter_kind":"PRODUCER_SHAPE","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"logical_input_id":"EVIDENCE_INDEXED","open_source_fields_policy":"PRESERVE_UNMODELED_FIELDS_WITH_RAW_HASH","producer_id":"Task_B1_quality_gate_evidence_indexed","required_root_fields":["schema_contract_version","items"],"root_shape":"OBJECT_ENVELOPE","schema_contract_version":"evidence_indexed.v3"},"schema_ref":"schemas/ingress.schema.json#/$defs/producer_shape_adapter"},{"adapter_id":"S2A-EVENTS-V1-ENVELOPE-V1","adapter_kind":"PRODUCER_SHAPE","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"logical_input_id":"EVIDENCE_EVENT_CANDIDATES","open_source_fields_policy":"PRESERVE_UNMODELED_FIELDS_WITH_RAW_HASH","producer_id":"Task_B2_quality_gate_event_candidates","required_root_fields":["schema_version","items"],"root_shape":"OBJECT_ENVELOPE","schema_contract_version":"evidence_event_candidates.v1"},"schema_ref":"schemas/ingress.schema.json#/$defs/producer_shape_adapter"},{"adapter_id":"S2A-DOMAIN-CONFIG-V1","adapter_kind":"DOMAIN_CONFIG","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"accepted_schema_version":"stage1_domain_config.v1","depends_on_legal_dependency_allowed":false,"rebuttal_slot_synthesis_allowed":false,"required_slot_fields":["element_slots","opposing_fact_slots","defense_map","calculation_bindings","emits_signals"],"undeclared_slot_policy":"PRESERVE_AS_PROPOSED_NEW_SLOT_ISSUE"},"schema_ref":"schemas/ingress.schema.json#/$defs/domain_config_adapter_contract"},{"adapter_id":"S2A-DOMAIN-CONFIG-V2","adapter_kind":"DOMAIN_CONFIG","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"accepted_schema_version":"stage1_domain_config.v2","depends_on_legal_dependency_allowed":false,"rebuttal_slot_synthesis_allowed":false,"required_slot_fields":["element_slots","opposing_fact_slots","defense_map","calculation_bindings","emits_signals"],"undeclared_slot_policy":"PRESERVE_AS_PROPOSED_NEW_SLOT_ISSUE"},"schema_ref":"schemas/ingress.schema.json#/$defs/domain_config_adapter_contract"},{"adapter_id":"S2A-FACT-LEDGER-CURRENT-V8-V1","adapter_kind":"FACT_LEDGER","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"bo_source_bo_id_multiset_equality_required":true,"fact_id_pattern":"^F-[0-9]{3,}$","legacy_adapter_status":"DISABLED_NO_APPROVED_ADAPTER","producer_generation":"CURRENT_V8","required_row_fields":["fact_id","source_bo_id","domain_effects","calculation_requests"],"root_shape":"ARRAY"},"schema_ref":"schemas/ingress.schema.json#/$defs/fact_ledger_adapter_contract"},{"adapter_id":"PA-SG-COMPILER-001","adapter_kind":"PRODUCER_ALIAS","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"bidirectional_match_allowed":true,"global_alias_allowed":false,"orchestration_producer_id":"Task_C_BO_S0_signal_bundle_writer","schema_writer_id":"Task_C_BO_S0_canonical_signal_compiler","scope":"STAGE1_PART2_SIGNAL_TRANSACTION_ONLY"},"schema_ref":"schemas/ingress.schema.json#/$defs/producer_alias_contract"}],"authoring_root":"Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean","authorization_status":"DEV_VALIDATION_ONLY","bundle":{"active_profile_policy":"CASE_RUN_EXACT_RELEASE_SELECTED_SUBSET_ONLY","authority_release_status":"DEV_UNAVAILABLE","context_cohort_policy_id":"S2-CACHE-STRUCTURED-CONTEXT-HANDOFF-V2","downstream_hybrid_status":"OFFLINE_HYBRID_IMPLEMENTED_LIVE_ADMISSION_PENDING","executable":false,"handoff_contract_version":"S2_00_STRUCTURED_CONTEXT_HANDOFF_V2","mode":"STRUCTURAL_FIXTURE","reason_codes":["STRUCTURAL_FIXTURE_NOT_EXECUTABLE","AUTHORITY_RELEASE_UNAVAILABLE","S2_10_PLATFORM_MODEL_AND_LEGAL_ADMISSION_PENDING","DIRECT_MCP_LIVE_ADMISSION_PENDING"],"s2_10_agent_ref":{"asset_id":"AGENT-S2_10-HYBRID-OPAQUE","binding_status":"BOUND","path":"agent_scripts/Stage_2_S2_10.yml","schema_id":"liti_agent_yaml.v1","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513"},"s2_10_agent_sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","s2_10_llm_binding_ref":{"asset_id":"BINDING-S2_10-LLM-OPAQUE","binding_status":"BOUND","path":"deployment/stage2_s2_10_llm_binding.yml","schema_id":"stage2_s2_10_llm_binding.v2","sha256":"b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1"},"s2_10_llm_binding_sha256":"b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1","selected_context_ordered_refs_sha256":"37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570","selected_context_refs":[]},"dependency_locks":{"downstream_availability":[{"asset_id":"CASE-TYPE-COVERAGE","binding_status":"BOUND","path":"manifest/case_type_coverage.json","schema_id":"stage2.case_type_coverage.v1","sha256":"7a7cd1222964710c9e33bac57c3cd5fc482171a09b5f6af77e750513bc1a5f75"},{"asset_id":"CORPUS-RELEASE","binding_status":"BOUND","path":"manifest/corpus_release.json","schema_id":"stage2.corpus_release.v1","sha256":"27d0c83e7a95342efdbe5be675b6d5b7d0ff8695eeec022ebc5aded3359fc981"}],"stage1":{"closure_scope":"REFERENCED_55_ONLY_NOT_FULL_STAGE1_RUNTIME_RELEASE","closure_snapshot_date":"2026-08-29","concrete_paths":[{"binding_status":"BOUND","lock_id":"S1-DEPLOY-001","path":"runtime_manifest.json","schema_id":"stage1_runtime_manifest.v1","sha256":"8964593a64a9b1bc90122054bb09eb3911827a06ed62dab0d6b7c745e7e18f54","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-002","path":"domains/_registry_index.json","schema_id":null,"sha256":"9f177ebf8860e20e05483967a2037f3baa09c2ac92c69ddeb260c04ca31ebf39","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-003","path":"signals/signal_registry.v2.json","schema_id":"signal_registry.v2","sha256":"4392b40da458102f8dd11b40b40ae3f694b7b5911849b050e2e4118c569e5ab0","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-004","path":"domains/E-00/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"5919f7ea1d7be02666b0c48aa6a66445e6d454fc2fbb21d7fe5154b0a1e68f6f","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-005","path":"domains/E-01/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"b557e92cd1b093bf31792dbcf5b62cab8ad064a65c4421e79f141e06b4cc2192","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-006","path":"domains/E-02/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"be407c980c28226a15406f85b5861b04a4e19a13870513ac6626349fc05ac434","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-007","path":"domains/E-03/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"7d3814f9b50cd5b33ef65a4eb778693552b3685bd369e765e9ac032734ebe23e","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-008","path":"domains/E-04/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"95a8c600cdce5a687f766788af0f763ee1b6a895e6ed80934afd28fe9a107e25","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-009","path":"domains/E-05/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"e50541018f47aa27de2f8b13ec3fa52210cf8456a6feed8356af78c1f1da144a","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-010","path":"domains/E-06/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"1e2bee36cb3c24dd37fc3beb3cf70236d531126c4f62ee97b5b42e55f4b0745c","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-011","path":"domains/E-07/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"22ac562084b1ce231b7257d099c18b6a4619defa2fc42504d590e0bcc494c5f8","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-012","path":"domains/E-08/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"4d545306d42120e8552dd953d4336ef6de828ea827779944ba73acfda3d3a8bb","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-013","path":"domains/E-09/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"7ef7340750094efeb372c397eb3134e21d62dda6988b1f6fa0a197b9963868e0","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-014","path":"domains/E-10/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"e8d4f45fa76ea9e09333256dd4ea36cd3dd963bf60c04814a2cb8dc90d152f0a","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-015","path":"domains/E-11/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"eb78d0188a0a2400307b1c34c8f8703c54cd86dd06b942c1709c44a8630a68e1","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-016","path":"domains/E-12/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"f336accdc6de10cdcc28c1190328054bca402fb77a2a9859d59fbaf5e84dd170","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-017","path":"domains/E-13/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"e27e2e3855b5868a3ec12c7093b872434702f2e465b73c0bfc948b516aa0fc35","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-018","path":"domains/E-14/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"a273148cc17f07d90cda500fa5cb7df30c9cd253f7b86048cf4f63495d36a156","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-019","path":"domains/E-15/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"7f37edddc101a08ed8a0e25f3a2c638e72571edc212ac91d96c1e33c51202a69","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-020","path":"domains/E-16/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"ae9af46ee31b6ef0dafedd35ccd7959a941d67d1a3dcc70e0b13647896873323","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-021","path":"domains/E-17/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"5c61f4486bdc968e4b30734b3c045404f0a711f47ea3abbe6c5c64652fb7f68c","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-022","path":"domains/E-18/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"74ff76148d175929bdeeeced77e9a9922d29b51ad3d00ae6711c43c55717692c","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-023","path":"domains/E-19/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"a8578f54a3fead3bbd35c62d7199b0f8aafb77f5d409a87236a55d2550fbfd37","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-024","path":"domains/E-20/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"29ee14cfe7789f004e6b6978d5360cf1bebe33bf88715df0cb47257993a11d00","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-025","path":"domains/E-21/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"4e1684a843d9e0c5af82f45332ad85abe94eda0c3ff0d578235d892aee39b908","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-026","path":"domains/EC-00/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"fe74de112b73289485dcead7e0fc7d270c794b3cf8a29ee00fab1eb64ba13861","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-027","path":"domains/X1/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"ad2fee7d206018f9a1f66e5fdf40dd67b686f6938099bad1ffc5d538db14ac57","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-028","path":"domains/X2/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"eba7d4671546bd66f1350d144ae0884f8beffb0dffdc147b45b9d5292676d46f","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-029","path":"domains/X3/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"8b67a638ae4a86aca3a2216974242b11ec39790162c9f366edfa91b02c3d270a","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-030","path":"platform/schemas/client_goal_domain_profiles.schema.json","schema_id":null,"sha256":"ae2bfe0d754a09cbae16b2c15bf1518fc23f9e1bda8fa1f5f949606c8e42c010","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-031","path":"platform/schemas/domain_fanout_plan.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s1/domain_fanout_plan.schema.json","sha256":"3b0948613a5996028b9c030a99f0b51d682f6035e019557756b1a15d43971113","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-032","path":"platform/schemas/domain_seed_output.schema.v3.json","schema_id":"https://schemas.liti-agent.local/stage1/s1/domain_seed_output.schema.v3.json","sha256":"992acf05dbccb34c65ead4e8c592f424e3b91672dc109cbd1bfa76a0a71a13c9","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-033","path":"platform/schemas/domain_slice.schema.v2.json","schema_id":"https://schemas.liti-agent.local/stage1/s1/domain_slice.schema.v2.json","sha256":"212a405088e7cf7ba2c65528a1c716938c946df7fe3bae3256b613051ed31aa3","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-034","path":"platform/schemas/fact_exception_pack.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part4/fact_exception_pack.schema.json","sha256":"4eba7e51ed46a99e3704bc2333169749f4a16935de26a8c8027c1cac98ea58cf","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-035","path":"platform/schemas/fact_ledger_base.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part4/fact_ledger_base.schema.json","sha256":"b3f0e79ecb4c2f720f3e07e89154aadbd2327e4129cc703569fb5635240d2fe8","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-036","path":"platform/schemas/fact_ledger_candidate_bundle.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part4/fact_ledger_candidate_bundle.schema.json","sha256":"4e481504fb795b2be510680a8fa88124f5763a8124462f7870be4125ed9a7730","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-037","path":"platform/schemas/legal_effect_structures.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part3/legal_effect_structures.schema.json","sha256":"fc962e8ae39f9bede64ba017297eded6413689204a065e00c3b3bdca8f1854df","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-038","path":"platform/schemas/structure_seed_bundle.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part3/structure_seed_bundle.schema.json","sha256":"b7af9e422b6ac3876cffea39ec4f617eea76a631a57dfdfcd57d3785a83c667a","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-039","path":"signals/_common/evidence_slot_status.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/evidence_slot_status.schema.json","sha256":"292b03960b187cef668b8635a8d7539fde7c31f0c20d01af52c4f6ff8519d7b1","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-040","path":"signals/_common/signal_item.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/signal_item.schema.json","sha256":"de8695f98041c06cf50c0d8d2ebc31e7b3c518ca9d39a27da940438704c58bb1","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-041","path":"signals/schemas/domain_activation_manifest.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/domain_activation_manifest.schema.json","sha256":"013a6ebd230ebe46dda665af9f6c4448b267444b44e7b8f701f2fae80a2ee92a","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-042","path":"signals/schemas/procedural_posture_relief_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/procedural_posture_relief_signals.schema.json","sha256":"fefb4317ad63088919b61777c71fe75ee6aa507b9f599dcf455d2af63dfc5e0d","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-043","path":"signals/schemas/party_capacity_standing_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/party_capacity_standing_signals.schema.json","sha256":"66de89ac53964166f6caabd50cbc03eb82dede0acf702d5e6d825c1d82ef81d0","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-044","path":"signals/schemas/governing_law_version_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/governing_law_version_signals.schema.json","sha256":"13a3f62f03356090d2cb24de2da0ba217928dfe8eb3c111d0f5e87c7df3119ee","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-045","path":"signals/schemas/legal_relation_lifecycle_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/legal_relation_lifecycle_signals.schema.json","sha256":"420613a5900c4360487b89b978efedde58f5ddc61644130e4b9e63ef8ab33d8b","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-046","path":"signals/schemas/timeline_notice_condition_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/timeline_notice_condition_signals.schema.json","sha256":"99c66208524155cea6bbd5e24fd26998cc9b653c89b24b569c793e36f1623d35","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-047","path":"signals/schemas/asset_right_state_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/asset_right_state_signals.schema.json","sha256":"fc34fbb3d33a284c3d57f3c278cbda8b3555ef26ee2f06b803fd2410ebce38b6","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-048","path":"signals/schemas/liability_causation_damage_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/liability_causation_damage_signals.schema.json","sha256":"34102cb8eeda80773eb62a5ee61e3d714bf90424ed5350dcac4b7bf873a72c5a","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-049","path":"signals/schemas/defense_exception_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/defense_exception_signals.schema.json","sha256":"010148c15e60e4d112b142f80b1723c06e34ba22b3edefae9e4371f2353b053e","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-050","path":"signals/schemas/evidence_proof_conflict_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/evidence_proof_conflict_signals.schema.json","sha256":"c419f568e28c06c629bc715aff7b0737b77e9c4871c91d4fae8f6ecf04196390","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-051","path":"signals/schemas/calculation_requirements.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/calculation_requirements.schema.json","sha256":"7fdb5ef0f50d7af22ac417abc4022cd238f5ab0dc942866420616729a9e3571f","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-052","path":"signals/schemas/remedy_enforcement_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/remedy_enforcement_signals.schema.json","sha256":"999e1969b983748f209e9b5239f7edd0ec43bc642d9ea8fd7edbf34f9ce653f3","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-053","path":"signals/schemas/legal_effect_routes.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/legal_effect_routes.schema.json","sha256":"c24cb740c370aa2477199a0225be8291164ef5c787601fd962a370c642cc3cc0","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-054","path":"signals/schemas/domain_signal_envelope.schema.v2.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/domain_signal_envelope.schema.v2.json","sha256":"1483d6c5f98083f59172feff9b7c15b44d3ed789db5b6172d0de05f06e9d3fbc","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-055","path":"signals/schemas/signal_manifest.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/signal_manifest.schema.json","sha256":"5e72084780b82b29582c9ffcf48f3e4894d7c0b152e5ce8df394583c07dde681","source_manifest":"signals/signal_registry.v2.json"}],"contract_manifest_ref":{"mode":"CONDITIONAL_RELOCATION_ONLY","path":null,"sha256":null,"status":"NOT_REQUIRED_DEFAULT_PATHS"},"expected_concrete_path_count":55,"full_stage1_runtime_release_status":"STAGE1_NOT_RELEASE_READY"},"stage2_direct":[{"asset_id":"AGENT-S2_10-HYBRID-OPAQUE","binding_status":"BOUND","path":"agent_scripts/Stage_2_S2_10.yml","schema_id":"liti_agent_yaml.v1","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513"},{"asset_id":"BINDING-S2_10-LLM-OPAQUE","binding_status":"BOUND","path":"deployment/stage2_s2_10_llm_binding.yml","schema_id":"stage2_s2_10_llm_binding.v2","sha256":"b240212634c10017fa3b4d1d71fed7ab82cdb1c2da80d92ece29a6dadfb429f1"},{"asset_id":"AUTHORITY-REGISTRY","binding_status":"BOUND","path":"registry/authority/authority_registry.yml","schema_id":"stage2.authority_registry.v1","sha256":"b4663610e18098cf650fbd5846f18dcbc96074b3012c68f03f67402509c7d142"},{"asset_id":"AUTHORITY-RELEASE","binding_status":"BOUND","path":"manifest/authority_release.json","schema_id":"stage2.authority_release.v1","sha256":"cc683958377eea44756de6e9c74fc925da66dd60cc84363f5bec36584f5b2967"},{"asset_id":"PROFILE-EC-00_CONTRACT_GENERAL","binding_status":"BOUND","path":"registry/substantive/EC-00_contract_general.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"a4f7abd9957b3a9e58d05dd559c3c869bf96fd88ea18968a363c6e606e3c0aa8"},{"asset_id":"PROFILE-E-01_JURISTIC_ACT_VALIDITY","binding_status":"BOUND","path":"registry/substantive/E-01_juristic_act_validity.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"0a0d2676f96be321a7bb974b8e4fb1981ab43773f271c99dc86a70cb17d27c99"},{"asset_id":"PROFILE-E-02_CONTRACT_MONEY_CLAIM","binding_status":"BOUND","path":"registry/substantive/E-02_contract_money_claim.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"08e252bccc718ccc44a258bc540afc2567e509239399f4017550df32bac97824"},{"asset_id":"PROFILE-E-03_PARTIES_LIABILITY_SUCCESSION","binding_status":"BOUND","path":"registry/substantive/E-03_parties_liability_succession.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"4e841d5f640b06a1b52060b82dda39605369cbe26d81655b7918673437e4519c"},{"asset_id":"PROFILE-E-04_UNJUST_ENRICHMENT","binding_status":"BOUND","path":"registry/substantive/E-04_unjust_enrichment.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"31a5966d4c14940e810f870bf4382f1be642f7716ebaaa35193ef32e9b9d95c7"},{"asset_id":"PROFILE-E-05_TORT_GENERAL","binding_status":"BOUND","path":"registry/substantive/E-05_tort_general.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fe6ea92fc10c4196e0ac83b990bf2606e24024030e7b0002fecdae9f8f50221a"},{"asset_id":"PROFILE-E-06_PROFESSIONAL_LIABILITY","binding_status":"BOUND","path":"registry/substantive/E-06_professional_liability.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"efa7a4f6cda90c04a06558a9d0e1efe13b25169d55d03e36d55d72fcdd096a15"},{"asset_id":"PROFILE-E-07_CONSTRUCTION_DEFECT","binding_status":"BOUND","path":"registry/substantive/E-07_construction_defect.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"78c76cb0485a4acf741109aee05f634f51c3a6904044c613080b21c3978a09f9"},{"asset_id":"PROFILE-E-08_LEASE_DEPOSIT","binding_status":"BOUND","path":"registry/substantive/E-08_lease_deposit.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"dbe1886671edff49b8b0251eed567d1b3e4e5d748456434262286ca7acf990db"},{"asset_id":"PROFILE-E-09_REGISTRY_TRANSFER_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-09_registry_transfer_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"d18946322a8cb528146ac0aa0a2880c41ac2056671fd237b928341ceb8561002"},{"asset_id":"PROFILE-E-10_SECURED_REGISTRY","binding_status":"BOUND","path":"registry/substantive/E-10_secured_registry.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"5819c26ec8478e12a12e340c93a9d23cc410833e1192c2315a1986c9d48a1eba"},{"asset_id":"PROFILE-E-11_POSSESSION_VINDICATION","binding_status":"BOUND","path":"registry/substantive/E-11_possession_vindication.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"5e6d6460be4ec0f2ba0096305b9ae39f5f430d107337d0d7e07aac1a9d599aaf"},{"asset_id":"PROFILE-E-12_CO_OWNERSHIP_BOUNDARY","binding_status":"BOUND","path":"registry/substantive/E-12_co_ownership_boundary.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"62cfbf6a6c5c5edc8b912e348be1c4d4f7dd4c20c722e57e5554058f0caafb4a"},{"asset_id":"PROFILE-E-13_CREDITOR_PRESERVATION","binding_status":"BOUND","path":"registry/substantive/E-13_creditor_preservation.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"c60e6d67c7e3985d1f6ce44027726282f931934e3e66b44f06ccd26845ca2f45"},{"asset_id":"PROFILE-E-14_EXECUTION_LINKED_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-14_execution_linked_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"45d4b3ab46e1c8834c3b3eec72a28d05e39cb78a9ec2c2de07b5e55e81aeebd7"},{"asset_id":"PROFILE-E-15_SUCCESSION_FAMILY_PROPERTY","binding_status":"BOUND","path":"registry/substantive/E-15_succession_family_property.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"f691b67295ad8e634b34519cf71156caf6eaf73cef54577615ab79462f8aa766"},{"asset_id":"PROFILE-E-16_NEGOTIABLE_INSTRUMENTS","binding_status":"BOUND","path":"registry/substantive/E-16_negotiable_instruments.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"7720d83b64e7a44720788f6ff3c7cc8ceaf5195377a1e61b52de2144989a1194"},{"asset_id":"PROFILE-E-17_LABOR_WAGE_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-17_labor_wage_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"168ce5e41104937cf7ab946944f060b82cfe16066425897f07bb9e837b8689ea"},{"asset_id":"PROFILE-E-18_ORG_RESOLUTION_STATUS","binding_status":"BOUND","path":"registry/substantive/E-18_org_resolution_status.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"59cf689f886dfaf4ad00dbe6142f5b11c37d0062660f7467aa43427462aa075f"},{"asset_id":"PROFILE-E-19_INSURANCE_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-19_insurance_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"0fb4793446c155e7a0b4469c9b8c1be1323112eef55ca4c495307c1af56e754c"},{"asset_id":"PROFILE-E-20_IP_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-20_ip_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fe07f20d149a2c4deac2e46de299cd78f0bf829f58c615a86c45f542d30804e1"},{"asset_id":"PROFILE-E-21_MEDIA_PERSONALITY_RIGHTS","binding_status":"BOUND","path":"registry/substantive/E-21_media_personality_rights.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"951260bbe8ef116d682f49f3679106c7144ca68f8078f5c10b3459667c1f5b2c"},{"asset_id":"PROFILE-E-00_RESIDUAL_UNROUTED","binding_status":"BOUND","path":"profiles/crosscut/E-00_residual_unrouted.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"639e507147a6132f9e41ccd0b00d1b5450d6a9629991629b173d5bcaf5b6df39"},{"asset_id":"PROFILE-X1_NOTICE_LIFECYCLE","binding_status":"BOUND","path":"profiles/crosscut/X1_notice_lifecycle.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"9af8b5b5f3a8196a10308d1ee34e0788d9adeac7a99e142e449490d14316faef"},{"asset_id":"PROFILE-X2_ASSET_IDENTITY_LINEAGE","binding_status":"BOUND","path":"profiles/crosscut/X2_asset_identity_lineage.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"6c1e437944b3a62b60ee7acad5ec9177fefac2991fe970338acc27efcc8a13ad"},{"asset_id":"PROFILE-X3_PROCEDURE_STANDING_RELIEF","binding_status":"BOUND","path":"profiles/crosscut/X3_procedure_standing_relief.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"f3b794bef82566e569232ba02c63ae148099143d8d1ab84de1c4fb76105e65df"},{"asset_id":"PROFILE-X4_RESPONSE_ADMISSION_DEFENSE","binding_status":"BOUND","path":"profiles/crosscut/X4_response_admission_defense.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"57851ed55b3351a8139df29224d74b7cb8427708bbf6de00ecd27c44089df32e"},{"asset_id":"PROFILE-SL-AUTO_MOTOR_VEHICLE","binding_status":"BOUND","path":"profiles/special_law/SL-AUTO_motor_vehicle.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"7234a7ab890f242e6b7aa811528e34c0c85779007cffbf135da0bfff36d61d91"},{"asset_id":"PROFILE-SL-INDUSTRIAL_ACCIDENT","binding_status":"BOUND","path":"profiles/special_law/SL-INDUSTRIAL_ACCIDENT.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"10d78b8031df7d89f835afc9ca60fab36648e561c729780204c80885f4dd097f"},{"asset_id":"PROFILE-SL-PRODUCT_LIABILITY","binding_status":"BOUND","path":"profiles/special_law/SL-PRODUCT_LIABILITY.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"1d5ee8eddcf005fdb33df58f591a8948aa3e891a3a5656d919394ff0b39eea53"},{"asset_id":"PROFILE-SL-RESIDENTIAL_LEASE","binding_status":"BOUND","path":"profiles/special_law/SL-RESIDENTIAL_LEASE.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"3e2efd1b3c0524a305eafedc5ec91e05be989a6e55c1501ed161a9b8f8151fd7"},{"asset_id":"PROFILE-SL-COMMERCIAL_LEASE","binding_status":"BOUND","path":"profiles/special_law/SL-COMMERCIAL_LEASE.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"5aebed9ab56bc00eb7e0511f9fe5db8c3a42f1e81b28d58c38d487aa91cbab3e"},{"asset_id":"PROFILE-SL-LABOR","binding_status":"BOUND","path":"profiles/special_law/SL-LABOR.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"6b2542bb64f4c1d164b1ccd2ce5099fad29bcea4333d9e34bd0fb1667b9ced4a"},{"asset_id":"PROFILE-SL-STATE_LIABILITY","binding_status":"BOUND","path":"profiles/special_law/SL-STATE_LIABILITY.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"5b2fe6d57b918384a619efbd04360ddfaf7720d384136f69d359ccb0b6c853d3"},{"asset_id":"PROFILE-SL-IP-PATENT","binding_status":"BOUND","path":"profiles/special_law/SL-IP-PATENT.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"d0651a8a50d0b18d25931063314ecec284988c5a18fab68d08a317d725bb3066"},{"asset_id":"PROFILE-SL-IP-COPYRIGHT","binding_status":"BOUND","path":"profiles/special_law/SL-IP-COPYRIGHT.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"d4349677f68346f79709ea23a05957262f6426f976388bba45f05c617035eb78"},{"asset_id":"PROFILE-SL-IP-OTHER","binding_status":"BOUND","path":"profiles/special_law/SL-IP-OTHER.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fc3e2772252a3a90571663ebcfbc8e18dbf526977f4a3d77c4705ef8aa8dd6a8"},{"asset_id":"PROFILE-SL-MEDIA","binding_status":"BOUND","path":"profiles/special_law/SL-MEDIA.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"e0d5d287befd1578a06c64a265d5c4426af5772b6764018f6a15f00aac3b498c"},{"asset_id":"PROFILE-SL-TRANSPORT_MARITIME","binding_status":"BOUND","path":"profiles/special_law/SL-TRANSPORT_MARITIME.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"6e87a77c25f8c358f1d7d35ca165b1229c2cb422a440c91768ad80e520d35456"},{"asset_id":"PROFILE-SL-CONSUMER_CONTRACT","binding_status":"BOUND","path":"profiles/special_law/SL-CONSUMER_CONTRACT.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"afd8bbba102614d8d08f2e7a5384f6ca24e1309c1f52feb13906613f2ced51b7"},{"asset_id":"PROFILE-ACTIO-MORTGAGE","binding_status":"BOUND","path":"profiles/overlays/ACTIO-MORTGAGE.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"7e584f7b4e4d19132e9575071f87d05965fb77fa2ca870911fe9974a7c511c4d"},{"asset_id":"PROFILE-ACTIO-MORTGAGE-CREATION","binding_status":"BOUND","path":"profiles/overlays/ACTIO-MORTGAGE-CREATION.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"e727690ee23883e011d49eba01cfd87abc5482641c2bda519c0a39518e1769ee"},{"asset_id":"PROFILE-ACTIO-ENCUMBERED-TRANSFER","binding_status":"BOUND","path":"profiles/overlays/ACTIO-ENCUMBERED-TRANSFER.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fa950393a572829ba7e296c20cd08ff19a063b0f8441f950130fd677b1e38179"},{"asset_id":"PROFILE-ACTIO-PRESERVED-CLAIM-BUNDLE","binding_status":"BOUND","path":"profiles/overlays/ACTIO-PRESERVED-CLAIM-BUNDLE.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fbd99f029438c1c278f93d88d18a703db72e78f20c7049f344e5748c108f94c5"},{"asset_id":"PROFILE-ACTIO-DEFENSE-MAP","binding_status":"BOUND","path":"profiles/overlays/ACTIO-DEFENSE-MAP.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"05348c493e6046a3e5c55f9b516c0e862fe5aa753e1b2fc71624bb2b11f0c191"}]},"executor_binding_ref":{"asset_id":"BINDING-S2_00-CODE-EXECUTOR","binding_status":"EXTERNAL_TRUST_ROOT_PENDING_LIVE_ADMISSION","path":"deployment/stage2_code_executor_binding.yml","schema_id":"stage2_code_executor_binding.v2"},"loader_binding_ref":{"asset_id":"BINDING-S2_00-LEGACY-REFERENCE","binding_status":"BOUND","path":"deployment/stage2_loader_binding.yml","schema_id":"stage2_loader_binding.reference.v1","sha256":"02acead6cc840008342f0f1b48c0a17368b980592d94f474edf70cee7edf62da"},"module_manifest_ref":{"asset_id":"MANIFEST-MODULE","binding_status":"BOUND","path":"manifest/module_manifest.json","schema_id":"stage2_module_manifest.v1","sha256":"9f0ca25587444e228cc39419e2ad7b4ec5e1d7c0c53ad1faec2b91a0f058a16e"},"open_items":[{"open_id":"O-07","owner":"release_operator","required_evidence":"signed canary or production admission with current Stage 1 integrity closure","status":"OPEN_RELEASE_AUTHORIZATION"},{"open_id":"CEG-02-DIRECT-MCP-LIVE","owner":"AgentBackend_owner","required_evidence":"live code-executor.run_code outer result and inner single-JSON receipt with exact Agent/code/release binding","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-03-SECRET-INJECTION","owner":"AgentBackend_owner","required_evidence":"backend-injected or pre-registered Code Executor authentication with no plaintext credential in YAML or code","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-04-RUNTIME-IMAGE-PIN","owner":"Code_Executor_owner","required_evidence":"verified Python runtime image digest compatible with the bound httpx dependency","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-05-REQUEST-SIZE-TIMEOUT","owner":"Code_Executor_owner","required_evidence":"representative S2_00 Agent request accepted and completed within backend request-size and 300-second limits","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-06-BINARY-LOCALDOCS","owner":"localdocs_owner","required_evidence":"live read_binary_doc and write_binary_file byte-preservation receipt over the approved path set","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-07-ISOLATION-EGRESS","owner":"AgentBackend_security_owner","required_evidence":"workspace identity substitution and backend-enforced S2_00_LOCALDOCS_ONLY_V1 egress verification","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-08-BARRIER-ROUTE","owner":"Stage_2_integration_owner","required_evidence":"live status-last logical publish and exactly-one downstream route receipt","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"DEV-DETACHED-RELEASE-ENVELOPE","owner":"release_operator","required_evidence":"detached signed envelope admits the release and executor binding without a cross-file hash cycle","status":"OPEN_RELEASE_AUTHORIZATION"},{"open_id":"DOWNSTREAM-CASE-TYPE-REGISTRY","owner":"S2_20_case_type_registry_owner","required_evidence":"manifest/case_type_registry.yml exact path/hash and 137-row sign-off; not a direct S2_00 executable dependency","status":"PENDING_SEQUENTIAL_BIND"}],"release_class":"DEV_FIXTURE_RELEASE","release_digest":"adc76cb9869183ffbda99ad5400f9901bd6808605229de78ba7247a70d153ebd","release_digest_contract":{"algorithm_id":"STAGE2-RELEASE-DIGEST-V1","array_order":"PRESERVE_DECLARED_ORDER","canonicalization_algorithm_id":"STAGE2-CANONICAL-JSON-V1","digest_algorithm":"sha256","digest_scope":"ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_RELEASE_DIGEST","encoding":"UTF-8","line_termination":"LF_ONE_TRAILING_NEWLINE","non_finite_numbers_allowed":false,"object_key_order":"SORT_CODEPOINT"},"release_evidence":[{"evidence_id":"DEV-DRAFT-UNSIGNED-001","input_digest":"c2c6949b6ddfa1b74a048c633413f3452967155bcaa1110bcd41cde3aeceba8b","release_class":"DEV_FIXTURE_RELEASE","scope":"INLINE_CODE_EXECUTOR_AND_STRUCTURED_HANDOFF_OFFLINE_HASH_BOUND_VALIDATION_ONLY","signature":"PENDING_SEQUENTIAL_BIND","signer_id":"UNSIGNED_DRAFT","status":"PENDING_SEQUENTIAL_BIND"}],"release_id":"STAGE2-CLEAN-DEV-DRAFT-S2_00-HYBRID-HANDOFF-2026-08-30","release_status":"DRAFT_NOT_EXECUTABLE","retry_policies":[{"backoff_seconds":[0,1,3],"max_attempts":3,"non_retryable_classes":["HASH","SCHEMA","PRODUCER","CONSERVATION","RUN_BINDING"],"retry_policy_id":"S2-RETRY-TRANSIENT-READ-V1","retryable_codes":["TRANSIENT_READ_ERROR","TRANSIENT_LOCK_ERROR"]}],"schema_version":"stage2_release.v2","signature":"PENDING_SEQUENTIAL_BIND","stage1_sources":[{"adapter_id":"S2A-EVIDENCE-V3-ENVELOPE-V1","logical_input_id":"evidence_indexed","path":"evidence_indexed.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B1_quality_gate_evidence_indexed","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_contract_version","items"],"requirement_class":"EVIDENCE_EVENT_SCOPE","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-EVENTS-V1-ENVELOPE-V1","logical_input_id":"evidence_event_candidates","path":"evidence_event_candidates.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B2_quality_gate_event_candidates","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_version","items"],"requirement_class":"EVIDENCE_EVENT_SCOPE","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-CLIENT-GOAL-V8-V1","logical_input_id":"client_goal","path":"client_goal.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_A_client_goal","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["primary_goal","constraints","parties"],"requirement_class":"OPTIMIZATION_CONTEXT","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-DOMAIN-SCREENING-V1","logical_input_id":"domain_screening","path":"routing/domain_screening.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_A0_domain_screener_02","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["domain_screening"],"requirement_class":"ROUTING_PROFILE_BACKBONE","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-DUAL-SG01-V1","logical_input_id":"domain_activation_manifest","path":"routing/domain_activation_manifest.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_D0_domain_activation_gate","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["domain_activation_manifest"],"requirement_class":"ROUTING_PROFILE_BACKBONE","run_identity_pointer":null,"schema_ref":{"$id":"https://schemas.liti-agent.local/stage1/s5/domain_activation_manifest.schema.json","path":"signals/schemas/domain_activation_manifest.schema.json","sha256":"013a6ebd230ebe46dda665af9f6c4448b267444b44e7b8f701f2fae80a2ee92a"},"transaction_identity_pointer":null},{"adapter_id":"S2A-B1-GATE-V1","logical_input_id":"b1_evidence_indexed_gate","path":"quality_gates/B1_evidence_indexed_gate.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B12_gate_audit_finalizer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_contract_version","gate_id","overall_severity","hard_gate_findings","review_findings","stage2_auto_progression_allowed"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-B2-GATE-V1","logical_input_id":"b2_event_candidates_gate","path":"quality_gates/B2_event_candidates_gate.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B12_gate_audit_finalizer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_contract_version","gate_id","overall_severity","hard_gate_findings","review_findings","stage2_auto_progression_allowed"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-P1-HANDOFF-FLAT-V1","logical_input_id":"stage1_part1_soft_gate_handoff","path":"quality_gates/stage1_part1_soft_gate_handoff.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B2_SHA256_soft_gate_handoff_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_version","handoff_status","review_items","stage2_auto_progression_allowed","hard_gate_summary","review_item_conservation","digest_guard"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-BO-V8-LIST-V1","logical_input_id":"bo","path":"BO.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_BO_F0_final_bo_compiler_gate_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":[],"requirement_class":"IDENTITY_BACKBONE","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-SIGNAL-ALL-V1","logical_input_id":"signal_manifest","path":"signals/signal_manifest.json","path_rule":null,"producer_alias_id":"PA-SG-COMPILER-001","producer_id":"Task_C_BO_S0_signal_bundle_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["files","downstream_read_sets"],"requirement_class":"ROUTING_PROFILE_BACKBONE","run_identity_pointer":null,"schema_ref":{"$id":"https://schemas.liti-agent.local/stage1/s5/signal_manifest.schema.json","path":"signals/schemas/signal_manifest.schema.json","sha256":"5e72084780b82b29582c9ffcf48f3e4894d7c0b152e5ce8df394583c07dde681"},"transaction_identity_pointer":"/transaction_id"},{"adapter_id":"S2A-P2-HANDOFF-FLAT-V1","logical_input_id":"stage1_part2_review_handoff","path":"quality_gates/stage1_part2_review_handoff.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_BO_F0_final_bo_compiler_gate_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_version","status","review_items"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-LES-CURRENT-V8-V1","logical_input_id":"legal_effect_structures","path":"legal_effect_structures.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_LE_L2_final_structure_index_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":[],"requirement_class":"ROUTING_PROFILE_BACKBONE","run_identity_pointer":null,"schema_ref":{"$id":"https://schemas.liti-agent.local/stage1/part3/legal_effect_structures.schema.json","path":"platform/schemas/legal_effect_structures.schema.json","sha256":"fc962e8ae39f9bede64ba017297eded6413689204a065e00c3b3bdca8f1854df"},"transaction_identity_pointer":"/signal_manifest_transaction_id"},{"adapter_id":"S2A-P3-HANDOFF-WRAPPED-V1","logical_input_id":"stage1_part3_review_handoff","path":"quality_gates/stage1_part3_review_handoff.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_LE_L2_final_structure_index_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["stage1_part3_review_handoff"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-FACT-LEDGER-CURRENT-V8-V1","logical_input_id":"fact_ledger_base","path":"Fact_Ledger_base.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_FL_F2_final_fact_ledger_gate_and_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":[],"requirement_class":"IDENTITY_BACKBONE","run_identity_pointer":null,"schema_ref":{"$id":"https://schemas.liti-agent.local/stage1/part4/fact_ledger_base.schema.json","path":"platform/schemas/fact_ledger_base.schema.json","sha256":"b3f0e79ecb4c2f720f3e07e89154aadbd2327e4129cc703569fb5635240d2fe8"},"transaction_identity_pointer":null},{"adapter_id":"S2A-FACT-LEDGER-WRITER-REPORT-V1","logical_input_id":"fact_ledger_writer_report","path":"stage1_tmp/fact_ledger/fact_ledger_writer_report.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_FL_F2_final_fact_ledger_gate_and_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_version"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-P4-HANDOFF-WRAPPED-V1","logical_input_id":"stage1_part4_review_handoff","path":"quality_gates/stage1_part4_review_handoff.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_FL_F2_final_fact_ledger_gate_and_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["stage1_part4_review_handoff"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-SIGNAL-ALL-V1","logical_input_id":"signal_payload_family","path":null,"path_rule":"signals/","producer_alias_id":"PA-SG-COMPILER-001","producer_id":"Task_C_BO_S0_signal_bundle_writer","raw_hash_source":"MANIFEST_ROW","required_keys":[],"requirement_class":"SIGNAL_PAYLOAD","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null}]} +{"adapter_decisions":[{"adapter_id":"S2A-SIGNAL-ALL-V1","adapter_kind":"SIGNAL_ALL_EXPANSION","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"file_conservation_equation":"semantic_file_rows + integrity_only_file_rows = Counter(signal_manifest.files[])","global_signal_id_uniqueness_assumed":false,"integrity_only_kinds":["compatibility_view"],"manifest_selector":"/downstream_read_sets/stage2","physical_path_rule":"U/signals/","record_conservation_equation":"used_record_occurrences + unused_record_occurrences + unmapped_record_occurrences = records_from_semantic_files","record_occurrence_key":["manifest_transaction_id","file_path","record_ordinal","signal_id"],"row_order":"PRESERVE_MANIFEST_ORDER","row_source":"/files","semantic_kinds":["canonical","domain_signal"],"sentinel":["ALL"]},"schema_ref":"schemas/ingress.schema.json#/$defs/all_expansion_contract"},{"adapter_id":"S2A-DUAL-SG01-V1","adapter_kind":"DUAL_SG01_PROJECTION","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"comparison":"PARSED_CANONICAL_PROJECTION_EQUAL","payload_root":"/domain_activation_manifest","projection_json_pointers":["/schema_version","/signal_id","/status","/registry_version","/registry_index_sha256","/screening_sha256","/domain_entries","/active_domain_ids","/supporting_domain_ids","/monitor_domain_ids","/expected_runnable_domain_ids","/required_calculation_domains","/unrouted_material","/conservation_gate","/fail_open_policy","/review_items","/contract_guards"],"raw_hash_policy":"PRESERVE_AND_VERIFY_SEPARATELY","routing_path":"routing/domain_activation_manifest.json","set_semantics_json_pointers":["/active_domain_ids","/supporting_domain_ids","/monitor_domain_ids","/expected_runnable_domain_ids","/required_calculation_domains"],"signal_path":"signals/domain_activation_manifest.json"},"schema_ref":"schemas/ingress.schema.json#/$defs/dual_sg01_projection_contract"},{"adapter_id":"S2A-P1-HANDOFF-FLAT-V1","adapter_kind":"HANDOFF","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"count_field_required":false,"logical_input_id":"P1_REVIEW_HANDOFF","p1_digest_keys":["evidence_indexed_sha256","evidence_event_candidates_sha256","b1_gate_sha256","b2_gate_sha256","screening_sha256","activation_manifest_sha256","registry_index_sha256"],"review_items_json_pointer":"/review_items","schema_version":"stage1_part1_soft_gate_handoff.v1","seal_sources":["routing/domain_screening.json","routing/domain_activation_manifest.json","domains/_registry_index.json"],"source_stage":"P1","status_json_pointer":"/handoff_status","wrapper_json_pointer":""},"schema_ref":"schemas/ingress.schema.json#/$defs/handoff_adapter_contract"},{"adapter_id":"S2A-P2-HANDOFF-FLAT-V1","adapter_kind":"HANDOFF","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"count_field_required":false,"logical_input_id":"P2_REVIEW_HANDOFF","review_items_json_pointer":"/review_items","schema_version":"stage1_part2_review_handoff.v1","seal_sources":["BO.json","signals/signal_manifest.json"],"source_stage":"P2","status_json_pointer":"/status","wrapper_json_pointer":""},"schema_ref":"schemas/ingress.schema.json#/$defs/handoff_adapter_contract"},{"adapter_id":"S2A-P3-HANDOFF-WRAPPED-V1","adapter_kind":"HANDOFF","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"count_field_required":true,"logical_input_id":"P3_REVIEW_HANDOFF","review_items_json_pointer":"/review_items","schema_version":"stage1_part3_review_handoff.v1","seal_sources":["legal_effect_structures.json","validation_assets/routing/part3_receipt.json"],"source_stage":"P3","status_json_pointer":"/status","wrapper_json_pointer":"/stage1_part3_review_handoff"},"schema_ref":"schemas/ingress.schema.json#/$defs/handoff_adapter_contract"},{"adapter_id":"S2A-P4-HANDOFF-WRAPPED-V1","adapter_kind":"HANDOFF","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"count_field_required":true,"logical_input_id":"P4_REVIEW_HANDOFF","review_items_json_pointer":"/review_items","schema_version":"stage1_part4_review_handoff.v1","seal_sources":["Fact_Ledger_base.json","validation_assets/routing/part4_receipt.json","stage1_tmp/fact_ledger/fact_ledger_writer_report.json"],"source_stage":"P4","status_json_pointer":"/status","wrapper_json_pointer":"/stage1_part4_review_handoff"},"schema_ref":"schemas/ingress.schema.json#/$defs/handoff_adapter_contract"},{"adapter_id":"S2-REVIEW-MAP-V1","adapter_kind":"REVIEW_NORMALIZATION","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"aggregate_handoff_status_never_resolves_item":true,"handoff_status_mappings":[{"source_stage":"P1","source_value":"READY_NO_REVIEW","technical_disposition":"AVAILABLE"},{"source_stage":"P1","source_value":"READY_WITH_REVIEW","technical_disposition":"AVAILABLE_WITH_ISSUES"},{"source_stage":"P1","source_value":"BLOCKED","technical_disposition":"UNAVAILABLE"},{"source_stage":"P2","source_value":"PENDING_FINALIZE","technical_disposition":"AVAILABLE_WITH_ISSUES"},{"source_stage":"P2","source_value":"FINALIZED","technical_disposition":"AVAILABLE"},{"source_stage":"P3","source_value":"OPEN","technical_disposition":"AVAILABLE_WITH_ISSUES"},{"source_stage":"P3","source_value":"FINALIZED","technical_disposition":"AVAILABLE"},{"source_stage":"P4","source_value":"OPEN","technical_disposition":"AVAILABLE_WITH_ISSUES"},{"source_stage":"P4","source_value":"FINALIZED","technical_disposition":"AVAILABLE"}],"mappings":[{"mapping_id":"S2RM-001","normalized_partition":"SUPPORTED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"SUPPORTED","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-002","normalized_partition":"CONDITIONAL","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"CONDITIONAL","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-003","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"UNRESOLVED","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-004","normalized_partition":"EXCLUDED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"EXCLUDED","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-005","normalized_partition":"SUPPORTED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"observed","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-006","normalized_partition":"CONDITIONAL","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"inferred","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-007","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"contested","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-008","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"missing_required","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-009","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"review","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-010","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_STATUS","source_stage":"ANY","source_value":"NO_SUPPORT","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-011","normalized_partition":"CONDITIONAL","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"info","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-012","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"review","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-013","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"SOFT_WARNING","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-014","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"hard_warning","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-015","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"HARD_WARNING","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-016","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"block","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},{"mapping_id":"S2RM-017","normalized_partition":"UNRESOLVED","resolution_inference_allowed":false,"source_field_kind":"REVIEW_ITEM_SEVERITY","source_stage":"ANY","source_value":"BLOCK","unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"}],"normalized_partitions":["SUPPORTED","CONDITIONAL","UNRESOLVED","EXCLUDED","UNMAPPED"],"resolution_inference_allowed":false,"unknown_value_policy":"MAP_TO_UNMAPPED_AND_ISSUE"},"schema_ref":"schemas/review_status.schema.json#/$defs/review_mapping_row"},{"adapter_id":"S2A-BO-V8-LIST-V1","adapter_kind":"PRODUCER_SHAPE","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"logical_input_id":"BO","open_source_fields_policy":"PRESERVE_UNMODELED_FIELDS_WITH_RAW_HASH","producer_id":"Task_C_BO_F0_final_bo_compiler_gate_writer","required_item_fields":["BO_ID","id","BOType","ActionType","JuristicAct","Action","Reason","PriorAct","ReasonRefs","Legal_Keywords","core_field_base","amount","EvidenceTitles","Evidence","source_evidence_indexes","provenance","downstream_seed_refs","extensions"],"required_root_fields":[],"root_shape":"ARRAY","schema_contract_version":null},"schema_ref":"schemas/ingress.schema.json#/$defs/producer_shape_adapter"},{"adapter_id":"S2A-EVIDENCE-V3-ENVELOPE-V1","adapter_kind":"PRODUCER_SHAPE","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"logical_input_id":"EVIDENCE_INDEXED","open_source_fields_policy":"PRESERVE_UNMODELED_FIELDS_WITH_RAW_HASH","producer_id":"Task_B1_quality_gate_evidence_indexed","required_root_fields":["schema_contract_version","items"],"root_shape":"OBJECT_ENVELOPE","schema_contract_version":"evidence_indexed.v3"},"schema_ref":"schemas/ingress.schema.json#/$defs/producer_shape_adapter"},{"adapter_id":"S2A-EVENTS-V1-ENVELOPE-V1","adapter_kind":"PRODUCER_SHAPE","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"logical_input_id":"EVIDENCE_EVENT_CANDIDATES","open_source_fields_policy":"PRESERVE_UNMODELED_FIELDS_WITH_RAW_HASH","producer_id":"Task_B2_quality_gate_event_candidates","required_root_fields":["schema_version","items"],"root_shape":"OBJECT_ENVELOPE","schema_contract_version":"evidence_event_candidates.v1"},"schema_ref":"schemas/ingress.schema.json#/$defs/producer_shape_adapter"},{"adapter_id":"S2A-DOMAIN-CONFIG-V1","adapter_kind":"DOMAIN_CONFIG","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"accepted_schema_version":"stage1_domain_config.v1","depends_on_legal_dependency_allowed":false,"rebuttal_slot_synthesis_allowed":false,"required_slot_fields":["element_slots","opposing_fact_slots","defense_map","calculation_bindings","emits_signals"],"undeclared_slot_policy":"PRESERVE_AS_PROPOSED_NEW_SLOT_ISSUE"},"schema_ref":"schemas/ingress.schema.json#/$defs/domain_config_adapter_contract"},{"adapter_id":"S2A-DOMAIN-CONFIG-V2","adapter_kind":"DOMAIN_CONFIG","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"accepted_schema_version":"stage1_domain_config.v2","depends_on_legal_dependency_allowed":false,"rebuttal_slot_synthesis_allowed":false,"required_slot_fields":["element_slots","opposing_fact_slots","defense_map","calculation_bindings","emits_signals"],"undeclared_slot_policy":"PRESERVE_AS_PROPOSED_NEW_SLOT_ISSUE"},"schema_ref":"schemas/ingress.schema.json#/$defs/domain_config_adapter_contract"},{"adapter_id":"S2A-FACT-LEDGER-CURRENT-V8-V1","adapter_kind":"FACT_LEDGER","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"bo_source_bo_id_multiset_equality_required":true,"fact_id_pattern":"^F-[0-9]{3,}$","legacy_adapter_status":"DISABLED_NO_APPROVED_ADAPTER","producer_generation":"CURRENT_V8","required_row_fields":["fact_id","source_bo_id","domain_effects","calculation_requests"],"root_shape":"ARRAY"},"schema_ref":"schemas/ingress.schema.json#/$defs/fact_ledger_adapter_contract"},{"adapter_id":"PA-SG-COMPILER-001","adapter_kind":"PRODUCER_ALIAS","contract_status":"CLOSED_DECISION_FIXTURE_PENDING","decision":{"bidirectional_match_allowed":true,"global_alias_allowed":false,"orchestration_producer_id":"Task_C_BO_S0_signal_bundle_writer","schema_writer_id":"Task_C_BO_S0_canonical_signal_compiler","scope":"STAGE1_PART2_SIGNAL_TRANSACTION_ONLY"},"schema_ref":"schemas/ingress.schema.json#/$defs/producer_alias_contract"}],"authoring_root":"Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean","authorization_status":"DEV_VALIDATION_ONLY","bundle":{"active_profile_policy":"CASE_RUN_EXACT_RELEASE_SELECTED_SUBSET_ONLY","authority_release_status":"DEV_UNAVAILABLE","context_cohort_policy_id":"S2-CACHE-STRUCTURED-CONTEXT-HANDOFF-V2","downstream_hybrid_status":"OFFLINE_HYBRID_IMPLEMENTED_LIVE_ADMISSION_PENDING","executable":false,"handoff_contract_version":"S2_00_STRUCTURED_CONTEXT_HANDOFF_V2","mode":"STRUCTURAL_FIXTURE","reason_codes":["STRUCTURAL_FIXTURE_NOT_EXECUTABLE","AUTHORITY_RELEASE_UNAVAILABLE","S2_10_PLATFORM_MODEL_AND_LEGAL_ADMISSION_PENDING","DIRECT_MCP_LIVE_ADMISSION_PENDING"],"s2_10_agent_ref":{"asset_id":"AGENT-S2_10-HYBRID-OPAQUE","binding_status":"BOUND","path":"agent_scripts/Stage_2_S2_10.yml","schema_id":"liti_agent_yaml.v1","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513"},"s2_10_agent_sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513","s2_10_llm_binding_ref":{"asset_id":"BINDING-S2_10-LLM-OPAQUE","binding_status":"BOUND","path":"deployment/stage2_s2_10_llm_binding.yml","schema_id":"stage2_s2_10_llm_binding.v2","sha256":"90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95"},"s2_10_llm_binding_sha256":"90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95","selected_context_ordered_refs_sha256":"37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570","selected_context_refs":[]},"dependency_locks":{"downstream_availability":[{"asset_id":"CASE-TYPE-COVERAGE","binding_status":"BOUND","path":"manifest/case_type_coverage.json","schema_id":"stage2.case_type_coverage.v1","sha256":"34d6f758fc1486fa7330ede376e65b9448a6eaa971bd21472b0e4b7d9a5b8cba"},{"asset_id":"CORPUS-RELEASE","binding_status":"BOUND","path":"manifest/corpus_release.json","schema_id":"stage2.corpus_release.v1","sha256":"27d0c83e7a95342efdbe5be675b6d5b7d0ff8695eeec022ebc5aded3359fc981"}],"stage1":{"closure_scope":"REFERENCED_55_ONLY_NOT_FULL_STAGE1_RUNTIME_RELEASE","closure_snapshot_date":"2026-08-29","concrete_paths":[{"binding_status":"BOUND","lock_id":"S1-DEPLOY-001","path":"runtime_manifest.json","schema_id":"stage1_runtime_manifest.v1","sha256":"8964593a64a9b1bc90122054bb09eb3911827a06ed62dab0d6b7c745e7e18f54","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-002","path":"domains/_registry_index.json","schema_id":null,"sha256":"9f177ebf8860e20e05483967a2037f3baa09c2ac92c69ddeb260c04ca31ebf39","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-003","path":"signals/signal_registry.v2.json","schema_id":"signal_registry.v2","sha256":"4392b40da458102f8dd11b40b40ae3f694b7b5911849b050e2e4118c569e5ab0","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-004","path":"domains/E-00/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"5919f7ea1d7be02666b0c48aa6a66445e6d454fc2fbb21d7fe5154b0a1e68f6f","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-005","path":"domains/E-01/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"b557e92cd1b093bf31792dbcf5b62cab8ad064a65c4421e79f141e06b4cc2192","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-006","path":"domains/E-02/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"be407c980c28226a15406f85b5861b04a4e19a13870513ac6626349fc05ac434","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-007","path":"domains/E-03/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"7d3814f9b50cd5b33ef65a4eb778693552b3685bd369e765e9ac032734ebe23e","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-008","path":"domains/E-04/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"95a8c600cdce5a687f766788af0f763ee1b6a895e6ed80934afd28fe9a107e25","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-009","path":"domains/E-05/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"e50541018f47aa27de2f8b13ec3fa52210cf8456a6feed8356af78c1f1da144a","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-010","path":"domains/E-06/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"1e2bee36cb3c24dd37fc3beb3cf70236d531126c4f62ee97b5b42e55f4b0745c","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-011","path":"domains/E-07/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"22ac562084b1ce231b7257d099c18b6a4619defa2fc42504d590e0bcc494c5f8","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-012","path":"domains/E-08/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"4d545306d42120e8552dd953d4336ef6de828ea827779944ba73acfda3d3a8bb","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-013","path":"domains/E-09/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"7ef7340750094efeb372c397eb3134e21d62dda6988b1f6fa0a197b9963868e0","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-014","path":"domains/E-10/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"e8d4f45fa76ea9e09333256dd4ea36cd3dd963bf60c04814a2cb8dc90d152f0a","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-015","path":"domains/E-11/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"eb78d0188a0a2400307b1c34c8f8703c54cd86dd06b942c1709c44a8630a68e1","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-016","path":"domains/E-12/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"f336accdc6de10cdcc28c1190328054bca402fb77a2a9859d59fbaf5e84dd170","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-017","path":"domains/E-13/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"e27e2e3855b5868a3ec12c7093b872434702f2e465b73c0bfc948b516aa0fc35","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-018","path":"domains/E-14/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"a273148cc17f07d90cda500fa5cb7df30c9cd253f7b86048cf4f63495d36a156","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-019","path":"domains/E-15/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"7f37edddc101a08ed8a0e25f3a2c638e72571edc212ac91d96c1e33c51202a69","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-020","path":"domains/E-16/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"ae9af46ee31b6ef0dafedd35ccd7959a941d67d1a3dcc70e0b13647896873323","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-021","path":"domains/E-17/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"5c61f4486bdc968e4b30734b3c045404f0a711f47ea3abbe6c5c64652fb7f68c","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-022","path":"domains/E-18/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"74ff76148d175929bdeeeced77e9a9922d29b51ad3d00ae6711c43c55717692c","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-023","path":"domains/E-19/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"a8578f54a3fead3bbd35c62d7199b0f8aafb77f5d409a87236a55d2550fbfd37","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-024","path":"domains/E-20/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"29ee14cfe7789f004e6b6978d5360cf1bebe33bf88715df0cb47257993a11d00","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-025","path":"domains/E-21/domain_config.json","schema_id":"stage1_domain_config.v2","sha256":"4e1684a843d9e0c5af82f45332ad85abe94eda0c3ff0d578235d892aee39b908","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-026","path":"domains/EC-00/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"fe74de112b73289485dcead7e0fc7d270c794b3cf8a29ee00fab1eb64ba13861","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-027","path":"domains/X1/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"ad2fee7d206018f9a1f66e5fdf40dd67b686f6938099bad1ffc5d538db14ac57","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-028","path":"domains/X2/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"eba7d4671546bd66f1350d144ae0884f8beffb0dffdc147b45b9d5292676d46f","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-029","path":"domains/X3/domain_config.json","schema_id":"stage1_domain_config.v1","sha256":"8b67a638ae4a86aca3a2216974242b11ec39790162c9f366edfa91b02c3d270a","source_manifest":"domains/_registry_index.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-030","path":"platform/schemas/client_goal_domain_profiles.schema.json","schema_id":null,"sha256":"ae2bfe0d754a09cbae16b2c15bf1518fc23f9e1bda8fa1f5f949606c8e42c010","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-031","path":"platform/schemas/domain_fanout_plan.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s1/domain_fanout_plan.schema.json","sha256":"3b0948613a5996028b9c030a99f0b51d682f6035e019557756b1a15d43971113","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-032","path":"platform/schemas/domain_seed_output.schema.v3.json","schema_id":"https://schemas.liti-agent.local/stage1/s1/domain_seed_output.schema.v3.json","sha256":"992acf05dbccb34c65ead4e8c592f424e3b91672dc109cbd1bfa76a0a71a13c9","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-033","path":"platform/schemas/domain_slice.schema.v2.json","schema_id":"https://schemas.liti-agent.local/stage1/s1/domain_slice.schema.v2.json","sha256":"212a405088e7cf7ba2c65528a1c716938c946df7fe3bae3256b613051ed31aa3","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-034","path":"platform/schemas/fact_exception_pack.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part4/fact_exception_pack.schema.json","sha256":"4eba7e51ed46a99e3704bc2333169749f4a16935de26a8c8027c1cac98ea58cf","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-035","path":"platform/schemas/fact_ledger_base.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part4/fact_ledger_base.schema.json","sha256":"b3f0e79ecb4c2f720f3e07e89154aadbd2327e4129cc703569fb5635240d2fe8","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-036","path":"platform/schemas/fact_ledger_candidate_bundle.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part4/fact_ledger_candidate_bundle.schema.json","sha256":"4e481504fb795b2be510680a8fa88124f5763a8124462f7870be4125ed9a7730","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-037","path":"platform/schemas/legal_effect_structures.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part3/legal_effect_structures.schema.json","sha256":"fc962e8ae39f9bede64ba017297eded6413689204a065e00c3b3bdca8f1854df","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-038","path":"platform/schemas/structure_seed_bundle.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/part3/structure_seed_bundle.schema.json","sha256":"b7af9e422b6ac3876cffea39ec4f617eea76a631a57dfdfcd57d3785a83c667a","source_manifest":"runtime_manifest.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-039","path":"signals/_common/evidence_slot_status.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/evidence_slot_status.schema.json","sha256":"292b03960b187cef668b8635a8d7539fde7c31f0c20d01af52c4f6ff8519d7b1","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-040","path":"signals/_common/signal_item.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/signal_item.schema.json","sha256":"de8695f98041c06cf50c0d8d2ebc31e7b3c518ca9d39a27da940438704c58bb1","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-041","path":"signals/schemas/domain_activation_manifest.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/domain_activation_manifest.schema.json","sha256":"013a6ebd230ebe46dda665af9f6c4448b267444b44e7b8f701f2fae80a2ee92a","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-042","path":"signals/schemas/procedural_posture_relief_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/procedural_posture_relief_signals.schema.json","sha256":"fefb4317ad63088919b61777c71fe75ee6aa507b9f599dcf455d2af63dfc5e0d","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-043","path":"signals/schemas/party_capacity_standing_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/party_capacity_standing_signals.schema.json","sha256":"66de89ac53964166f6caabd50cbc03eb82dede0acf702d5e6d825c1d82ef81d0","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-044","path":"signals/schemas/governing_law_version_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/governing_law_version_signals.schema.json","sha256":"13a3f62f03356090d2cb24de2da0ba217928dfe8eb3c111d0f5e87c7df3119ee","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-045","path":"signals/schemas/legal_relation_lifecycle_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/legal_relation_lifecycle_signals.schema.json","sha256":"420613a5900c4360487b89b978efedde58f5ddc61644130e4b9e63ef8ab33d8b","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-046","path":"signals/schemas/timeline_notice_condition_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/timeline_notice_condition_signals.schema.json","sha256":"99c66208524155cea6bbd5e24fd26998cc9b653c89b24b569c793e36f1623d35","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-047","path":"signals/schemas/asset_right_state_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/asset_right_state_signals.schema.json","sha256":"fc34fbb3d33a284c3d57f3c278cbda8b3555ef26ee2f06b803fd2410ebce38b6","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-048","path":"signals/schemas/liability_causation_damage_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/liability_causation_damage_signals.schema.json","sha256":"34102cb8eeda80773eb62a5ee61e3d714bf90424ed5350dcac4b7bf873a72c5a","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-049","path":"signals/schemas/defense_exception_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/defense_exception_signals.schema.json","sha256":"010148c15e60e4d112b142f80b1723c06e34ba22b3edefae9e4371f2353b053e","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-050","path":"signals/schemas/evidence_proof_conflict_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/evidence_proof_conflict_signals.schema.json","sha256":"c419f568e28c06c629bc715aff7b0737b77e9c4871c91d4fae8f6ecf04196390","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-051","path":"signals/schemas/calculation_requirements.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/calculation_requirements.schema.json","sha256":"7fdb5ef0f50d7af22ac417abc4022cd238f5ab0dc942866420616729a9e3571f","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-052","path":"signals/schemas/remedy_enforcement_signals.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/remedy_enforcement_signals.schema.json","sha256":"999e1969b983748f209e9b5239f7edd0ec43bc642d9ea8fd7edbf34f9ce653f3","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-053","path":"signals/schemas/legal_effect_routes.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/legal_effect_routes.schema.json","sha256":"c24cb740c370aa2477199a0225be8291164ef5c787601fd962a370c642cc3cc0","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-054","path":"signals/schemas/domain_signal_envelope.schema.v2.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/domain_signal_envelope.schema.v2.json","sha256":"1483d6c5f98083f59172feff9b7c15b44d3ed789db5b6172d0de05f06e9d3fbc","source_manifest":"signals/signal_registry.v2.json"},{"binding_status":"BOUND","lock_id":"S1-DEPLOY-055","path":"signals/schemas/signal_manifest.schema.json","schema_id":"https://schemas.liti-agent.local/stage1/s5/signal_manifest.schema.json","sha256":"5e72084780b82b29582c9ffcf48f3e4894d7c0b152e5ce8df394583c07dde681","source_manifest":"signals/signal_registry.v2.json"}],"contract_manifest_ref":{"mode":"CONDITIONAL_RELOCATION_ONLY","path":null,"sha256":null,"status":"NOT_REQUIRED_DEFAULT_PATHS"},"expected_concrete_path_count":55,"full_stage1_runtime_release_status":"STAGE1_NOT_RELEASE_READY"},"stage2_direct":[{"asset_id":"AGENT-S2_10-HYBRID-OPAQUE","binding_status":"BOUND","path":"agent_scripts/Stage_2_S2_10.yml","schema_id":"liti_agent_yaml.v1","sha256":"0eed6f354d3539a58cc3953dd3a4eb30bb14ba515e2c90380df5830d404a8513"},{"asset_id":"BINDING-S2_10-LLM-OPAQUE","binding_status":"BOUND","path":"deployment/stage2_s2_10_llm_binding.yml","schema_id":"stage2_s2_10_llm_binding.v2","sha256":"90f23bbe5b0c91374e550e9c841f708f6df51d2692f626e4a05367d4dffd9f95"},{"asset_id":"AUTHORITY-REGISTRY","binding_status":"BOUND","path":"registry/authority/authority_registry.yml","schema_id":"stage2.authority_registry.v1","sha256":"b4663610e18098cf650fbd5846f18dcbc96074b3012c68f03f67402509c7d142"},{"asset_id":"AUTHORITY-RELEASE","binding_status":"BOUND","path":"manifest/authority_release.json","schema_id":"stage2.authority_release.v1","sha256":"cc683958377eea44756de6e9c74fc925da66dd60cc84363f5bec36584f5b2967"},{"asset_id":"PROFILE-EC-00_CONTRACT_GENERAL","binding_status":"BOUND","path":"registry/substantive/EC-00_contract_general.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"a4f7abd9957b3a9e58d05dd559c3c869bf96fd88ea18968a363c6e606e3c0aa8"},{"asset_id":"PROFILE-E-01_JURISTIC_ACT_VALIDITY","binding_status":"BOUND","path":"registry/substantive/E-01_juristic_act_validity.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"0a0d2676f96be321a7bb974b8e4fb1981ab43773f271c99dc86a70cb17d27c99"},{"asset_id":"PROFILE-E-02_CONTRACT_MONEY_CLAIM","binding_status":"BOUND","path":"registry/substantive/E-02_contract_money_claim.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"08e252bccc718ccc44a258bc540afc2567e509239399f4017550df32bac97824"},{"asset_id":"PROFILE-E-03_PARTIES_LIABILITY_SUCCESSION","binding_status":"BOUND","path":"registry/substantive/E-03_parties_liability_succession.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"4e841d5f640b06a1b52060b82dda39605369cbe26d81655b7918673437e4519c"},{"asset_id":"PROFILE-E-04_UNJUST_ENRICHMENT","binding_status":"BOUND","path":"registry/substantive/E-04_unjust_enrichment.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"31a5966d4c14940e810f870bf4382f1be642f7716ebaaa35193ef32e9b9d95c7"},{"asset_id":"PROFILE-E-05_TORT_GENERAL","binding_status":"BOUND","path":"registry/substantive/E-05_tort_general.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fe6ea92fc10c4196e0ac83b990bf2606e24024030e7b0002fecdae9f8f50221a"},{"asset_id":"PROFILE-E-06_PROFESSIONAL_LIABILITY","binding_status":"BOUND","path":"registry/substantive/E-06_professional_liability.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"efa7a4f6cda90c04a06558a9d0e1efe13b25169d55d03e36d55d72fcdd096a15"},{"asset_id":"PROFILE-E-07_CONSTRUCTION_DEFECT","binding_status":"BOUND","path":"registry/substantive/E-07_construction_defect.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"78c76cb0485a4acf741109aee05f634f51c3a6904044c613080b21c3978a09f9"},{"asset_id":"PROFILE-E-08_LEASE_DEPOSIT","binding_status":"BOUND","path":"registry/substantive/E-08_lease_deposit.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"dbe1886671edff49b8b0251eed567d1b3e4e5d748456434262286ca7acf990db"},{"asset_id":"PROFILE-E-09_REGISTRY_TRANSFER_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-09_registry_transfer_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"d18946322a8cb528146ac0aa0a2880c41ac2056671fd237b928341ceb8561002"},{"asset_id":"PROFILE-E-10_SECURED_REGISTRY","binding_status":"BOUND","path":"registry/substantive/E-10_secured_registry.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"5819c26ec8478e12a12e340c93a9d23cc410833e1192c2315a1986c9d48a1eba"},{"asset_id":"PROFILE-E-11_POSSESSION_VINDICATION","binding_status":"BOUND","path":"registry/substantive/E-11_possession_vindication.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"5e6d6460be4ec0f2ba0096305b9ae39f5f430d107337d0d7e07aac1a9d599aaf"},{"asset_id":"PROFILE-E-12_CO_OWNERSHIP_BOUNDARY","binding_status":"BOUND","path":"registry/substantive/E-12_co_ownership_boundary.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"62cfbf6a6c5c5edc8b912e348be1c4d4f7dd4c20c722e57e5554058f0caafb4a"},{"asset_id":"PROFILE-E-13_CREDITOR_PRESERVATION","binding_status":"BOUND","path":"registry/substantive/E-13_creditor_preservation.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"c60e6d67c7e3985d1f6ce44027726282f931934e3e66b44f06ccd26845ca2f45"},{"asset_id":"PROFILE-E-14_EXECUTION_LINKED_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-14_execution_linked_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"45d4b3ab46e1c8834c3b3eec72a28d05e39cb78a9ec2c2de07b5e55e81aeebd7"},{"asset_id":"PROFILE-E-15_SUCCESSION_FAMILY_PROPERTY","binding_status":"BOUND","path":"registry/substantive/E-15_succession_family_property.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"f691b67295ad8e634b34519cf71156caf6eaf73cef54577615ab79462f8aa766"},{"asset_id":"PROFILE-E-16_NEGOTIABLE_INSTRUMENTS","binding_status":"BOUND","path":"registry/substantive/E-16_negotiable_instruments.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"7720d83b64e7a44720788f6ff3c7cc8ceaf5195377a1e61b52de2144989a1194"},{"asset_id":"PROFILE-E-17_LABOR_WAGE_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-17_labor_wage_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"168ce5e41104937cf7ab946944f060b82cfe16066425897f07bb9e837b8689ea"},{"asset_id":"PROFILE-E-18_ORG_RESOLUTION_STATUS","binding_status":"BOUND","path":"registry/substantive/E-18_org_resolution_status.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"59cf689f886dfaf4ad00dbe6142f5b11c37d0062660f7467aa43427462aa075f"},{"asset_id":"PROFILE-E-19_INSURANCE_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-19_insurance_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"0fb4793446c155e7a0b4469c9b8c1be1323112eef55ca4c495307c1af56e754c"},{"asset_id":"PROFILE-E-20_IP_CLAIMS","binding_status":"BOUND","path":"registry/substantive/E-20_ip_claims.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fe07f20d149a2c4deac2e46de299cd78f0bf829f58c615a86c45f542d30804e1"},{"asset_id":"PROFILE-E-21_MEDIA_PERSONALITY_RIGHTS","binding_status":"BOUND","path":"registry/substantive/E-21_media_personality_rights.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"951260bbe8ef116d682f49f3679106c7144ca68f8078f5c10b3459667c1f5b2c"},{"asset_id":"PROFILE-E-00_RESIDUAL_UNROUTED","binding_status":"BOUND","path":"profiles/crosscut/E-00_residual_unrouted.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"639e507147a6132f9e41ccd0b00d1b5450d6a9629991629b173d5bcaf5b6df39"},{"asset_id":"PROFILE-X1_NOTICE_LIFECYCLE","binding_status":"BOUND","path":"profiles/crosscut/X1_notice_lifecycle.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"9af8b5b5f3a8196a10308d1ee34e0788d9adeac7a99e142e449490d14316faef"},{"asset_id":"PROFILE-X2_ASSET_IDENTITY_LINEAGE","binding_status":"BOUND","path":"profiles/crosscut/X2_asset_identity_lineage.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"6c1e437944b3a62b60ee7acad5ec9177fefac2991fe970338acc27efcc8a13ad"},{"asset_id":"PROFILE-X3_PROCEDURE_STANDING_RELIEF","binding_status":"BOUND","path":"profiles/crosscut/X3_procedure_standing_relief.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"f3b794bef82566e569232ba02c63ae148099143d8d1ab84de1c4fb76105e65df"},{"asset_id":"PROFILE-X4_RESPONSE_ADMISSION_DEFENSE","binding_status":"BOUND","path":"profiles/crosscut/X4_response_admission_defense.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"57851ed55b3351a8139df29224d74b7cb8427708bbf6de00ecd27c44089df32e"},{"asset_id":"PROFILE-SL-AUTO_MOTOR_VEHICLE","binding_status":"BOUND","path":"profiles/special_law/SL-AUTO_motor_vehicle.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"7234a7ab890f242e6b7aa811528e34c0c85779007cffbf135da0bfff36d61d91"},{"asset_id":"PROFILE-SL-INDUSTRIAL_ACCIDENT","binding_status":"BOUND","path":"profiles/special_law/SL-INDUSTRIAL_ACCIDENT.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"10d78b8031df7d89f835afc9ca60fab36648e561c729780204c80885f4dd097f"},{"asset_id":"PROFILE-SL-PRODUCT_LIABILITY","binding_status":"BOUND","path":"profiles/special_law/SL-PRODUCT_LIABILITY.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"1d5ee8eddcf005fdb33df58f591a8948aa3e891a3a5656d919394ff0b39eea53"},{"asset_id":"PROFILE-SL-RESIDENTIAL_LEASE","binding_status":"BOUND","path":"profiles/special_law/SL-RESIDENTIAL_LEASE.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"3e2efd1b3c0524a305eafedc5ec91e05be989a6e55c1501ed161a9b8f8151fd7"},{"asset_id":"PROFILE-SL-COMMERCIAL_LEASE","binding_status":"BOUND","path":"profiles/special_law/SL-COMMERCIAL_LEASE.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"5aebed9ab56bc00eb7e0511f9fe5db8c3a42f1e81b28d58c38d487aa91cbab3e"},{"asset_id":"PROFILE-SL-LABOR","binding_status":"BOUND","path":"profiles/special_law/SL-LABOR.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"6b2542bb64f4c1d164b1ccd2ce5099fad29bcea4333d9e34bd0fb1667b9ced4a"},{"asset_id":"PROFILE-SL-STATE_LIABILITY","binding_status":"BOUND","path":"profiles/special_law/SL-STATE_LIABILITY.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"5b2fe6d57b918384a619efbd04360ddfaf7720d384136f69d359ccb0b6c853d3"},{"asset_id":"PROFILE-SL-IP-PATENT","binding_status":"BOUND","path":"profiles/special_law/SL-IP-PATENT.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"d0651a8a50d0b18d25931063314ecec284988c5a18fab68d08a317d725bb3066"},{"asset_id":"PROFILE-SL-IP-COPYRIGHT","binding_status":"BOUND","path":"profiles/special_law/SL-IP-COPYRIGHT.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"d4349677f68346f79709ea23a05957262f6426f976388bba45f05c617035eb78"},{"asset_id":"PROFILE-SL-IP-OTHER","binding_status":"BOUND","path":"profiles/special_law/SL-IP-OTHER.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fc3e2772252a3a90571663ebcfbc8e18dbf526977f4a3d77c4705ef8aa8dd6a8"},{"asset_id":"PROFILE-SL-MEDIA","binding_status":"BOUND","path":"profiles/special_law/SL-MEDIA.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"e0d5d287befd1578a06c64a265d5c4426af5772b6764018f6a15f00aac3b498c"},{"asset_id":"PROFILE-SL-TRANSPORT_MARITIME","binding_status":"BOUND","path":"profiles/special_law/SL-TRANSPORT_MARITIME.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"6e87a77c25f8c358f1d7d35ca165b1229c2cb422a440c91768ad80e520d35456"},{"asset_id":"PROFILE-SL-CONSUMER_CONTRACT","binding_status":"BOUND","path":"profiles/special_law/SL-CONSUMER_CONTRACT.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"afd8bbba102614d8d08f2e7a5384f6ca24e1309c1f52feb13906613f2ced51b7"},{"asset_id":"PROFILE-ACTIO-MORTGAGE","binding_status":"BOUND","path":"profiles/overlays/ACTIO-MORTGAGE.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"7e584f7b4e4d19132e9575071f87d05965fb77fa2ca870911fe9974a7c511c4d"},{"asset_id":"PROFILE-ACTIO-MORTGAGE-CREATION","binding_status":"BOUND","path":"profiles/overlays/ACTIO-MORTGAGE-CREATION.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"e727690ee23883e011d49eba01cfd87abc5482641c2bda519c0a39518e1769ee"},{"asset_id":"PROFILE-ACTIO-ENCUMBERED-TRANSFER","binding_status":"BOUND","path":"profiles/overlays/ACTIO-ENCUMBERED-TRANSFER.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fa950393a572829ba7e296c20cd08ff19a063b0f8441f950130fd677b1e38179"},{"asset_id":"PROFILE-ACTIO-PRESERVED-CLAIM-BUNDLE","binding_status":"BOUND","path":"profiles/overlays/ACTIO-PRESERVED-CLAIM-BUNDLE.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"fbd99f029438c1c278f93d88d18a703db72e78f20c7049f344e5748c108f94c5"},{"asset_id":"PROFILE-ACTIO-DEFENSE-MAP","binding_status":"BOUND","path":"profiles/overlays/ACTIO-DEFENSE-MAP.yml","schema_id":"stage2.llm_context_profile.v1","sha256":"05348c493e6046a3e5c55f9b516c0e862fe5aa753e1b2fc71624bb2b11f0c191"}]},"executor_binding_ref":{"asset_id":"BINDING-S2_00-CODE-EXECUTOR","binding_status":"EXTERNAL_TRUST_ROOT_PENDING_LIVE_ADMISSION","path":"deployment/stage2_code_executor_binding.yml","schema_id":"stage2_code_executor_binding.v3"},"loader_binding_ref":{"asset_id":"BINDING-S2_00-LEGACY-REFERENCE","binding_status":"BOUND","path":"deployment/stage2_loader_binding.yml","schema_id":"stage2_loader_binding.reference.v1","sha256":"120313754c597541680a8dc7d9d59c6f8b2438c053553f030ef89171709dd23a"},"module_manifest_ref":{"asset_id":"MANIFEST-MODULE","binding_status":"BOUND","path":"manifest/module_manifest.json","schema_id":"stage2_module_manifest.v1","sha256":"fe406f0392b34d89b3070bae637ecda67f0572ffb59d0d86d30d2ca843c7cebe"},"open_items":[{"open_id":"O-07","owner":"release_operator","required_evidence":"signed canary or production admission with current Stage 1 integrity closure","status":"OPEN_RELEASE_AUTHORIZATION"},{"open_id":"CEG-02-DIRECT-MCP-LIVE","owner":"AgentBackend_owner","required_evidence":"live code-executor.run_code outer result and inner single-JSON receipt with exact Agent/code/release binding","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-03-SECRET-INJECTION","owner":"AgentBackend_owner","required_evidence":"backend-injected or pre-registered Code Executor authentication with no plaintext credential in YAML or code","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-04-RUNTIME-IMAGE-PIN","owner":"Code_Executor_owner","required_evidence":"verified Python runtime image digest compatible with the bound httpx dependency","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-05-REQUEST-SIZE-TIMEOUT","owner":"Code_Executor_owner","required_evidence":"representative S2_00 Agent request accepted and completed within backend request-size and 300-second limits","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-06-BINARY-LOCALDOCS","owner":"localdocs_owner","required_evidence":"live read_binary_doc and write_binary_file byte-preservation receipt over the approved path set","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-07-ISOLATION-EGRESS","owner":"AgentBackend_security_owner","required_evidence":"workspace identity substitution and backend-enforced S2_00_LOCALDOCS_ONLY_V1 egress verification","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"CEG-08-BARRIER-ROUTE","owner":"Stage_2_integration_owner","required_evidence":"live status-last logical publish and exactly-one downstream route receipt","status":"OPEN_EXTERNAL_BACKEND"},{"open_id":"DEV-DETACHED-RELEASE-ENVELOPE","owner":"release_operator","required_evidence":"detached signed envelope admits the release and executor binding without a cross-file hash cycle","status":"OPEN_RELEASE_AUTHORIZATION"},{"open_id":"DOWNSTREAM-CASE-TYPE-REGISTRY","owner":"S2_20_case_type_registry_owner","required_evidence":"manifest/case_type_registry.yml exact path/hash and 137-row sign-off; not a direct S2_00 executable dependency","status":"PENDING_SEQUENTIAL_BIND"}],"release_class":"DEV_FIXTURE_RELEASE","release_digest":"0fec6e67baedd7dcc36591348facd769a9c5a8c8e7052bbc395bdb6d2b96f6bc","release_digest_contract":{"algorithm_id":"STAGE2-RELEASE-DIGEST-V1","array_order":"PRESERVE_DECLARED_ORDER","canonicalization_algorithm_id":"STAGE2-CANONICAL-JSON-V1","digest_algorithm":"sha256","digest_scope":"ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_RELEASE_DIGEST","encoding":"UTF-8","line_termination":"LF_ONE_TRAILING_NEWLINE","non_finite_numbers_allowed":false,"object_key_order":"SORT_CODEPOINT"},"release_evidence":[{"evidence_id":"DEV-DRAFT-UNSIGNED-001","input_digest":"c2c6949b6ddfa1b74a048c633413f3452967155bcaa1110bcd41cde3aeceba8b","release_class":"DEV_FIXTURE_RELEASE","scope":"INLINE_CODE_EXECUTOR_AND_STRUCTURED_HANDOFF_OFFLINE_HASH_BOUND_VALIDATION_ONLY","signature":"PENDING_SEQUENTIAL_BIND","signer_id":"UNSIGNED_DRAFT","status":"PENDING_SEQUENTIAL_BIND"}],"release_id":"STAGE2-CLEAN-DEV-DRAFT-S2_00-HYBRID-HANDOFF-2026-08-30","release_status":"DRAFT_NOT_EXECUTABLE","retry_policies":[{"backoff_seconds":[0,1,3],"max_attempts":3,"non_retryable_classes":["HASH","SCHEMA","PRODUCER","CONSERVATION","RUN_BINDING"],"retry_policy_id":"S2-RETRY-TRANSIENT-READ-V1","retryable_codes":["TRANSIENT_READ_ERROR","TRANSIENT_LOCK_ERROR"]}],"schema_version":"stage2_release.v2","signature":"PENDING_SEQUENTIAL_BIND","stage1_sources":[{"adapter_id":"S2A-EVIDENCE-V3-ENVELOPE-V1","logical_input_id":"evidence_indexed","path":"evidence_indexed.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B1_quality_gate_evidence_indexed","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_contract_version","items"],"requirement_class":"EVIDENCE_EVENT_SCOPE","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-EVENTS-V1-ENVELOPE-V1","logical_input_id":"evidence_event_candidates","path":"evidence_event_candidates.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B2_quality_gate_event_candidates","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_version","items"],"requirement_class":"EVIDENCE_EVENT_SCOPE","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-CLIENT-GOAL-V8-V1","logical_input_id":"client_goal","path":"client_goal.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_A_client_goal","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["primary_goal","constraints","parties"],"requirement_class":"OPTIMIZATION_CONTEXT","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-DOMAIN-SCREENING-V1","logical_input_id":"domain_screening","path":"routing/domain_screening.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_A0_domain_screener_02","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["domain_screening"],"requirement_class":"ROUTING_PROFILE_BACKBONE","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-DUAL-SG01-V1","logical_input_id":"domain_activation_manifest","path":"routing/domain_activation_manifest.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_D0_domain_activation_gate","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["domain_activation_manifest"],"requirement_class":"ROUTING_PROFILE_BACKBONE","run_identity_pointer":null,"schema_ref":{"$id":"https://schemas.liti-agent.local/stage1/s5/domain_activation_manifest.schema.json","path":"signals/schemas/domain_activation_manifest.schema.json","sha256":"013a6ebd230ebe46dda665af9f6c4448b267444b44e7b8f701f2fae80a2ee92a"},"transaction_identity_pointer":null},{"adapter_id":"S2A-B1-GATE-V1","logical_input_id":"b1_evidence_indexed_gate","path":"quality_gates/B1_evidence_indexed_gate.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B12_gate_audit_finalizer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_contract_version","gate_id","overall_severity","hard_gate_findings","review_findings","stage2_auto_progression_allowed"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-B2-GATE-V1","logical_input_id":"b2_event_candidates_gate","path":"quality_gates/B2_event_candidates_gate.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B12_gate_audit_finalizer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_contract_version","gate_id","overall_severity","hard_gate_findings","review_findings","stage2_auto_progression_allowed"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-P1-HANDOFF-FLAT-V1","logical_input_id":"stage1_part1_soft_gate_handoff","path":"quality_gates/stage1_part1_soft_gate_handoff.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_B2_SHA256_soft_gate_handoff_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_version","handoff_status","review_items","stage2_auto_progression_allowed","hard_gate_summary","review_item_conservation","digest_guard"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-BO-V8-LIST-V1","logical_input_id":"bo","path":"BO.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_BO_F0_final_bo_compiler_gate_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":[],"requirement_class":"IDENTITY_BACKBONE","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-SIGNAL-ALL-V1","logical_input_id":"signal_manifest","path":"signals/signal_manifest.json","path_rule":null,"producer_alias_id":"PA-SG-COMPILER-001","producer_id":"Task_C_BO_S0_signal_bundle_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["files","downstream_read_sets"],"requirement_class":"ROUTING_PROFILE_BACKBONE","run_identity_pointer":null,"schema_ref":{"$id":"https://schemas.liti-agent.local/stage1/s5/signal_manifest.schema.json","path":"signals/schemas/signal_manifest.schema.json","sha256":"5e72084780b82b29582c9ffcf48f3e4894d7c0b152e5ce8df394583c07dde681"},"transaction_identity_pointer":"/transaction_id"},{"adapter_id":"S2A-P2-HANDOFF-FLAT-V1","logical_input_id":"stage1_part2_review_handoff","path":"quality_gates/stage1_part2_review_handoff.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_BO_F0_final_bo_compiler_gate_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_version","status","review_items"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-LES-CURRENT-V8-V1","logical_input_id":"legal_effect_structures","path":"legal_effect_structures.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_LE_L2_final_structure_index_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":[],"requirement_class":"ROUTING_PROFILE_BACKBONE","run_identity_pointer":null,"schema_ref":{"$id":"https://schemas.liti-agent.local/stage1/part3/legal_effect_structures.schema.json","path":"platform/schemas/legal_effect_structures.schema.json","sha256":"fc962e8ae39f9bede64ba017297eded6413689204a065e00c3b3bdca8f1854df"},"transaction_identity_pointer":"/signal_manifest_transaction_id"},{"adapter_id":"S2A-P3-HANDOFF-WRAPPED-V1","logical_input_id":"stage1_part3_review_handoff","path":"quality_gates/stage1_part3_review_handoff.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_LE_L2_final_structure_index_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["stage1_part3_review_handoff"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-FACT-LEDGER-CURRENT-V8-V1","logical_input_id":"fact_ledger_base","path":"Fact_Ledger_base.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_FL_F2_final_fact_ledger_gate_and_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":[],"requirement_class":"IDENTITY_BACKBONE","run_identity_pointer":null,"schema_ref":{"$id":"https://schemas.liti-agent.local/stage1/part4/fact_ledger_base.schema.json","path":"platform/schemas/fact_ledger_base.schema.json","sha256":"b3f0e79ecb4c2f720f3e07e89154aadbd2327e4129cc703569fb5635240d2fe8"},"transaction_identity_pointer":null},{"adapter_id":"S2A-FACT-LEDGER-WRITER-REPORT-V1","logical_input_id":"fact_ledger_writer_report","path":"stage1_tmp/fact_ledger/fact_ledger_writer_report.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_FL_F2_final_fact_ledger_gate_and_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["schema_version"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-P4-HANDOFF-WRAPPED-V1","logical_input_id":"stage1_part4_review_handoff","path":"quality_gates/stage1_part4_review_handoff.json","path_rule":null,"producer_alias_id":null,"producer_id":"Task_C_FL_F2_final_fact_ledger_gate_and_writer","raw_hash_source":"UNAVAILABLE_DEV","required_keys":["stage1_part4_review_handoff"],"requirement_class":"INTEGRITY_CORROBORATOR","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null},{"adapter_id":"S2A-SIGNAL-ALL-V1","logical_input_id":"signal_payload_family","path":null,"path_rule":"signals/","producer_alias_id":"PA-SG-COMPILER-001","producer_id":"Task_C_BO_S0_signal_bundle_writer","raw_hash_source":"MANIFEST_ROW","required_keys":[],"requirement_class":"SIGNAL_PAYLOAD","run_identity_pointer":null,"schema_ref":null,"transaction_identity_pointer":null}]} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/migration/actio_assets_receipt.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/migration/actio_assets_receipt.json new file mode 100644 index 00000000..dd906f64 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/migration/actio_assets_receipt.json @@ -0,0 +1,186 @@ +{ + "schema_version": "stage2_actio_migration_receipt.v1", + "receipt_id": "S2-ACTIO-MIGRATION-PENDING", + "status": "PENDING_KOREAN_LAWYER_AND_AUTHORITY_REVIEW", + "execution_eligible": false, + "path_base": "Default_Agent/Stage_2_Clean", + "source_root_ref": "../../사해행위취소소송관련기존자산", + "expected_source_count": 10, + "observed_source_count": 10, + "completed_migration_count": 0, + "rows": [ + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-01", + "raw_path": "../../사해행위취소소송관련기존자산/actio_pauliana_calc_v1.txt", + "raw_basename_utf8_hex": "616374696f5f7061756c69616e615f63616c635f76312e747874", + "nfc_display_name": "actio_pauliana_calc_v1.txt", + "raw_sha256": "57dd4a10a982c4b344239f37dcdeab9a388f2dd77ba41bdf89698d2bf8d5ba27", + "byte_length": 22913, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + }, + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-02", + "raw_path": "../../사해행위취소소송관련기존자산/actio_pauliana_calc_v3_mini_v1.json", + "raw_basename_utf8_hex": "616374696f5f7061756c69616e615f63616c635f76335f6d696e695f76312e6a736f6e", + "nfc_display_name": "actio_pauliana_calc_v3_mini_v1.json", + "raw_sha256": "551e0965de9061181beb9a019188fe54ced2b6ef74f979e2cd79eb4edadf1f09", + "byte_length": 11803, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + }, + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-03", + "raw_path": "../../사해행위취소소송관련기존자산/actio_pauliana_case_type_determination.csv", + "raw_basename_utf8_hex": "616374696f5f7061756c69616e615f636173655f747970655f64657465726d696e6174696f6e2e637376", + "nfc_display_name": "actio_pauliana_case_type_determination.csv", + "raw_sha256": "fed50482bff95e8644c912d8e03ccbfe1ea74844a15cf25826fa2d8a681e3786", + "byte_length": 1605, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + }, + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-04", + "raw_path": "../../사해행위취소소송관련기존자산/actio_pauliana_mortgage_actio_calc_both_v1.txt", + "raw_basename_utf8_hex": "616374696f5f7061756c69616e615f6d6f7274676167655f616374696f5f63616c635f626f74685f76312e747874", + "nfc_display_name": "actio_pauliana_mortgage_actio_calc_both_v1.txt", + "raw_sha256": "40405566257145b34c3e539861cf472aa5dc3c73f4f983136da58605eb352e83", + "byte_length": 11138, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + }, + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-05", + "raw_path": "../../사해행위취소소송관련기존자산/actio_pauliana_mortgage_v1.txt", + "raw_basename_utf8_hex": "616374696f5f7061756c69616e615f6d6f7274676167655f76312e747874", + "nfc_display_name": "actio_pauliana_mortgage_v1.txt", + "raw_sha256": "91b8949dfed4bbe8a9553eef0775a39d1967e0c86432e6f8c13f6e8381befdb4", + "byte_length": 21449, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + }, + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-06", + "raw_path": "../../사해행위취소소송관련기존자산/mortgage_fraudulent_act_module_v1_mini_v1.json", + "raw_basename_utf8_hex": "6d6f7274676167655f6672617564756c656e745f6163745f6d6f64756c655f76315f6d696e695f76312e6a736f6e", + "nfc_display_name": "mortgage_fraudulent_act_module_v1_mini_v1.json", + "raw_sha256": "ee5a29a871a82ec09ddf4e7cf5ea31be0e09314a178ba4de716bef427a0c218e", + "byte_length": 12141, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + }, + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-07", + "raw_path": "../../사해행위취소소송관련기존자산/부담부_부동산소유권이전_사해행위_가액배상_모듈.md", + "raw_basename_utf8_hex": "e18487e185aee18483e185a1e186b7e18487e185ae5fe18487e185aee18483e185a9e186bce18489e185a1e186abe18489e185a9e1848be185b2e18480e185afe186abe1848be185b5e1848ce185a5e186ab5fe18489e185a1e18492e185a2e18492e185a2e186bce1848be185b15fe18480e185a1e1848be185a2e186a8e18487e185a2e18489e185a1e186bc5fe18486e185a9e18483e185b2e186af2e6d64", + "nfc_display_name": "부담부_부동산소유권이전_사해행위_가액배상_모듈.md", + "raw_sha256": "424b3d34b30e4c2822d79ab7ec6a3b0645213a743141b72da021365c3f8553ac", + "byte_length": 15743, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + }, + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-08", + "raw_path": "../../사해행위취소소송관련기존자산/사해행위취소_가액배상_최종화게이트_모듈.md", + "raw_basename_utf8_hex": "e18489e185a1e18492e185a2e18492e185a2e186bce1848be185b1e1848ee185b1e18489e185a95fe18480e185a1e1848be185a2e186a8e18487e185a2e18489e185a1e186bc5fe1848ee185ace1848ce185a9e186bce18492e185aae18480e185a6e1848be185b5e18490e185b35fe18486e185a9e18483e185b2e186af2e6d64", + "nfc_display_name": "사해행위취소_가액배상_최종화게이트_모듈.md", + "raw_sha256": "794e7133e3b06d9e38314cc06aad5320faf5269870424e27fcdb2db1dff58478", + "byte_length": 15011, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + }, + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-09", + "raw_path": "../../사해행위취소소송관련기존자산/사해행위취소_피보전채권번들_검증_모듈.md", + "raw_basename_utf8_hex": "e18489e185a1e18492e185a2e18492e185a2e186bce1848be185b1e1848ee185b1e18489e185a95fe18491e185b5e18487e185a9e1848ce185a5e186abe1848ee185a2e18480e185afe186abe18487e185a5e186abe18483e185b3e186af5fe18480e185a5e186b7e1848ce185b3e186bc5fe18486e185a9e18483e185b2e186af2e6d64", + "nfc_display_name": "사해행위취소_피보전채권번들_검증_모듈.md", + "raw_sha256": "7b91276b363c8a99d5c4cb2ce2a5154b7003ee2dc081fa8e4db83dd0e5c0275b", + "byte_length": 13130, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + }, + { + "source_asset_id": "ACTIO-MIGRATION-SOURCE-10", + "raw_path": "../../사해행위취소소송관련기존자산/사해행위취소_항변통합_모듈.md", + "raw_basename_utf8_hex": "e18489e185a1e18492e185a2e18492e185a2e186bce1848be185b1e1848ee185b1e18489e185a95fe18492e185a1e186bce18487e185a7e186abe18490e185a9e186bce18492e185a1e186b85fe18486e185a9e18483e185b2e186af2e6d64", + "nfc_display_name": "사해행위취소_항변통합_모듈.md", + "raw_sha256": "be5e43c5b16758ae2f6e02d37d1735bfb871c46ab014bc81f3ae197463a569fc", + "byte_length": 13211, + "extracted_propositions": [], + "official_authority_checks": [], + "migration_disposition": null, + "correction_or_drop_reason": "PENDING_PROPOSITION_EXTRACTION_AND_OFFICIAL_AUTHORITY_COMPARISON", + "canonical_targets": [], + "canonical_target_sha256s": [], + "reviewer_id": null, + "review_status": "PENDING_KOREAN_LAWYER_REVIEW" + } + ], + "unresolved_reason_codes": [ + "PROPOSITION_EXTRACTION_NOT_COMPLETED", + "OFFICIAL_AUTHORITY_COMPARISON_NOT_COMPLETED", + "CANONICAL_TARGETS_NOT_BOUND", + "KOREAN_LAWYER_REVIEW_NOT_COMPLETED" + ], + "guards": { + "legacy_source_runtime_load_forbidden": true, + "legacy_source_fallback_forbidden": true, + "unverified_proposition_migration_forbidden": true, + "source_hash_mutation_forbidden": true, + "pending_receipt_is_not_approval": true + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/migration/legacy_asset_disposition.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/migration/legacy_asset_disposition.yml new file mode 100644 index 00000000..33e56b92 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/migration/legacy_asset_disposition.yml @@ -0,0 +1,32 @@ +{ + "schema_version": "stage2_legacy_asset_disposition.v1", + "registry_id": "S2-LEGACY-16-DISPOSITION-PENDING", + "status": "PENDING_SOURCE_DISCOVERY_AND_REVIEW", + "execution_eligible": false, + "audit_is_runtime_dependency": false, + "path_base": "Default_Agent/Stage_2_Clean", + "expected_source_root_ref": "../../../../Default_Agent_updated/Modules_and_New_Rules", + "expected_row_count": 16, + "completed_disposition_count": 0, + "allowed_final_dispositions": ["MIGRATE", "REBUILD", "DROP"], + "rows": [ + {"legacy_asset_id":"LEGACY-01","expected_basename_nfc":"금전채권_이행기_시효_지연손해금_작성규칙.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-02","expected_basename_nfc":"상호속용_영업양수인_책임_작성규칙.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-03","expected_basename_nfc":"법정변제충당_담보채무_지분말소_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-04","expected_basename_nfc":"담보권실행경매_공신력_항변_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-05","expected_basename_nfc":"미등기건물_토지사용_부당이득_부진정연대_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-06","expected_basename_nfc":"차임감정표_법률상_기준선택_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-07","expected_basename_nfc":"임대차보증금_부당이득_배제검토_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-08","expected_basename_nfc":"부담부_부동산소유권이전_사해행위_가액배상_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-09","expected_basename_nfc":"사해행위취소_피보전채권번들_검증_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-10","expected_basename_nfc":"사해행위취소_항변통합_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-11","expected_basename_nfc":"사해행위취소_가액배상_최종화게이트_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-12","expected_basename_nfc":"유치권소멸_건물인도_부당이득반환_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-13","expected_basename_nfc":"상속포기_배우자단독상속_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-14","expected_basename_nfc":"통지_도달_법률효과_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-15","expected_basename_nfc":"별지목록_등기부_asset_alias_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"}, + {"legacy_asset_id":"LEGACY-16","expected_basename_nfc":"피고답변서_항변추출_모듈.md","source_presence_status":"PENDING_SOURCE_DISCOVERY","raw_path":null,"raw_sha256":null,"disposition":null,"canonical_targets":[],"reason_code":"PENDING_SOURCE_AND_SEMANTIC_REVIEW","review_status":"PENDING"} + ], + "unresolved_reason_codes": ["LEGACY_SOURCE_ROOT_NOT_OBSERVED","DISPOSITION_AUDIT_NOT_COMPLETED","CANONICAL_TARGETS_NOT_BOUND","KOREAN_LAWYER_REVIEW_NOT_COMPLETED"], + "guards": {"runtime_load_forbidden":true,"runtime_fallback_forbidden":true,"basename_only_merge_forbidden":true,"pending_row_is_not_disposition":true,"audit_is_non_blocking_until_unique_required_proposition_found":true} +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_authority_law_value_release.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_authority_law_value_release.py new file mode 100644 index 00000000..c489f6ce --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_authority_law_value_release.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +"""Build or check the offline authority/law-value release inventory. + +This program performs local, explicit-path hashing only. It never retrieves +an authority, verifies a legal proposition, signs a release, or promotes a +release beyond the honest ``DEV_UNAVAILABLE`` state. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re +from typing import Any, Mapping, Sequence + + +REGISTRY_REF = "registry/authority/authority_registry.yml" +REGISTRY_SCHEMA_VERSION = "stage2.authority_registry.v1" +SCHEMA_VERSION_PATTERN = re.compile( + r'^schema_version:\s*["\']?([^"\'\s#]+)["\']?\s*(?:#.*)?$' +) + + +class AuthorityReleaseBuildError(ValueError): + """Raised when an explicit local release input is invalid.""" + + +def _output_bytes(payload: Mapping[str, Any]) -> bytes: + return (json.dumps(payload, ensure_ascii=False, indent=2) + "\n").encode("utf-8") + + +def _sha256(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest() + + +def _contained_file(root: Path, relative_path: str) -> Path: + if not isinstance(relative_path, str) or not relative_path: + raise AuthorityReleaseBuildError("AUTHORITY_ASSET_PATH_INVALID") + root = root.resolve() + target = (root / relative_path).resolve() + if root not in target.parents or not target.is_file(): + raise AuthorityReleaseBuildError(f"AUTHORITY_ASSET_UNAVAILABLE:{relative_path}") + return target + + +def _registry_schema_version(raw: bytes) -> str: + text = raw.decode("utf-8", errors="strict") + for line in text.splitlines(): + match = SCHEMA_VERSION_PATTERN.fullmatch(line.strip()) + if match: + return match.group(1) + raise AuthorityReleaseBuildError("AUTHORITY_REGISTRY_SCHEMA_VERSION_MISSING") + + +def _inventory_rows(root: Path, spec: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + seen: set[str] = set() + for item in spec: + if not isinstance(item, Mapping): + raise AuthorityReleaseBuildError("AUTHORITY_SPEC_ROW_INVALID") + relative_path = item.get("path") + if not isinstance(relative_path, str) or not relative_path: + raise AuthorityReleaseBuildError("AUTHORITY_SPEC_PATH_INVALID") + if relative_path in seen: + raise AuthorityReleaseBuildError(f"AUTHORITY_SPEC_DUPLICATE_PATH:{relative_path}") + seen.add(relative_path) + target = _contained_file(root, relative_path) + raw = target.read_bytes() + approval_status = item.get("approval_status", "PENDING_LEGAL_CONTENT") + if not isinstance(approval_status, str) or not approval_status: + raise AuthorityReleaseBuildError( + f"AUTHORITY_SPEC_APPROVAL_STATUS_INVALID:{relative_path}" + ) + rows.append( + { + "path": relative_path, + "sha256": _sha256(raw), + "size_bytes": len(raw), + "approval_status": approval_status, + } + ) + return sorted(rows, key=lambda row: row["path"]) + + +def build_release(root: Path, spec: Sequence[Mapping[str, Any]]) -> dict[str, Any]: + """Return the current canonical, unsigned pending release shape. + + Explicit inventory rows are hashed and recorded as captures, but their + mere presence cannot establish official-source provenance, legal review, + a detached signature, or live admission. The result therefore remains a + non-executable development release. + """ + + root = root.resolve() + registry_path = _contained_file(root, REGISTRY_REF) + registry_raw = registry_path.read_bytes() + registry_schema_version = _registry_schema_version(registry_raw) + if registry_schema_version != REGISTRY_SCHEMA_VERSION: + raise AuthorityReleaseBuildError( + f"AUTHORITY_REGISTRY_SCHEMA_VERSION_INVALID:{registry_schema_version}" + ) + captures = _inventory_rows(root, spec) + return { + "schema_version": "stage2.authority_release.v1", + "release_id": "AUTHORITY-RELEASE-DEV-UNAVAILABLE", + "status": "DEV_UNAVAILABLE", + "release_class": "DEV_FIXTURE_RELEASE", + "sealed": False, + "signed": False, + "executable": False, + "created_at": None, + "as_of": None, + "registry": { + "path": REGISTRY_REF, + "sha256": _sha256(registry_raw), + "required_status": "VERIFIED", + "schema_version": REGISTRY_SCHEMA_VERSION, + }, + "captures": captures, + "law_value_dependencies": [], + "selected_common_authority_ids": [], + "signature": None, + "unresolved": [ + "OFFICIAL_AUTHORITY_NOT_CAPTURED", + "AUTHORITY_REGISTRY_NOT_LEGALLY_VERIFIED", + "RELEASE_HASH_SET_NOT_SEALED", + "RELEASE_SIGNATURE_MISSING", + ], + "guards": { + "glob_selection_forbidden": True, + "unlisted_capture_ingest_forbidden": True, + "unverified_registry_row_use_forbidden": True, + "production_or_canary_use_forbidden": True, + }, + "hash_binding_status": "DEV_HASH_BOUND_UNSIGNED", + } + + +def write_or_check(output: Path, expected: bytes, *, check: bool) -> int: + """Write expected bytes, or perform read-only exact-byte drift detection.""" + + digest = _sha256(expected) + if check: + if not output.is_file(): + print( + json.dumps( + {"status": "DRIFT", "reason": "OUTPUT_MISSING", "output": str(output)}, + sort_keys=True, + ) + ) + return 1 + actual = output.read_bytes() + if actual != expected: + print( + json.dumps( + { + "status": "DRIFT", + "reason": "OUTPUT_BYTES_MISMATCH", + "actual_sha256": _sha256(actual), + "expected_sha256": digest, + "output": str(output), + }, + sort_keys=True, + ) + ) + return 1 + print(json.dumps({"status": "PASS", "output_sha256": digest}, sort_keys=True)) + return 0 + output.parent.mkdir(parents=True, exist_ok=True) + output.write_bytes(expected) + print(json.dumps({"status": "BUILT", "output_sha256": digest}, sort_keys=True)) + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("root", type=Path) + parser.add_argument("spec", type=Path, help="JSON list of {path, approval_status}") + parser.add_argument("output", type=Path) + parser.add_argument( + "--check", + action="store_true", + help="read-only exact-byte drift check; never creates or modifies output", + ) + args = parser.parse_args() + spec = json.loads(args.spec.read_text(encoding="utf-8")) + if not isinstance(spec, list): + raise AuthorityReleaseBuildError("AUTHORITY_SPEC_LIST_REQUIRED") + payload = build_release(args.root, spec) + return write_or_check(args.output, _output_bytes(payload), check=args.check) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_authority_law_value_release.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_authority_law_value_release.txt new file mode 100644 index 00000000..c489f6ce --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_authority_law_value_release.txt @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +"""Build or check the offline authority/law-value release inventory. + +This program performs local, explicit-path hashing only. It never retrieves +an authority, verifies a legal proposition, signs a release, or promotes a +release beyond the honest ``DEV_UNAVAILABLE`` state. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re +from typing import Any, Mapping, Sequence + + +REGISTRY_REF = "registry/authority/authority_registry.yml" +REGISTRY_SCHEMA_VERSION = "stage2.authority_registry.v1" +SCHEMA_VERSION_PATTERN = re.compile( + r'^schema_version:\s*["\']?([^"\'\s#]+)["\']?\s*(?:#.*)?$' +) + + +class AuthorityReleaseBuildError(ValueError): + """Raised when an explicit local release input is invalid.""" + + +def _output_bytes(payload: Mapping[str, Any]) -> bytes: + return (json.dumps(payload, ensure_ascii=False, indent=2) + "\n").encode("utf-8") + + +def _sha256(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest() + + +def _contained_file(root: Path, relative_path: str) -> Path: + if not isinstance(relative_path, str) or not relative_path: + raise AuthorityReleaseBuildError("AUTHORITY_ASSET_PATH_INVALID") + root = root.resolve() + target = (root / relative_path).resolve() + if root not in target.parents or not target.is_file(): + raise AuthorityReleaseBuildError(f"AUTHORITY_ASSET_UNAVAILABLE:{relative_path}") + return target + + +def _registry_schema_version(raw: bytes) -> str: + text = raw.decode("utf-8", errors="strict") + for line in text.splitlines(): + match = SCHEMA_VERSION_PATTERN.fullmatch(line.strip()) + if match: + return match.group(1) + raise AuthorityReleaseBuildError("AUTHORITY_REGISTRY_SCHEMA_VERSION_MISSING") + + +def _inventory_rows(root: Path, spec: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + seen: set[str] = set() + for item in spec: + if not isinstance(item, Mapping): + raise AuthorityReleaseBuildError("AUTHORITY_SPEC_ROW_INVALID") + relative_path = item.get("path") + if not isinstance(relative_path, str) or not relative_path: + raise AuthorityReleaseBuildError("AUTHORITY_SPEC_PATH_INVALID") + if relative_path in seen: + raise AuthorityReleaseBuildError(f"AUTHORITY_SPEC_DUPLICATE_PATH:{relative_path}") + seen.add(relative_path) + target = _contained_file(root, relative_path) + raw = target.read_bytes() + approval_status = item.get("approval_status", "PENDING_LEGAL_CONTENT") + if not isinstance(approval_status, str) or not approval_status: + raise AuthorityReleaseBuildError( + f"AUTHORITY_SPEC_APPROVAL_STATUS_INVALID:{relative_path}" + ) + rows.append( + { + "path": relative_path, + "sha256": _sha256(raw), + "size_bytes": len(raw), + "approval_status": approval_status, + } + ) + return sorted(rows, key=lambda row: row["path"]) + + +def build_release(root: Path, spec: Sequence[Mapping[str, Any]]) -> dict[str, Any]: + """Return the current canonical, unsigned pending release shape. + + Explicit inventory rows are hashed and recorded as captures, but their + mere presence cannot establish official-source provenance, legal review, + a detached signature, or live admission. The result therefore remains a + non-executable development release. + """ + + root = root.resolve() + registry_path = _contained_file(root, REGISTRY_REF) + registry_raw = registry_path.read_bytes() + registry_schema_version = _registry_schema_version(registry_raw) + if registry_schema_version != REGISTRY_SCHEMA_VERSION: + raise AuthorityReleaseBuildError( + f"AUTHORITY_REGISTRY_SCHEMA_VERSION_INVALID:{registry_schema_version}" + ) + captures = _inventory_rows(root, spec) + return { + "schema_version": "stage2.authority_release.v1", + "release_id": "AUTHORITY-RELEASE-DEV-UNAVAILABLE", + "status": "DEV_UNAVAILABLE", + "release_class": "DEV_FIXTURE_RELEASE", + "sealed": False, + "signed": False, + "executable": False, + "created_at": None, + "as_of": None, + "registry": { + "path": REGISTRY_REF, + "sha256": _sha256(registry_raw), + "required_status": "VERIFIED", + "schema_version": REGISTRY_SCHEMA_VERSION, + }, + "captures": captures, + "law_value_dependencies": [], + "selected_common_authority_ids": [], + "signature": None, + "unresolved": [ + "OFFICIAL_AUTHORITY_NOT_CAPTURED", + "AUTHORITY_REGISTRY_NOT_LEGALLY_VERIFIED", + "RELEASE_HASH_SET_NOT_SEALED", + "RELEASE_SIGNATURE_MISSING", + ], + "guards": { + "glob_selection_forbidden": True, + "unlisted_capture_ingest_forbidden": True, + "unverified_registry_row_use_forbidden": True, + "production_or_canary_use_forbidden": True, + }, + "hash_binding_status": "DEV_HASH_BOUND_UNSIGNED", + } + + +def write_or_check(output: Path, expected: bytes, *, check: bool) -> int: + """Write expected bytes, or perform read-only exact-byte drift detection.""" + + digest = _sha256(expected) + if check: + if not output.is_file(): + print( + json.dumps( + {"status": "DRIFT", "reason": "OUTPUT_MISSING", "output": str(output)}, + sort_keys=True, + ) + ) + return 1 + actual = output.read_bytes() + if actual != expected: + print( + json.dumps( + { + "status": "DRIFT", + "reason": "OUTPUT_BYTES_MISMATCH", + "actual_sha256": _sha256(actual), + "expected_sha256": digest, + "output": str(output), + }, + sort_keys=True, + ) + ) + return 1 + print(json.dumps({"status": "PASS", "output_sha256": digest}, sort_keys=True)) + return 0 + output.parent.mkdir(parents=True, exist_ok=True) + output.write_bytes(expected) + print(json.dumps({"status": "BUILT", "output_sha256": digest}, sort_keys=True)) + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("root", type=Path) + parser.add_argument("spec", type=Path, help="JSON list of {path, approval_status}") + parser.add_argument("output", type=Path) + parser.add_argument( + "--check", + action="store_true", + help="read-only exact-byte drift check; never creates or modifies output", + ) + args = parser.parse_args() + spec = json.loads(args.spec.read_text(encoding="utf-8")) + if not isinstance(spec, list): + raise AuthorityReleaseBuildError("AUTHORITY_SPEC_LIST_REQUIRED") + payload = build_release(args.root, spec) + return write_or_check(args.output, _output_bytes(payload), check=args.check) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_corpus_snapshot.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_corpus_snapshot.py new file mode 100644 index 00000000..2dc6ea8f --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_corpus_snapshot.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python3 +"""Create a deterministic, schema-conformant corpus build receipt.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re +from typing import Any + + +DEPLOYMENT_ROOT = Path(__file__).resolve().parents[1] +COLLECTION_SCHEMA_REF = "corpus/weaviate_collection.schema.json" +HEADING = re.compile(r"^(#{1,6})\s+(.+?)\s*$", re.MULTILINE) + + +def _canonical(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + + +def _output_bytes(value: Any) -> bytes: + """Serialize receipts in the repository's stable reviewable JSON form.""" + + return (json.dumps(value, ensure_ascii=False, indent=2) + "\n").encode("utf-8") + + +def _digest(value: Any) -> str: + return hashlib.sha256(_canonical(value)).hexdigest() + + +def chunks(path: Path) -> list[dict[str, object]]: + raw = path.read_bytes() + text = raw.decode("utf-8", errors="strict") + matches = list(HEADING.finditer(text)) + rows: list[dict[str, object]] = [] + for index, match in enumerate(matches): + start = match.start() + end = matches[index + 1].start() if index + 1 < len(matches) else len(text) + content = text[start:end] + rows.append( + { + "section_path": match.group(2), + "char_start": start, + "char_end": end, + "chunk_sha256": hashlib.sha256(content.encode("utf-8")).hexdigest(), + } + ) + return rows or [ + { + "section_path": "ROOT", + "char_start": 0, + "char_end": len(text), + "chunk_sha256": hashlib.sha256(raw).hexdigest(), + } + ] + + +def build_receipt( + raw_root: Path, + *, + deployment_root: Path = DEPLOYMENT_ROOT, + collection_name: str | None = None, + tenant: str | None = None, +) -> dict[str, Any]: + """Return the exact ``corpus_build_receipt`` schema contract. + + Local chunking does not constitute Korean-lawyer approval or live MCP + admission. Accordingly, even a nonempty local build remains ineligible for + production and exposes those missing approvals explicitly. + """ + + source_rows: list[dict[str, Any]] = [] + chunk_rows: list[dict[str, Any]] = [] + for path in sorted(raw_root.glob("CT-*/*.md")): + rel = path.relative_to(raw_root).as_posix() + raw = path.read_bytes() + source_id = "SRC-" + hashlib.sha256(rel.encode("utf-8")).hexdigest()[:24] + source_rows.append( + { + "source_document_id": source_id, + "source_path": rel, + "source_document_sha256": hashlib.sha256(raw).hexdigest(), + } + ) + for ordinal, row in enumerate(chunks(path), 1): + chunk_rows.append( + { + "chunk_id": f"{source_id}-CH-{ordinal:04d}", + "source_document_id": source_id, + **row, + } + ) + + collection_schema_path = deployment_root / COLLECTION_SCHEMA_REF + if not collection_schema_path.is_file(): + raise FileNotFoundError(collection_schema_path) + collection_schema_sha256 = hashlib.sha256(collection_schema_path.read_bytes()).hexdigest() + if not source_rows: + return { + "schema_version": "stage2_corpus_build_receipt.v1", + "receipt_id": "S2-CORPUS-BUILD-PENDING", + "build_status": "PENDING_SOURCE_AND_OPERATOR_BUILD", + "build_executed": False, + "created_at": None, + "collection_contract_status": "PENDING_CORPUS_BUILD", + "collection_schema_ref": COLLECTION_SCHEMA_REF, + "collection_schema_sha256": collection_schema_sha256, + "collection_name": None, + "tenant": None, + "snapshot_id": None, + "snapshot_sha256": None, + "source_document_count": 0, + "chunk_count": 0, + "approved_case_type_ids": [], + "components": [ + { + "component_id": component_id, + "status": "PENDING", + "configuration_digest": None, + "evidence_refs": [], + } + for component_id in ("CHUNKER", "EMBEDDING", "RERANKER", "INDEX", "CROSSWALK") + ], + "legal_review_status": "PENDING_KOREAN_LAWYER_REVIEW", + "live_mcp_admission_status": "PENDING_LIVE_MCP_ADMISSION", + "production_eligible": False, + "unresolved_reason_codes": [ + "CORPUS_BUILD_NOT_EXECUTED", + "LEGAL_REVIEW_NOT_COMPLETED", + "LIVE_MCP_ADMISSION_NOT_COMPLETED", + "RAW_CORPUS_NOT_PROVIDED", + ], + "guards": { + "raw_corpus_runtime_ingest_forbidden": True, + "directory_scan_forbidden": True, + "filter_relaxation_forbidden": True, + "embedded_instruction_execution_forbidden": True, + "unapproved_chunk_use_forbidden": True, + }, + } + + snapshot_scope = {"source_rows": source_rows, "chunk_rows": chunk_rows} + snapshot_sha256 = _digest(snapshot_scope) + chunker_digest = _digest( + {"heading_pattern": HEADING.pattern, "fallback_section": "ROOT", "algorithm": "SHA256"} + ) + evidence_refs = sorted(row["source_path"] for row in source_rows) + return { + "schema_version": "stage2_corpus_build_receipt.v1", + "receipt_id": f"S2-CORPUS-BUILD-{snapshot_sha256[:24]}", + "build_status": "BUILT_NOT_LEGALLY_REVIEWED", + "build_executed": True, + "created_at": None, + "collection_contract_status": "PENDING_CORPUS_BUILD", + "collection_schema_ref": COLLECTION_SCHEMA_REF, + "collection_schema_sha256": collection_schema_sha256, + "collection_name": collection_name, + "tenant": tenant, + "snapshot_id": f"SNAP-{snapshot_sha256[:24]}", + "snapshot_sha256": snapshot_sha256, + "source_document_count": len(source_rows), + "chunk_count": len(chunk_rows), + "approved_case_type_ids": [], + "components": [ + { + "component_id": "CHUNKER", + "status": "BUILT_NOT_ADMITTED", + "configuration_digest": chunker_digest, + "evidence_refs": evidence_refs, + }, + *[ + { + "component_id": component_id, + "status": "PENDING", + "configuration_digest": None, + "evidence_refs": [], + } + for component_id in ("EMBEDDING", "RERANKER", "INDEX", "CROSSWALK") + ], + ], + "legal_review_status": "PENDING_KOREAN_LAWYER_REVIEW", + "live_mcp_admission_status": "PENDING_LIVE_MCP_ADMISSION", + "production_eligible": False, + "unresolved_reason_codes": [ + "LEGAL_REVIEW_NOT_COMPLETED", + "LIVE_MCP_ADMISSION_NOT_COMPLETED", + "EMBEDDING_NOT_BUILT", + "RERANKER_NOT_BUILT", + "INDEX_NOT_BUILT", + "CROSSWALK_NOT_BUILT", + ], + "guards": { + "raw_corpus_runtime_ingest_forbidden": True, + "directory_scan_forbidden": True, + "filter_relaxation_forbidden": True, + "embedded_instruction_execution_forbidden": True, + "unapproved_chunk_use_forbidden": True, + }, + } + + +def write_or_check(output: Path, expected: bytes, *, check: bool) -> int: + """Write expected bytes, or perform read-only exact-byte drift detection.""" + + expected_sha256 = hashlib.sha256(expected).hexdigest() + if check: + if not output.is_file(): + print( + json.dumps( + {"status": "DRIFT", "reason": "OUTPUT_MISSING", "output": str(output)}, + sort_keys=True, + ) + ) + return 1 + actual = output.read_bytes() + if actual != expected: + print( + json.dumps( + { + "status": "DRIFT", + "reason": "OUTPUT_BYTES_MISMATCH", + "actual_sha256": hashlib.sha256(actual).hexdigest(), + "expected_sha256": expected_sha256, + "output": str(output), + }, + sort_keys=True, + ) + ) + return 1 + print( + json.dumps( + {"status": "PASS", "output_sha256": expected_sha256}, sort_keys=True + ) + ) + return 0 + output.parent.mkdir(parents=True, exist_ok=True) + output.write_bytes(expected) + print( + json.dumps( + {"status": "BUILT", "output_sha256": expected_sha256}, sort_keys=True + ) + ) + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("raw_root", type=Path) + parser.add_argument("output", type=Path) + parser.add_argument("--deployment-root", type=Path, default=DEPLOYMENT_ROOT) + parser.add_argument("--collection-name") + parser.add_argument("--tenant") + parser.add_argument( + "--check", + action="store_true", + help="read-only exact-byte drift check; never creates or modifies output", + ) + args = parser.parse_args() + payload = build_receipt( + args.raw_root, + deployment_root=args.deployment_root, + collection_name=args.collection_name, + tenant=args.tenant, + ) + return write_or_check(args.output, _output_bytes(payload), check=args.check) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_corpus_snapshot.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_corpus_snapshot.txt new file mode 100644 index 00000000..2dc6ea8f --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_corpus_snapshot.txt @@ -0,0 +1,273 @@ +#!/usr/bin/env python3 +"""Create a deterministic, schema-conformant corpus build receipt.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re +from typing import Any + + +DEPLOYMENT_ROOT = Path(__file__).resolve().parents[1] +COLLECTION_SCHEMA_REF = "corpus/weaviate_collection.schema.json" +HEADING = re.compile(r"^(#{1,6})\s+(.+?)\s*$", re.MULTILINE) + + +def _canonical(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + + +def _output_bytes(value: Any) -> bytes: + """Serialize receipts in the repository's stable reviewable JSON form.""" + + return (json.dumps(value, ensure_ascii=False, indent=2) + "\n").encode("utf-8") + + +def _digest(value: Any) -> str: + return hashlib.sha256(_canonical(value)).hexdigest() + + +def chunks(path: Path) -> list[dict[str, object]]: + raw = path.read_bytes() + text = raw.decode("utf-8", errors="strict") + matches = list(HEADING.finditer(text)) + rows: list[dict[str, object]] = [] + for index, match in enumerate(matches): + start = match.start() + end = matches[index + 1].start() if index + 1 < len(matches) else len(text) + content = text[start:end] + rows.append( + { + "section_path": match.group(2), + "char_start": start, + "char_end": end, + "chunk_sha256": hashlib.sha256(content.encode("utf-8")).hexdigest(), + } + ) + return rows or [ + { + "section_path": "ROOT", + "char_start": 0, + "char_end": len(text), + "chunk_sha256": hashlib.sha256(raw).hexdigest(), + } + ] + + +def build_receipt( + raw_root: Path, + *, + deployment_root: Path = DEPLOYMENT_ROOT, + collection_name: str | None = None, + tenant: str | None = None, +) -> dict[str, Any]: + """Return the exact ``corpus_build_receipt`` schema contract. + + Local chunking does not constitute Korean-lawyer approval or live MCP + admission. Accordingly, even a nonempty local build remains ineligible for + production and exposes those missing approvals explicitly. + """ + + source_rows: list[dict[str, Any]] = [] + chunk_rows: list[dict[str, Any]] = [] + for path in sorted(raw_root.glob("CT-*/*.md")): + rel = path.relative_to(raw_root).as_posix() + raw = path.read_bytes() + source_id = "SRC-" + hashlib.sha256(rel.encode("utf-8")).hexdigest()[:24] + source_rows.append( + { + "source_document_id": source_id, + "source_path": rel, + "source_document_sha256": hashlib.sha256(raw).hexdigest(), + } + ) + for ordinal, row in enumerate(chunks(path), 1): + chunk_rows.append( + { + "chunk_id": f"{source_id}-CH-{ordinal:04d}", + "source_document_id": source_id, + **row, + } + ) + + collection_schema_path = deployment_root / COLLECTION_SCHEMA_REF + if not collection_schema_path.is_file(): + raise FileNotFoundError(collection_schema_path) + collection_schema_sha256 = hashlib.sha256(collection_schema_path.read_bytes()).hexdigest() + if not source_rows: + return { + "schema_version": "stage2_corpus_build_receipt.v1", + "receipt_id": "S2-CORPUS-BUILD-PENDING", + "build_status": "PENDING_SOURCE_AND_OPERATOR_BUILD", + "build_executed": False, + "created_at": None, + "collection_contract_status": "PENDING_CORPUS_BUILD", + "collection_schema_ref": COLLECTION_SCHEMA_REF, + "collection_schema_sha256": collection_schema_sha256, + "collection_name": None, + "tenant": None, + "snapshot_id": None, + "snapshot_sha256": None, + "source_document_count": 0, + "chunk_count": 0, + "approved_case_type_ids": [], + "components": [ + { + "component_id": component_id, + "status": "PENDING", + "configuration_digest": None, + "evidence_refs": [], + } + for component_id in ("CHUNKER", "EMBEDDING", "RERANKER", "INDEX", "CROSSWALK") + ], + "legal_review_status": "PENDING_KOREAN_LAWYER_REVIEW", + "live_mcp_admission_status": "PENDING_LIVE_MCP_ADMISSION", + "production_eligible": False, + "unresolved_reason_codes": [ + "CORPUS_BUILD_NOT_EXECUTED", + "LEGAL_REVIEW_NOT_COMPLETED", + "LIVE_MCP_ADMISSION_NOT_COMPLETED", + "RAW_CORPUS_NOT_PROVIDED", + ], + "guards": { + "raw_corpus_runtime_ingest_forbidden": True, + "directory_scan_forbidden": True, + "filter_relaxation_forbidden": True, + "embedded_instruction_execution_forbidden": True, + "unapproved_chunk_use_forbidden": True, + }, + } + + snapshot_scope = {"source_rows": source_rows, "chunk_rows": chunk_rows} + snapshot_sha256 = _digest(snapshot_scope) + chunker_digest = _digest( + {"heading_pattern": HEADING.pattern, "fallback_section": "ROOT", "algorithm": "SHA256"} + ) + evidence_refs = sorted(row["source_path"] for row in source_rows) + return { + "schema_version": "stage2_corpus_build_receipt.v1", + "receipt_id": f"S2-CORPUS-BUILD-{snapshot_sha256[:24]}", + "build_status": "BUILT_NOT_LEGALLY_REVIEWED", + "build_executed": True, + "created_at": None, + "collection_contract_status": "PENDING_CORPUS_BUILD", + "collection_schema_ref": COLLECTION_SCHEMA_REF, + "collection_schema_sha256": collection_schema_sha256, + "collection_name": collection_name, + "tenant": tenant, + "snapshot_id": f"SNAP-{snapshot_sha256[:24]}", + "snapshot_sha256": snapshot_sha256, + "source_document_count": len(source_rows), + "chunk_count": len(chunk_rows), + "approved_case_type_ids": [], + "components": [ + { + "component_id": "CHUNKER", + "status": "BUILT_NOT_ADMITTED", + "configuration_digest": chunker_digest, + "evidence_refs": evidence_refs, + }, + *[ + { + "component_id": component_id, + "status": "PENDING", + "configuration_digest": None, + "evidence_refs": [], + } + for component_id in ("EMBEDDING", "RERANKER", "INDEX", "CROSSWALK") + ], + ], + "legal_review_status": "PENDING_KOREAN_LAWYER_REVIEW", + "live_mcp_admission_status": "PENDING_LIVE_MCP_ADMISSION", + "production_eligible": False, + "unresolved_reason_codes": [ + "LEGAL_REVIEW_NOT_COMPLETED", + "LIVE_MCP_ADMISSION_NOT_COMPLETED", + "EMBEDDING_NOT_BUILT", + "RERANKER_NOT_BUILT", + "INDEX_NOT_BUILT", + "CROSSWALK_NOT_BUILT", + ], + "guards": { + "raw_corpus_runtime_ingest_forbidden": True, + "directory_scan_forbidden": True, + "filter_relaxation_forbidden": True, + "embedded_instruction_execution_forbidden": True, + "unapproved_chunk_use_forbidden": True, + }, + } + + +def write_or_check(output: Path, expected: bytes, *, check: bool) -> int: + """Write expected bytes, or perform read-only exact-byte drift detection.""" + + expected_sha256 = hashlib.sha256(expected).hexdigest() + if check: + if not output.is_file(): + print( + json.dumps( + {"status": "DRIFT", "reason": "OUTPUT_MISSING", "output": str(output)}, + sort_keys=True, + ) + ) + return 1 + actual = output.read_bytes() + if actual != expected: + print( + json.dumps( + { + "status": "DRIFT", + "reason": "OUTPUT_BYTES_MISMATCH", + "actual_sha256": hashlib.sha256(actual).hexdigest(), + "expected_sha256": expected_sha256, + "output": str(output), + }, + sort_keys=True, + ) + ) + return 1 + print( + json.dumps( + {"status": "PASS", "output_sha256": expected_sha256}, sort_keys=True + ) + ) + return 0 + output.parent.mkdir(parents=True, exist_ok=True) + output.write_bytes(expected) + print( + json.dumps( + {"status": "BUILT", "output_sha256": expected_sha256}, sort_keys=True + ) + ) + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("raw_root", type=Path) + parser.add_argument("output", type=Path) + parser.add_argument("--deployment-root", type=Path, default=DEPLOYMENT_ROOT) + parser.add_argument("--collection-name") + parser.add_argument("--tenant") + parser.add_argument( + "--check", + action="store_true", + help="read-only exact-byte drift check; never creates or modifies output", + ) + args = parser.parse_args() + payload = build_receipt( + args.raw_root, + deployment_root=args.deployment_root, + collection_name=args.collection_name, + tenant=args.tenant, + ) + return write_or_check(args.output, _output_bytes(payload), check=args.check) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_release_manifests.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_release_manifests.py new file mode 100644 index 00000000..95e37a6b --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_release_manifests.py @@ -0,0 +1,493 @@ +#!/usr/bin/env python3 +"""Build the canonical Stage-2 module manifest and parent release. + +The parent release is deliberately a *raw closure*. Assets that embed the +parent release hash (Agent projections, inline-code mirrors/receipts, the +shared executor binding and detached admission material) are kept outside the +closure so that the release graph cannot become self-referential. +""" + +from __future__ import annotations + +import argparse +import copy +import hashlib +import json +import os +from pathlib import Path, PurePosixPath +from typing import Any, Iterable + + +MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1" +PARENT_RELEASE_SCHEMA = "stage2_release.v2" + +# Downstream-of-parent, detached, and self-referential assets. They may appear +# as an unhashed external trust-root pointer, but never in the parent closure. +FORBIDDEN_PARENT_MEMBERS = frozenset( + { + "manifest/module_manifest.json", + "manifest/stage2_release.json", + "deployment/stage2_code_executor_binding.yml", + "manifest/stage2_deterministic_admission_receipt.json", + "agent_scripts/Stage_2_S2_00.yml", + "runtime/s2_00_ingress.py", + "runtime/s2_00_ingress.txt", + "manifest/s2_00_inline_code_receipt.json", + "agent_scripts/Stage_2_S2_20.yml", + "runtime/s2_20_reduce.py", + "runtime/s2_20_reduce.txt", + "manifest/s2_20_inline_code_receipt.json", + "manifest/s2_10_release.json", + "manifest/s2_10_agent_receipt.json", + "manifest/s2_10_platform_adapter_receipt.json", + "manifest/s2_10_model_benchmark_receipt.json", + "manifest/s2_10_legal_review_receipt.json", + } +) + + +class ReleaseBuildError(ValueError): + """Raised when a release input violates the canonical closure contract.""" + + +def _reject_duplicate_pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise ReleaseBuildError(f"DUPLICATE_JSON_KEY:{key}") + result[key] = value + return result + + +def _load_json(path: Path) -> Any: + try: + return json.loads( + path.read_text(encoding="utf-8"), + object_pairs_hook=_reject_duplicate_pairs, + parse_constant=lambda token: (_ for _ in ()).throw( + ReleaseBuildError(f"NON_FINITE_JSON_NUMBER:{token}") + ), + ) + except UnicodeDecodeError as exc: + raise ReleaseBuildError(f"UTF8_REQUIRED:{path.as_posix()}") from exc + + +def _canonical_bytes(document: Any) -> bytes: + return ( + json.dumps( + document, + ensure_ascii=False, + sort_keys=True, + separators=(",", ":"), + allow_nan=False, + ) + + "\n" + ).encode("utf-8") + + +def _document_digest(document: dict[str, Any], digest_field: str) -> str: + payload = copy.deepcopy(document) + payload.pop(digest_field, None) + return hashlib.sha256(_canonical_bytes(payload)).hexdigest() + + +def _sha256(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest() + + +def _relative_path(value: str) -> str: + if not isinstance(value, str) or not value or "\\" in value: + raise ReleaseBuildError(f"RELATIVE_POSIX_PATH_REQUIRED:{value!r}") + pure = PurePosixPath(value) + if pure.is_absolute() or any(part in {"", ".", ".."} for part in pure.parts): + raise ReleaseBuildError(f"UNSAFE_RELATIVE_PATH:{value}") + normalized = pure.as_posix() + if any(segment in {"v.0", "v.1", "v.2", "v.3"} for segment in pure.parts): + raise ReleaseBuildError(f"LEGACY_STAGE2_DEPENDENCY:{normalized}") + return normalized + + +def _physical_file(root: Path, relative: str) -> Path: + relative = _relative_path(relative) + root_real = root.resolve(strict=True) + candidate = root / relative + if candidate.is_symlink(): + raise ReleaseBuildError(f"MODULE_SYMLINK_FORBIDDEN:{relative}") + try: + resolved = candidate.resolve(strict=True) + except FileNotFoundError as exc: + raise ReleaseBuildError(f"MODULE_PATH_UNAVAILABLE:{relative}") from exc + if root_real not in resolved.parents or not resolved.is_file(): + raise ReleaseBuildError(f"MODULE_PATH_UNAVAILABLE:{relative}") + return resolved + + +def _asset_bytes(root: Path, relative: str) -> bytes: + return _physical_file(root, relative).read_bytes() + + +def _mirror_for(root: Path, source_path: str) -> tuple[str, bytes] | None: + if not source_path.endswith(".py"): + return None + mirror_path = source_path[:-3] + ".txt" + candidate = root / mirror_path + if not candidate.exists(): + raise ReleaseBuildError(f"PYTHON_DOCUMENTATION_MIRROR_MISSING:{source_path}") + source_raw = _asset_bytes(root, source_path) + mirror_raw = _asset_bytes(root, mirror_path) + if source_raw != mirror_raw: + raise ReleaseBuildError(f"PYTHON_DOCUMENTATION_MIRROR_MISMATCH:{source_path}") + return mirror_path, mirror_raw + + +def _generic_kind(relative: str) -> str: + if relative.startswith("tests/"): + return "TEST" + if relative.startswith("schemas/"): + return "JSON_SCHEMA" + if relative.startswith("workflows/"): + return "WORKFLOW" + if relative.startswith("offline_build/") or relative.startswith("release_ops/"): + return "BUILD_ONLY" + if relative.startswith("runtime/"): + return "RUNTIME_CORE" + if relative.startswith("rules/"): + return "LEGAL_RULE_SOURCE" + if relative.startswith("manifest/"): + return "MANIFEST" + if relative.startswith("registry/"): + return "DECLARATIVE_REGISTRY" + return "S2_20_ASSET" + + +def _generic_module_row(relative: str) -> dict[str, Any]: + identity = hashlib.sha256(relative.encode("utf-8")).hexdigest()[:16].upper() + return { + "asset_version": "s2_20.1", + "authority_ids": [], + "consumed_schema_ids": [], + "dependency_module_ids": [], + "forbidden_contract_codes": ["LEGACY-STAGE2-V0-V3"], + "implementation_status": "DEV_HASH_BOUND_OFFLINE_ONLY", + "incompatible_module_ids": [], + "inputs": [], + "module_id": f"S2_20-ASSET-{identity}", + "module_kind": _generic_kind(relative), + "outputs": [], + "owner": "Stage_2_S2_20_owner", + "path": relative, + "produced_schema_ids": [], + "schema_version": "stage2_s2_20_generic_asset.v1", + "scope_predicate": "workflow_id == S2_20", + "semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION", + } + + +def _refresh_module_row(root: Path, row: dict[str, Any]) -> dict[str, Any]: + refreshed = copy.deepcopy(row) + relative = _relative_path(refreshed.get("path")) + if relative in FORBIDDEN_PARENT_MEMBERS: + raise ReleaseBuildError(f"NON_CYCLIC_PARENT_VIOLATION:{relative}") + raw = _asset_bytes(root, relative) + refreshed["path"] = relative + refreshed["sha256"] = _sha256(raw) + refreshed["size_bytes"] = len(raw) + mirror = _mirror_for(root, relative) + if mirror is not None: + mirror_path, mirror_raw = mirror + refreshed["mirror_path"] = mirror_path + refreshed["mirror_sha256"] = _sha256(mirror_raw) + refreshed["mirror_size_bytes"] = len(mirror_raw) + refreshed["mirror_byte_parity"] = True + return refreshed + + +def _documentation_mirror(row: dict[str, Any]) -> dict[str, Any] | None: + source = row.get("path") + mirror = row.get("mirror_path") + if not isinstance(source, str) or not isinstance(mirror, str): + return None + return { + "byte_identical": True, + "mirror_path": mirror, + "mirror_sha256": row["mirror_sha256"], + "mirror_size_bytes": row["mirror_size_bytes"], + "runtime_import_allowed": False, + "source_path": source, + "source_sha256": row["sha256"], + "source_size_bytes": row["size_bytes"], + } + + +def build_module_manifest( + root: Path, + template: dict[str, Any], + explicit_paths: Iterable[str], +) -> dict[str, Any]: + """Preserve canonical rows, refresh physical bindings and append S2_20 rows.""" + + if template.get("schema_version") != MODULE_MANIFEST_SCHEMA: + raise ReleaseBuildError("CANONICAL_MODULE_MANIFEST_V1_REQUIRED") + existing = template.get("modules") + if not isinstance(existing, list): + raise ReleaseBuildError("CANONICAL_MODULES_ARRAY_REQUIRED") + + normalized_explicit = [_relative_path(value) for value in explicit_paths] + if len(normalized_explicit) != len(set(normalized_explicit)): + raise ReleaseBuildError("PATH_LIST_MUST_BE_UNIQUE_ARRAY") + forbidden = sorted(set(normalized_explicit) & FORBIDDEN_PARENT_MEMBERS) + if forbidden: + raise ReleaseBuildError(f"NON_CYCLIC_PARENT_VIOLATION:{forbidden}") + + result = copy.deepcopy(template) + refreshed_rows: list[dict[str, Any]] = [] + seen_ids: set[str] = set() + seen_paths: set[str] = set() + for value in existing: + if not isinstance(value, dict): + raise ReleaseBuildError("MODULE_ROW_OBJECT_REQUIRED") + module_id = value.get("module_id") + relative = _relative_path(value.get("path")) + if not isinstance(module_id, str) or not module_id or module_id in seen_ids: + raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{module_id!r}") + if relative in seen_paths: + raise ReleaseBuildError(f"MODULE_PATH_DUPLICATE:{relative}") + seen_ids.add(module_id) + seen_paths.add(relative) + refreshed_rows.append(_refresh_module_row(root, value)) + + explicit_set = set(normalized_explicit) + for relative in sorted(normalized_explicit): + # A .txt copy is documentation for an explicitly bound .py source, not + # a second executable module row. + if relative.endswith(".txt"): + source = relative[:-4] + ".py" + if source in explicit_set or source in seen_paths: + _mirror_for(root, source) + continue + if relative in seen_paths: + continue + row = _refresh_module_row(root, _generic_module_row(relative)) + if row["module_id"] in seen_ids: + raise ReleaseBuildError(f"GENERATED_MODULE_ID_COLLISION:{row['module_id']}") + seen_ids.add(row["module_id"]) + seen_paths.add(relative) + refreshed_rows.append(row) + + result["modules"] = refreshed_rows + mirrors = [entry for row in refreshed_rows if (entry := _documentation_mirror(row))] + mirror_sources = [str(entry["source_path"]) for entry in mirrors] + if len(mirror_sources) != len(set(mirror_sources)): + raise ReleaseBuildError("DOCUMENTATION_MIRROR_SOURCE_DUPLICATE") + result["documentation_mirrors"] = sorted( + mirrors, key=lambda entry: str(entry["source_path"]) + ) + + summary = result.get("closure_summary") + if not isinstance(summary, dict): + raise ReleaseBuildError("CLOSURE_SUMMARY_REQUIRED") + summary.update( + { + "module_count": len(refreshed_rows), + "documentation_mirror_count": len(mirrors), + "test_module_count": sum( + 1 for row in refreshed_rows if row.get("module_kind") == "TEST" + ), + "build_tool_module_count": sum( + 1 for row in refreshed_rows if row.get("module_kind") == "BUILD_TOOL" + ), + "s2_20_generic_module_count": sum( + 1 + for row in refreshed_rows + if row.get("schema_version") == "stage2_s2_20_generic_asset.v1" + ), + "executable_agent_hash_included": False, + "executor_binding_hash_included": False, + "inline_runtime_code_hash_included": False, + "self_hash_included": False, + "stage2_release_hash_included": False, + } + ) + result["manifest_digest"] = _document_digest(result, "manifest_digest") + return result + + +def _refresh_parent_refs( + value: Any, + root: Path, + raw_overrides: dict[str, bytes], + location: str = "$", +) -> None: + if isinstance(value, list): + for index, item in enumerate(value): + _refresh_parent_refs(item, root, raw_overrides, f"{location}/{index}") + return + if not isinstance(value, dict): + return + + relative = value.get("path") + if isinstance(relative, str) and relative and "<" not in relative: + try: + relative = _relative_path(relative) + except ReleaseBuildError: + relative = None + if relative == "manifest/module_manifest.json": + raw = raw_overrides.get(relative) + if raw is None: + raise ReleaseBuildError("MODULE_MANIFEST_RAW_OVERRIDE_REQUIRED") + if "sha256" in value: + value["sha256"] = _sha256(raw) + if "size_bytes" in value: + value["size_bytes"] = len(raw) + elif relative in FORBIDDEN_PARENT_MEMBERS: + if "sha256" in value or value.get("binding_status") == "BOUND": + raise ReleaseBuildError( + f"NON_CYCLIC_PARENT_HASH_REFERENCE:{location}:{relative}" + ) + elif relative is not None and (relative in raw_overrides or (root / relative).is_file()): + raw = raw_overrides.get(relative) + if raw is None: + raw = _asset_bytes(root, relative) + if "sha256" in value: + value["sha256"] = _sha256(raw) + if "size_bytes" in value: + value["size_bytes"] = len(raw) + + for key, item in list(value.items()): + _refresh_parent_refs(item, root, raw_overrides, f"{location}/{key}") + + +def build_parent_release( + root: Path, + template: dict[str, Any], + module_manifest_raw: bytes, +) -> dict[str, Any]: + """Refresh safe parent refs and its self digest without changing admission.""" + + if template.get("schema_version") != PARENT_RELEASE_SCHEMA: + raise ReleaseBuildError("PARENT_STAGE2_RELEASE_V2_REQUIRED") + result = copy.deepcopy(template) + immutable_admission = { + key: copy.deepcopy(result.get(key)) + for key in ( + "release_class", + "release_status", + "authorization_status", + "signature", + "release_evidence", + ) + } + module_ref = result.get("module_manifest_ref") + if not isinstance(module_ref, dict) or module_ref.get("path") != "manifest/module_manifest.json": + raise ReleaseBuildError("PARENT_MODULE_MANIFEST_REF_REQUIRED") + module_ref["sha256"] = _sha256(module_manifest_raw) + if "size_bytes" in module_ref: + module_ref["size_bytes"] = len(module_manifest_raw) + + executor_ref = result.get("executor_binding_ref") + if ( + not isinstance(executor_ref, dict) + or executor_ref.get("path") != "deployment/stage2_code_executor_binding.yml" + ): + raise ReleaseBuildError("PARENT_EXECUTOR_TRUST_ROOT_REF_REQUIRED") + if "sha256" in executor_ref or executor_ref.get("binding_status") == "BOUND": + raise ReleaseBuildError("PARENT_EXECUTOR_TRUST_ROOT_MUST_BE_UNHASHED") + executor_ref["schema_id"] = "stage2_code_executor_binding.v3" + + _refresh_parent_refs( + result, + root, + {"manifest/module_manifest.json": module_manifest_raw}, + ) + bundle = result.get("bundle") + if isinstance(bundle, dict): + agent_ref = bundle.get("s2_10_agent_ref") + binding_ref = bundle.get("s2_10_llm_binding_ref") + if isinstance(agent_ref, dict) and isinstance(agent_ref.get("sha256"), str): + bundle["s2_10_agent_sha256"] = agent_ref["sha256"] + if isinstance(binding_ref, dict) and isinstance(binding_ref.get("sha256"), str): + bundle["s2_10_llm_binding_sha256"] = binding_ref["sha256"] + + for key, expected in immutable_admission.items(): + if result.get(key) != expected: + raise ReleaseBuildError(f"ADMISSION_STATE_MUTATION_FORBIDDEN:{key}") + result["release_digest"] = _document_digest(result, "release_digest") + return result + + +def build_release_package( + root: Path, + path_list: list[str], + module_template: dict[str, Any], + parent_template: dict[str, Any], +) -> tuple[dict[str, Any], dict[str, Any]]: + module_manifest = build_module_manifest(root, module_template, path_list) + module_raw = _canonical_bytes(module_manifest) + parent_release = build_parent_release(root, parent_template, module_raw) + return module_manifest, parent_release + + +def _write_bytes(path: Path, raw: bytes) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + temporary = path.with_name(f".{path.name}.tmp-{os.getpid()}") + temporary.write_bytes(raw) + os.replace(temporary, path) + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Reseal canonical Stage-2 module and parent release manifests" + ) + parser.add_argument("root", type=Path) + parser.add_argument("path_list", type=Path, help="JSON array of relative S2_20 paths") + parser.add_argument("output", type=Path, help="module_manifest.json output") + parser.add_argument( + "--module-template", + type=Path, + help="canonical module manifest template (default: ROOT/manifest/module_manifest.json)", + ) + parser.add_argument( + "--parent-template", + type=Path, + help="parent release template (default: ROOT/manifest/stage2_release.json)", + ) + parser.add_argument( + "--parent-output", + type=Path, + help="parent release output (default: OUTPUT sibling stage2_release.json)", + ) + args = parser.parse_args() + + path_list = _load_json(args.path_list) + if not isinstance(path_list, list) or not all(isinstance(row, str) for row in path_list): + raise ReleaseBuildError("PATH_LIST_MUST_BE_STRING_ARRAY") + module_template_path = args.module_template or args.root / "manifest/module_manifest.json" + parent_template_path = args.parent_template or args.root / "manifest/stage2_release.json" + parent_output = args.parent_output or args.output.with_name("stage2_release.json") + module_manifest, parent_release = build_release_package( + args.root, + path_list, + _load_json(module_template_path), + _load_json(parent_template_path), + ) + _write_bytes(args.output, _canonical_bytes(module_manifest)) + _write_bytes(parent_output, _canonical_bytes(parent_release)) + print( + json.dumps( + { + "module_count": len(module_manifest["modules"]), + "module_manifest_output": args.output.as_posix(), + "module_manifest_sha256": _sha256(_canonical_bytes(module_manifest)), + "parent_release_output": parent_output.as_posix(), + "parent_release_sha256": _sha256(_canonical_bytes(parent_release)), + "status": "PASS", + }, + sort_keys=True, + separators=(",", ":"), + ) + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_release_manifests.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_release_manifests.txt new file mode 100644 index 00000000..95e37a6b --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_release_manifests.txt @@ -0,0 +1,493 @@ +#!/usr/bin/env python3 +"""Build the canonical Stage-2 module manifest and parent release. + +The parent release is deliberately a *raw closure*. Assets that embed the +parent release hash (Agent projections, inline-code mirrors/receipts, the +shared executor binding and detached admission material) are kept outside the +closure so that the release graph cannot become self-referential. +""" + +from __future__ import annotations + +import argparse +import copy +import hashlib +import json +import os +from pathlib import Path, PurePosixPath +from typing import Any, Iterable + + +MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1" +PARENT_RELEASE_SCHEMA = "stage2_release.v2" + +# Downstream-of-parent, detached, and self-referential assets. They may appear +# as an unhashed external trust-root pointer, but never in the parent closure. +FORBIDDEN_PARENT_MEMBERS = frozenset( + { + "manifest/module_manifest.json", + "manifest/stage2_release.json", + "deployment/stage2_code_executor_binding.yml", + "manifest/stage2_deterministic_admission_receipt.json", + "agent_scripts/Stage_2_S2_00.yml", + "runtime/s2_00_ingress.py", + "runtime/s2_00_ingress.txt", + "manifest/s2_00_inline_code_receipt.json", + "agent_scripts/Stage_2_S2_20.yml", + "runtime/s2_20_reduce.py", + "runtime/s2_20_reduce.txt", + "manifest/s2_20_inline_code_receipt.json", + "manifest/s2_10_release.json", + "manifest/s2_10_agent_receipt.json", + "manifest/s2_10_platform_adapter_receipt.json", + "manifest/s2_10_model_benchmark_receipt.json", + "manifest/s2_10_legal_review_receipt.json", + } +) + + +class ReleaseBuildError(ValueError): + """Raised when a release input violates the canonical closure contract.""" + + +def _reject_duplicate_pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise ReleaseBuildError(f"DUPLICATE_JSON_KEY:{key}") + result[key] = value + return result + + +def _load_json(path: Path) -> Any: + try: + return json.loads( + path.read_text(encoding="utf-8"), + object_pairs_hook=_reject_duplicate_pairs, + parse_constant=lambda token: (_ for _ in ()).throw( + ReleaseBuildError(f"NON_FINITE_JSON_NUMBER:{token}") + ), + ) + except UnicodeDecodeError as exc: + raise ReleaseBuildError(f"UTF8_REQUIRED:{path.as_posix()}") from exc + + +def _canonical_bytes(document: Any) -> bytes: + return ( + json.dumps( + document, + ensure_ascii=False, + sort_keys=True, + separators=(",", ":"), + allow_nan=False, + ) + + "\n" + ).encode("utf-8") + + +def _document_digest(document: dict[str, Any], digest_field: str) -> str: + payload = copy.deepcopy(document) + payload.pop(digest_field, None) + return hashlib.sha256(_canonical_bytes(payload)).hexdigest() + + +def _sha256(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest() + + +def _relative_path(value: str) -> str: + if not isinstance(value, str) or not value or "\\" in value: + raise ReleaseBuildError(f"RELATIVE_POSIX_PATH_REQUIRED:{value!r}") + pure = PurePosixPath(value) + if pure.is_absolute() or any(part in {"", ".", ".."} for part in pure.parts): + raise ReleaseBuildError(f"UNSAFE_RELATIVE_PATH:{value}") + normalized = pure.as_posix() + if any(segment in {"v.0", "v.1", "v.2", "v.3"} for segment in pure.parts): + raise ReleaseBuildError(f"LEGACY_STAGE2_DEPENDENCY:{normalized}") + return normalized + + +def _physical_file(root: Path, relative: str) -> Path: + relative = _relative_path(relative) + root_real = root.resolve(strict=True) + candidate = root / relative + if candidate.is_symlink(): + raise ReleaseBuildError(f"MODULE_SYMLINK_FORBIDDEN:{relative}") + try: + resolved = candidate.resolve(strict=True) + except FileNotFoundError as exc: + raise ReleaseBuildError(f"MODULE_PATH_UNAVAILABLE:{relative}") from exc + if root_real not in resolved.parents or not resolved.is_file(): + raise ReleaseBuildError(f"MODULE_PATH_UNAVAILABLE:{relative}") + return resolved + + +def _asset_bytes(root: Path, relative: str) -> bytes: + return _physical_file(root, relative).read_bytes() + + +def _mirror_for(root: Path, source_path: str) -> tuple[str, bytes] | None: + if not source_path.endswith(".py"): + return None + mirror_path = source_path[:-3] + ".txt" + candidate = root / mirror_path + if not candidate.exists(): + raise ReleaseBuildError(f"PYTHON_DOCUMENTATION_MIRROR_MISSING:{source_path}") + source_raw = _asset_bytes(root, source_path) + mirror_raw = _asset_bytes(root, mirror_path) + if source_raw != mirror_raw: + raise ReleaseBuildError(f"PYTHON_DOCUMENTATION_MIRROR_MISMATCH:{source_path}") + return mirror_path, mirror_raw + + +def _generic_kind(relative: str) -> str: + if relative.startswith("tests/"): + return "TEST" + if relative.startswith("schemas/"): + return "JSON_SCHEMA" + if relative.startswith("workflows/"): + return "WORKFLOW" + if relative.startswith("offline_build/") or relative.startswith("release_ops/"): + return "BUILD_ONLY" + if relative.startswith("runtime/"): + return "RUNTIME_CORE" + if relative.startswith("rules/"): + return "LEGAL_RULE_SOURCE" + if relative.startswith("manifest/"): + return "MANIFEST" + if relative.startswith("registry/"): + return "DECLARATIVE_REGISTRY" + return "S2_20_ASSET" + + +def _generic_module_row(relative: str) -> dict[str, Any]: + identity = hashlib.sha256(relative.encode("utf-8")).hexdigest()[:16].upper() + return { + "asset_version": "s2_20.1", + "authority_ids": [], + "consumed_schema_ids": [], + "dependency_module_ids": [], + "forbidden_contract_codes": ["LEGACY-STAGE2-V0-V3"], + "implementation_status": "DEV_HASH_BOUND_OFFLINE_ONLY", + "incompatible_module_ids": [], + "inputs": [], + "module_id": f"S2_20-ASSET-{identity}", + "module_kind": _generic_kind(relative), + "outputs": [], + "owner": "Stage_2_S2_20_owner", + "path": relative, + "produced_schema_ids": [], + "schema_version": "stage2_s2_20_generic_asset.v1", + "scope_predicate": "workflow_id == S2_20", + "semantic_review_status": "PENDING_LEGAL_AND_LIVE_ADMISSION", + } + + +def _refresh_module_row(root: Path, row: dict[str, Any]) -> dict[str, Any]: + refreshed = copy.deepcopy(row) + relative = _relative_path(refreshed.get("path")) + if relative in FORBIDDEN_PARENT_MEMBERS: + raise ReleaseBuildError(f"NON_CYCLIC_PARENT_VIOLATION:{relative}") + raw = _asset_bytes(root, relative) + refreshed["path"] = relative + refreshed["sha256"] = _sha256(raw) + refreshed["size_bytes"] = len(raw) + mirror = _mirror_for(root, relative) + if mirror is not None: + mirror_path, mirror_raw = mirror + refreshed["mirror_path"] = mirror_path + refreshed["mirror_sha256"] = _sha256(mirror_raw) + refreshed["mirror_size_bytes"] = len(mirror_raw) + refreshed["mirror_byte_parity"] = True + return refreshed + + +def _documentation_mirror(row: dict[str, Any]) -> dict[str, Any] | None: + source = row.get("path") + mirror = row.get("mirror_path") + if not isinstance(source, str) or not isinstance(mirror, str): + return None + return { + "byte_identical": True, + "mirror_path": mirror, + "mirror_sha256": row["mirror_sha256"], + "mirror_size_bytes": row["mirror_size_bytes"], + "runtime_import_allowed": False, + "source_path": source, + "source_sha256": row["sha256"], + "source_size_bytes": row["size_bytes"], + } + + +def build_module_manifest( + root: Path, + template: dict[str, Any], + explicit_paths: Iterable[str], +) -> dict[str, Any]: + """Preserve canonical rows, refresh physical bindings and append S2_20 rows.""" + + if template.get("schema_version") != MODULE_MANIFEST_SCHEMA: + raise ReleaseBuildError("CANONICAL_MODULE_MANIFEST_V1_REQUIRED") + existing = template.get("modules") + if not isinstance(existing, list): + raise ReleaseBuildError("CANONICAL_MODULES_ARRAY_REQUIRED") + + normalized_explicit = [_relative_path(value) for value in explicit_paths] + if len(normalized_explicit) != len(set(normalized_explicit)): + raise ReleaseBuildError("PATH_LIST_MUST_BE_UNIQUE_ARRAY") + forbidden = sorted(set(normalized_explicit) & FORBIDDEN_PARENT_MEMBERS) + if forbidden: + raise ReleaseBuildError(f"NON_CYCLIC_PARENT_VIOLATION:{forbidden}") + + result = copy.deepcopy(template) + refreshed_rows: list[dict[str, Any]] = [] + seen_ids: set[str] = set() + seen_paths: set[str] = set() + for value in existing: + if not isinstance(value, dict): + raise ReleaseBuildError("MODULE_ROW_OBJECT_REQUIRED") + module_id = value.get("module_id") + relative = _relative_path(value.get("path")) + if not isinstance(module_id, str) or not module_id or module_id in seen_ids: + raise ReleaseBuildError(f"MODULE_ID_INVALID_OR_DUPLICATE:{module_id!r}") + if relative in seen_paths: + raise ReleaseBuildError(f"MODULE_PATH_DUPLICATE:{relative}") + seen_ids.add(module_id) + seen_paths.add(relative) + refreshed_rows.append(_refresh_module_row(root, value)) + + explicit_set = set(normalized_explicit) + for relative in sorted(normalized_explicit): + # A .txt copy is documentation for an explicitly bound .py source, not + # a second executable module row. + if relative.endswith(".txt"): + source = relative[:-4] + ".py" + if source in explicit_set or source in seen_paths: + _mirror_for(root, source) + continue + if relative in seen_paths: + continue + row = _refresh_module_row(root, _generic_module_row(relative)) + if row["module_id"] in seen_ids: + raise ReleaseBuildError(f"GENERATED_MODULE_ID_COLLISION:{row['module_id']}") + seen_ids.add(row["module_id"]) + seen_paths.add(relative) + refreshed_rows.append(row) + + result["modules"] = refreshed_rows + mirrors = [entry for row in refreshed_rows if (entry := _documentation_mirror(row))] + mirror_sources = [str(entry["source_path"]) for entry in mirrors] + if len(mirror_sources) != len(set(mirror_sources)): + raise ReleaseBuildError("DOCUMENTATION_MIRROR_SOURCE_DUPLICATE") + result["documentation_mirrors"] = sorted( + mirrors, key=lambda entry: str(entry["source_path"]) + ) + + summary = result.get("closure_summary") + if not isinstance(summary, dict): + raise ReleaseBuildError("CLOSURE_SUMMARY_REQUIRED") + summary.update( + { + "module_count": len(refreshed_rows), + "documentation_mirror_count": len(mirrors), + "test_module_count": sum( + 1 for row in refreshed_rows if row.get("module_kind") == "TEST" + ), + "build_tool_module_count": sum( + 1 for row in refreshed_rows if row.get("module_kind") == "BUILD_TOOL" + ), + "s2_20_generic_module_count": sum( + 1 + for row in refreshed_rows + if row.get("schema_version") == "stage2_s2_20_generic_asset.v1" + ), + "executable_agent_hash_included": False, + "executor_binding_hash_included": False, + "inline_runtime_code_hash_included": False, + "self_hash_included": False, + "stage2_release_hash_included": False, + } + ) + result["manifest_digest"] = _document_digest(result, "manifest_digest") + return result + + +def _refresh_parent_refs( + value: Any, + root: Path, + raw_overrides: dict[str, bytes], + location: str = "$", +) -> None: + if isinstance(value, list): + for index, item in enumerate(value): + _refresh_parent_refs(item, root, raw_overrides, f"{location}/{index}") + return + if not isinstance(value, dict): + return + + relative = value.get("path") + if isinstance(relative, str) and relative and "<" not in relative: + try: + relative = _relative_path(relative) + except ReleaseBuildError: + relative = None + if relative == "manifest/module_manifest.json": + raw = raw_overrides.get(relative) + if raw is None: + raise ReleaseBuildError("MODULE_MANIFEST_RAW_OVERRIDE_REQUIRED") + if "sha256" in value: + value["sha256"] = _sha256(raw) + if "size_bytes" in value: + value["size_bytes"] = len(raw) + elif relative in FORBIDDEN_PARENT_MEMBERS: + if "sha256" in value or value.get("binding_status") == "BOUND": + raise ReleaseBuildError( + f"NON_CYCLIC_PARENT_HASH_REFERENCE:{location}:{relative}" + ) + elif relative is not None and (relative in raw_overrides or (root / relative).is_file()): + raw = raw_overrides.get(relative) + if raw is None: + raw = _asset_bytes(root, relative) + if "sha256" in value: + value["sha256"] = _sha256(raw) + if "size_bytes" in value: + value["size_bytes"] = len(raw) + + for key, item in list(value.items()): + _refresh_parent_refs(item, root, raw_overrides, f"{location}/{key}") + + +def build_parent_release( + root: Path, + template: dict[str, Any], + module_manifest_raw: bytes, +) -> dict[str, Any]: + """Refresh safe parent refs and its self digest without changing admission.""" + + if template.get("schema_version") != PARENT_RELEASE_SCHEMA: + raise ReleaseBuildError("PARENT_STAGE2_RELEASE_V2_REQUIRED") + result = copy.deepcopy(template) + immutable_admission = { + key: copy.deepcopy(result.get(key)) + for key in ( + "release_class", + "release_status", + "authorization_status", + "signature", + "release_evidence", + ) + } + module_ref = result.get("module_manifest_ref") + if not isinstance(module_ref, dict) or module_ref.get("path") != "manifest/module_manifest.json": + raise ReleaseBuildError("PARENT_MODULE_MANIFEST_REF_REQUIRED") + module_ref["sha256"] = _sha256(module_manifest_raw) + if "size_bytes" in module_ref: + module_ref["size_bytes"] = len(module_manifest_raw) + + executor_ref = result.get("executor_binding_ref") + if ( + not isinstance(executor_ref, dict) + or executor_ref.get("path") != "deployment/stage2_code_executor_binding.yml" + ): + raise ReleaseBuildError("PARENT_EXECUTOR_TRUST_ROOT_REF_REQUIRED") + if "sha256" in executor_ref or executor_ref.get("binding_status") == "BOUND": + raise ReleaseBuildError("PARENT_EXECUTOR_TRUST_ROOT_MUST_BE_UNHASHED") + executor_ref["schema_id"] = "stage2_code_executor_binding.v3" + + _refresh_parent_refs( + result, + root, + {"manifest/module_manifest.json": module_manifest_raw}, + ) + bundle = result.get("bundle") + if isinstance(bundle, dict): + agent_ref = bundle.get("s2_10_agent_ref") + binding_ref = bundle.get("s2_10_llm_binding_ref") + if isinstance(agent_ref, dict) and isinstance(agent_ref.get("sha256"), str): + bundle["s2_10_agent_sha256"] = agent_ref["sha256"] + if isinstance(binding_ref, dict) and isinstance(binding_ref.get("sha256"), str): + bundle["s2_10_llm_binding_sha256"] = binding_ref["sha256"] + + for key, expected in immutable_admission.items(): + if result.get(key) != expected: + raise ReleaseBuildError(f"ADMISSION_STATE_MUTATION_FORBIDDEN:{key}") + result["release_digest"] = _document_digest(result, "release_digest") + return result + + +def build_release_package( + root: Path, + path_list: list[str], + module_template: dict[str, Any], + parent_template: dict[str, Any], +) -> tuple[dict[str, Any], dict[str, Any]]: + module_manifest = build_module_manifest(root, module_template, path_list) + module_raw = _canonical_bytes(module_manifest) + parent_release = build_parent_release(root, parent_template, module_raw) + return module_manifest, parent_release + + +def _write_bytes(path: Path, raw: bytes) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + temporary = path.with_name(f".{path.name}.tmp-{os.getpid()}") + temporary.write_bytes(raw) + os.replace(temporary, path) + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Reseal canonical Stage-2 module and parent release manifests" + ) + parser.add_argument("root", type=Path) + parser.add_argument("path_list", type=Path, help="JSON array of relative S2_20 paths") + parser.add_argument("output", type=Path, help="module_manifest.json output") + parser.add_argument( + "--module-template", + type=Path, + help="canonical module manifest template (default: ROOT/manifest/module_manifest.json)", + ) + parser.add_argument( + "--parent-template", + type=Path, + help="parent release template (default: ROOT/manifest/stage2_release.json)", + ) + parser.add_argument( + "--parent-output", + type=Path, + help="parent release output (default: OUTPUT sibling stage2_release.json)", + ) + args = parser.parse_args() + + path_list = _load_json(args.path_list) + if not isinstance(path_list, list) or not all(isinstance(row, str) for row in path_list): + raise ReleaseBuildError("PATH_LIST_MUST_BE_STRING_ARRAY") + module_template_path = args.module_template or args.root / "manifest/module_manifest.json" + parent_template_path = args.parent_template or args.root / "manifest/stage2_release.json" + parent_output = args.parent_output or args.output.with_name("stage2_release.json") + module_manifest, parent_release = build_release_package( + args.root, + path_list, + _load_json(module_template_path), + _load_json(parent_template_path), + ) + _write_bytes(args.output, _canonical_bytes(module_manifest)) + _write_bytes(parent_output, _canonical_bytes(parent_release)) + print( + json.dumps( + { + "module_count": len(module_manifest["modules"]), + "module_manifest_output": args.output.as_posix(), + "module_manifest_sha256": _sha256(_canonical_bytes(module_manifest)), + "parent_release_output": parent_output.as_posix(), + "parent_release_sha256": _sha256(_canonical_bytes(parent_release)), + "status": "PASS", + }, + sort_keys=True, + separators=(",", ":"), + ) + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_00_inline_projection.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_00_inline_projection.py index f0243908..b08dafb3 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_00_inline_projection.py +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_00_inline_projection.py @@ -33,6 +33,7 @@ PROJECTION_PATH = DEPLOYMENT_ROOT / "agent_scripts" / "Stage_2_S2_00.yml" MIRROR_PY_PATH = DEPLOYMENT_ROOT / "runtime" / "s2_00_ingress.py" MIRROR_TXT_PATH = DEPLOYMENT_ROOT / "runtime" / "s2_00_ingress.txt" RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_00_inline_code_receipt.json" +BINDING_PATH = DEPLOYMENT_ROOT / "deployment" / "stage2_code_executor_binding.yml" EXPECTED_TASK_NAME = "Task_S2_00_deterministic_ingress" EXPECTED_AGENT_NAME = "Stage_2_S2_00_v2" @@ -207,6 +208,66 @@ def load_authoring(path: Path | None = None) -> tuple[bytes, dict[str, Any]]: return raw, parse_authoring_bytes(raw, source=path.as_posix()) +def select_s2_00_binding(document: Mapping[str, Any]) -> dict[str, Any]: + """Select the unique S2_00 row from the shared deterministic-stage binding. + + The v3 wrapper replaces the former singleton v2 document. This selector + deliberately checks only migration/identity semantics; byte hashes are + resealed after projection generation by the release pipeline. + """ + + if document.get("schema_version") != "stage2_code_executor_binding.v3": + raise ProjectionError( + "EXECUTOR_BINDING_SCHEMA_MISMATCH", + repr(document.get("schema_version")), + ) + rows = _require_list(document.get("stage_bindings"), "STAGE_BINDINGS_ARRAY_REQUIRED") + stage_ids = [row.get("stage_id") for row in rows if isinstance(row, dict)] + if len(stage_ids) != len(rows) or len(set(stage_ids)) != len(stage_ids): + raise ProjectionError("STAGE_BINDING_ID_NOT_UNIQUE", repr(stage_ids)) + matches = [row for row in rows if row.get("stage_id") == "S2_00"] + if len(matches) != 1: + raise ProjectionError("S2_00_BINDING_EXACT_ONE_REQUIRED", str(len(matches))) + row = _require_mapping(matches[0], "S2_00_BINDING_OBJECT_REQUIRED") + expected = { + "workflow_id": "S2_00", + "execution_class": "NON-LLM-DETERMINISTIC", + "mcp_server_id": "code-executor", + "tool_name": "run_code", + "language": "python", + "network": "agent-network", + } + drift = { + key: {"expected": value, "observed": row.get(key)} + for key, value in expected.items() + if row.get(key) != value + } + if row.get("active_runtime_authority") is not True: + drift["active_runtime_authority"] = { + "expected": True, + "observed": row.get("active_runtime_authority"), + } + if row.get("external_mcp_contract") is not None: + drift["external_mcp_contract"] = { + "expected": None, + "observed": row.get("external_mcp_contract"), + } + if drift: + raise ProjectionError( + "S2_00_BINDING_SEMANTICS_DRIFT", + json.dumps(drift, ensure_ascii=False, sort_keys=True), + ) + return row + + +def load_s2_00_binding(path: Path | None = None) -> dict[str, Any]: + path = BINDING_PATH if path is None else path + if not path.is_file() or path.is_symlink(): + raise ProjectionError("EXECUTOR_BINDING_UNAVAILABLE", path.as_posix()) + wrapper = parse_authoring_bytes(path.read_bytes(), source=path.as_posix()) + return select_s2_00_binding(wrapper) + + def _require_mapping(value: Any, code: str) -> dict[str, Any]: if not isinstance(value, dict): raise ProjectionError(code, repr(value)[:200]) @@ -738,6 +799,7 @@ def compare_outputs(outputs: Mapping[Path, bytes]) -> list[dict[str, Any]]: def run(*, check: bool) -> tuple[int, dict[str, Any]]: + load_s2_00_binding() before, document = load_authoring() outputs, receipt = expected_outputs(before, document) if check: diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_00_inline_projection.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_00_inline_projection.txt index f0243908..b08dafb3 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_00_inline_projection.txt +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_00_inline_projection.txt @@ -33,6 +33,7 @@ PROJECTION_PATH = DEPLOYMENT_ROOT / "agent_scripts" / "Stage_2_S2_00.yml" MIRROR_PY_PATH = DEPLOYMENT_ROOT / "runtime" / "s2_00_ingress.py" MIRROR_TXT_PATH = DEPLOYMENT_ROOT / "runtime" / "s2_00_ingress.txt" RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest" / "s2_00_inline_code_receipt.json" +BINDING_PATH = DEPLOYMENT_ROOT / "deployment" / "stage2_code_executor_binding.yml" EXPECTED_TASK_NAME = "Task_S2_00_deterministic_ingress" EXPECTED_AGENT_NAME = "Stage_2_S2_00_v2" @@ -207,6 +208,66 @@ def load_authoring(path: Path | None = None) -> tuple[bytes, dict[str, Any]]: return raw, parse_authoring_bytes(raw, source=path.as_posix()) +def select_s2_00_binding(document: Mapping[str, Any]) -> dict[str, Any]: + """Select the unique S2_00 row from the shared deterministic-stage binding. + + The v3 wrapper replaces the former singleton v2 document. This selector + deliberately checks only migration/identity semantics; byte hashes are + resealed after projection generation by the release pipeline. + """ + + if document.get("schema_version") != "stage2_code_executor_binding.v3": + raise ProjectionError( + "EXECUTOR_BINDING_SCHEMA_MISMATCH", + repr(document.get("schema_version")), + ) + rows = _require_list(document.get("stage_bindings"), "STAGE_BINDINGS_ARRAY_REQUIRED") + stage_ids = [row.get("stage_id") for row in rows if isinstance(row, dict)] + if len(stage_ids) != len(rows) or len(set(stage_ids)) != len(stage_ids): + raise ProjectionError("STAGE_BINDING_ID_NOT_UNIQUE", repr(stage_ids)) + matches = [row for row in rows if row.get("stage_id") == "S2_00"] + if len(matches) != 1: + raise ProjectionError("S2_00_BINDING_EXACT_ONE_REQUIRED", str(len(matches))) + row = _require_mapping(matches[0], "S2_00_BINDING_OBJECT_REQUIRED") + expected = { + "workflow_id": "S2_00", + "execution_class": "NON-LLM-DETERMINISTIC", + "mcp_server_id": "code-executor", + "tool_name": "run_code", + "language": "python", + "network": "agent-network", + } + drift = { + key: {"expected": value, "observed": row.get(key)} + for key, value in expected.items() + if row.get(key) != value + } + if row.get("active_runtime_authority") is not True: + drift["active_runtime_authority"] = { + "expected": True, + "observed": row.get("active_runtime_authority"), + } + if row.get("external_mcp_contract") is not None: + drift["external_mcp_contract"] = { + "expected": None, + "observed": row.get("external_mcp_contract"), + } + if drift: + raise ProjectionError( + "S2_00_BINDING_SEMANTICS_DRIFT", + json.dumps(drift, ensure_ascii=False, sort_keys=True), + ) + return row + + +def load_s2_00_binding(path: Path | None = None) -> dict[str, Any]: + path = BINDING_PATH if path is None else path + if not path.is_file() or path.is_symlink(): + raise ProjectionError("EXECUTOR_BINDING_UNAVAILABLE", path.as_posix()) + wrapper = parse_authoring_bytes(path.read_bytes(), source=path.as_posix()) + return select_s2_00_binding(wrapper) + + def _require_mapping(value: Any, code: str) -> dict[str, Any]: if not isinstance(value, dict): raise ProjectionError(code, repr(value)[:200]) @@ -738,6 +799,7 @@ def compare_outputs(outputs: Mapping[Path, bytes]) -> list[dict[str, Any]]: def run(*, check: bool) -> tuple[int, dict[str, Any]]: + load_s2_00_binding() before, document = load_authoring() outputs, receipt = expected_outputs(before, document) if check: diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_10_agent_projection.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_10_agent_projection.py index 11ccb32f..6e4b36c5 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_10_agent_projection.py +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_10_agent_projection.py @@ -1265,16 +1265,144 @@ def check() -> dict[str, Any]: } +def child_only_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]: + """Reseal only the downstream S2_10 child chain against a frozen parent. + + The canonical parent/module builder owns the raw parent closure. This mode + therefore never rewrites ``module_manifest.json``, ``stage2_release.json``, + the Agent projection, prompt receipt, or LLM binding. + """ + + authoring_raw = _require_file(AUTHORING_PATH) + if _require_file(PROJECTION_PATH) != authoring_raw: + raise BuildError("S2_10_AGENT_PROJECTION_DRIFT", _logical_path(PROJECTION_PATH)) + contract = extract_agent_contract(_load_yaml(authoring_raw, _logical_path(AUTHORING_PATH))) + binding_raw = _require_file(BINDING_PATH) + binding = _load_yaml(binding_raw, _logical_path(BINDING_PATH)) + inline_receipt_raw = _require_file(INLINE_RECEIPT_PATH) + parent_raw = _require_file(PARENT_RELEASE_PATH) + child = build_child_release(parent_raw, binding) + child_raw = canonical_json_bytes(child) + outputs = { + CHILD_RELEASE_PATH: child_raw, + AGENT_RECEIPT_PATH: canonical_json_bytes( + build_agent_receipt(authoring_raw, binding_raw, child_raw, contract) + ), + PLATFORM_RECEIPT_PATH: canonical_json_bytes( + build_platform_receipt( + parent_raw=parent_raw, binding_raw=binding_raw, child_raw=child_raw + ) + ), + MODEL_RECEIPT_PATH: canonical_json_bytes( + build_model_receipt(binding_raw=binding_raw, child_raw=child_raw) + ), + LEGAL_RECEIPT_PATH: canonical_json_bytes( + build_legal_receipt( + binding_raw=binding_raw, + child_raw=child_raw, + inline_receipt_raw=inline_receipt_raw, + ) + ), + } + return outputs, { + "child_release_sha256": sha256_bytes(child_raw), + "parent_release_sha256": sha256_bytes(parent_raw), + } + + +def prerequisite_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]: + """Build only the parent-owned S2_10 projection prerequisites.""" + + authoring_raw = _require_file(AUTHORING_PATH) + contract = extract_agent_contract(_load_yaml(authoring_raw, _logical_path(AUTHORING_PATH))) + inline_receipt = build_inline_receipt(authoring_raw, contract) + inline_receipt_raw = canonical_json_bytes(inline_receipt) + binding, producer_material = build_binding(authoring_raw, contract, inline_receipt_raw) + return { + PROJECTION_PATH: authoring_raw, + INLINE_RECEIPT_PATH: inline_receipt_raw, + BINDING_PATH: _yaml_bytes(binding), + }, { + "producer_contract_material": producer_material, + "producer_contract_digest": binding["producer_contract"]["s2_10_producer_contract_digest"], + } + + +def prerequisites_only(*, verify: bool) -> dict[str, Any]: + outputs, report = prerequisite_outputs() + drift: list[dict[str, Any]] = [] + for path, expected in outputs.items(): + observed = path.read_bytes() if path.is_file() and not path.is_symlink() else None + if observed != expected: + drift.append( + { + "path": _logical_path(path), + "expected_sha256": sha256_bytes(expected), + "observed_sha256": sha256_bytes(observed) if observed is not None else None, + } + ) + if not verify: + _atomic_write(path, expected) + if verify and drift: + raise BuildError("S2_10_PREREQUISITE_DRIFT", json.dumps(drift, ensure_ascii=False)) + return { + "status": "S2_10_PREREQUISITE_CHECK_PASS" if verify else "S2_10_PREREQUISITES_REBUILT", + "paths": [_logical_path(path) for path in outputs], + **report, + } + + +def child_only(*, verify: bool) -> dict[str, Any]: + outputs, report = child_only_outputs() + drift: list[dict[str, Any]] = [] + for path, expected in outputs.items(): + observed = path.read_bytes() if path.is_file() and not path.is_symlink() else None + if observed != expected: + drift.append( + { + "path": _logical_path(path), + "expected_sha256": sha256_bytes(expected), + "observed_sha256": sha256_bytes(observed) if observed is not None else None, + } + ) + if not verify: + _atomic_write(path, expected) + if verify and drift: + raise BuildError("S2_10_CHILD_CHAIN_DRIFT", json.dumps(drift, ensure_ascii=False)) + return { + "status": "S2_10_CHILD_CHAIN_CHECK_PASS" if verify else "S2_10_CHILD_CHAIN_RESEALED", + "paths": [_logical_path(path) for path in outputs], + **report, + } + + def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--check", action="store_true", help="verify without writing") + parser.add_argument( + "--child-only", + action="store_true", + help="reseal/check only child release and dependent receipts against the frozen parent", + ) + parser.add_argument( + "--prerequisites-only", + action="store_true", + help="rebuild/check only Agent projection, inline prompt receipt, and LLM binding", + ) return parser.parse_args(argv) def main(argv: Sequence[str] | None = None) -> int: args = parse_args(argv) try: - result = check() if args.check else build() + if args.child_only and args.prerequisites_only: + raise BuildError("BUILD_MODE_CONFLICT", "choose only one bounded reseal mode") + if args.child_only: + result = child_only(verify=args.check) + elif args.prerequisites_only: + result = prerequisites_only(verify=args.check) + else: + result = check() if args.check else build() except BuildError as exc: print(json.dumps({"status": "FAIL", "code": exc.code, "detail": exc.detail}, ensure_ascii=False)) return 2 diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_10_agent_projection.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_10_agent_projection.txt index 11ccb32f..6e4b36c5 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_10_agent_projection.txt +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_10_agent_projection.txt @@ -1265,16 +1265,144 @@ def check() -> dict[str, Any]: } +def child_only_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]: + """Reseal only the downstream S2_10 child chain against a frozen parent. + + The canonical parent/module builder owns the raw parent closure. This mode + therefore never rewrites ``module_manifest.json``, ``stage2_release.json``, + the Agent projection, prompt receipt, or LLM binding. + """ + + authoring_raw = _require_file(AUTHORING_PATH) + if _require_file(PROJECTION_PATH) != authoring_raw: + raise BuildError("S2_10_AGENT_PROJECTION_DRIFT", _logical_path(PROJECTION_PATH)) + contract = extract_agent_contract(_load_yaml(authoring_raw, _logical_path(AUTHORING_PATH))) + binding_raw = _require_file(BINDING_PATH) + binding = _load_yaml(binding_raw, _logical_path(BINDING_PATH)) + inline_receipt_raw = _require_file(INLINE_RECEIPT_PATH) + parent_raw = _require_file(PARENT_RELEASE_PATH) + child = build_child_release(parent_raw, binding) + child_raw = canonical_json_bytes(child) + outputs = { + CHILD_RELEASE_PATH: child_raw, + AGENT_RECEIPT_PATH: canonical_json_bytes( + build_agent_receipt(authoring_raw, binding_raw, child_raw, contract) + ), + PLATFORM_RECEIPT_PATH: canonical_json_bytes( + build_platform_receipt( + parent_raw=parent_raw, binding_raw=binding_raw, child_raw=child_raw + ) + ), + MODEL_RECEIPT_PATH: canonical_json_bytes( + build_model_receipt(binding_raw=binding_raw, child_raw=child_raw) + ), + LEGAL_RECEIPT_PATH: canonical_json_bytes( + build_legal_receipt( + binding_raw=binding_raw, + child_raw=child_raw, + inline_receipt_raw=inline_receipt_raw, + ) + ), + } + return outputs, { + "child_release_sha256": sha256_bytes(child_raw), + "parent_release_sha256": sha256_bytes(parent_raw), + } + + +def prerequisite_outputs() -> tuple[dict[Path, bytes], dict[str, Any]]: + """Build only the parent-owned S2_10 projection prerequisites.""" + + authoring_raw = _require_file(AUTHORING_PATH) + contract = extract_agent_contract(_load_yaml(authoring_raw, _logical_path(AUTHORING_PATH))) + inline_receipt = build_inline_receipt(authoring_raw, contract) + inline_receipt_raw = canonical_json_bytes(inline_receipt) + binding, producer_material = build_binding(authoring_raw, contract, inline_receipt_raw) + return { + PROJECTION_PATH: authoring_raw, + INLINE_RECEIPT_PATH: inline_receipt_raw, + BINDING_PATH: _yaml_bytes(binding), + }, { + "producer_contract_material": producer_material, + "producer_contract_digest": binding["producer_contract"]["s2_10_producer_contract_digest"], + } + + +def prerequisites_only(*, verify: bool) -> dict[str, Any]: + outputs, report = prerequisite_outputs() + drift: list[dict[str, Any]] = [] + for path, expected in outputs.items(): + observed = path.read_bytes() if path.is_file() and not path.is_symlink() else None + if observed != expected: + drift.append( + { + "path": _logical_path(path), + "expected_sha256": sha256_bytes(expected), + "observed_sha256": sha256_bytes(observed) if observed is not None else None, + } + ) + if not verify: + _atomic_write(path, expected) + if verify and drift: + raise BuildError("S2_10_PREREQUISITE_DRIFT", json.dumps(drift, ensure_ascii=False)) + return { + "status": "S2_10_PREREQUISITE_CHECK_PASS" if verify else "S2_10_PREREQUISITES_REBUILT", + "paths": [_logical_path(path) for path in outputs], + **report, + } + + +def child_only(*, verify: bool) -> dict[str, Any]: + outputs, report = child_only_outputs() + drift: list[dict[str, Any]] = [] + for path, expected in outputs.items(): + observed = path.read_bytes() if path.is_file() and not path.is_symlink() else None + if observed != expected: + drift.append( + { + "path": _logical_path(path), + "expected_sha256": sha256_bytes(expected), + "observed_sha256": sha256_bytes(observed) if observed is not None else None, + } + ) + if not verify: + _atomic_write(path, expected) + if verify and drift: + raise BuildError("S2_10_CHILD_CHAIN_DRIFT", json.dumps(drift, ensure_ascii=False)) + return { + "status": "S2_10_CHILD_CHAIN_CHECK_PASS" if verify else "S2_10_CHILD_CHAIN_RESEALED", + "paths": [_logical_path(path) for path in outputs], + **report, + } + + def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--check", action="store_true", help="verify without writing") + parser.add_argument( + "--child-only", + action="store_true", + help="reseal/check only child release and dependent receipts against the frozen parent", + ) + parser.add_argument( + "--prerequisites-only", + action="store_true", + help="rebuild/check only Agent projection, inline prompt receipt, and LLM binding", + ) return parser.parse_args(argv) def main(argv: Sequence[str] | None = None) -> int: args = parse_args(argv) try: - result = check() if args.check else build() + if args.child_only and args.prerequisites_only: + raise BuildError("BUILD_MODE_CONFLICT", "choose only one bounded reseal mode") + if args.child_only: + result = child_only(verify=args.check) + elif args.prerequisites_only: + result = prerequisites_only(verify=args.check) + else: + result = check() if args.check else build() except BuildError as exc: print(json.dumps({"status": "FAIL", "code": exc.code, "detail": exc.detail}, ensure_ascii=False)) return 2 diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_20_inline_projection.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_20_inline_projection.py new file mode 100644 index 00000000..e45a0931 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_20_inline_projection.py @@ -0,0 +1,665 @@ +#!/usr/bin/env python3 +"""Build/check the S2_20 single-task inline Python projection. + +The authoring YAML is the sole editable source. This offline tool rejects +duplicate YAML keys, validates the exact AgentBackend task graph and MCP +``run_code`` contract, compiles and audits parser-returned inline Python, then +projects authoring/code bytes without transformation. It never executes a +matter or imports Stage 2 runtime modules. +""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +import os +from pathlib import Path +import re +import sys +import tempfile +from typing import Any, Iterable, Mapping + +try: + import yaml +except ImportError: # pragma: no cover - incomplete offline build host + yaml = None # type: ignore[assignment] + + +DEPLOYMENT_ROOT = Path(__file__).resolve().parents[1] +MAIN_WORKING_DIRECTORY = DEPLOYMENT_ROOT.parent.parent +AUTHORING_PATH = MAIN_WORKING_DIRECTORY / "Stage_2_S2_20.yml" +PROJECTION_PATH = DEPLOYMENT_ROOT / "agent_scripts/Stage_2_S2_20.yml" +MIRROR_PY_PATH = DEPLOYMENT_ROOT / "runtime/s2_20_reduce.py" +MIRROR_TXT_PATH = DEPLOYMENT_ROOT / "runtime/s2_20_reduce.txt" +RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest/s2_20_inline_code_receipt.json" + +EXPECTED_AGENT_NAME = "Stage_2_S2_20" +EXPECTED_AGENT_VERSION = "1.0.0" +EXPECTED_STAGE_NAME = "S2_20" +EXPECTED_TASK_NAME = "Task_S2_20_deterministic_relief_plan" +EXPECTED_PARAMETERS = { + "language": "python", + "requirements": "httpx==0.28.1", + "network": "agent-network", + "timeout": 300, +} +EXPECTED_LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp" +EXPECTED_CODE_EXECUTOR_URL = "https://code-executor.mcp.eroomai.com/mcp" +EXPECTED_WEAVIATE_URL = "https://weaviate.eroomai.com/mcp" +ALLOWED_CODE_URLS = frozenset({EXPECTED_LOCALDOCS_URL, EXPECTED_WEAVIATE_URL}) +ALLOWED_NON_STDLIB_IMPORTS = frozenset({"httpx"}) +FORBIDDEN_IMPORT_ROOTS = frozenset( + {"ftplib", "http", "importlib", "smtplib", "socket", "subprocess", "telnetlib", "urllib", "xmlrpc"} +) +FORBIDDEN_CALL_NAMES = frozenset({"__import__", "compile", "eval", "exec", "open"}) +FORBIDDEN_ATTRIBUTE_CALLS = frozenset( + { + ("os", "popen"), + ("os", "system"), + ("os", "spawnl"), + ("os", "spawnle"), + ("os", "spawnlp"), + ("os", "spawnlpe"), + ("os", "spawnv"), + ("os", "spawnve"), + ("os", "spawnvp"), + ("os", "spawnvpe"), + } +) +MODEL_FIELDS = frozenset({"llm_provider", "llm_model", "llm_reasoning", "llm_verbosity", "prompt"}) +REQUIRED_CODE_TOKENS = ( + EXPECTED_LOCALDOCS_URL, + EXPECTED_WEAVIATE_URL, + "EXPECTED_STAGE2_RELEASE_SHA256", + "{{__user_hash__}}", + "{{__workspace_hash__}}", + "read_binary_doc", + "write_binary_file", + "plan_publish_status.json", + "build_portfolio", + "build_party_projection", + "calculate_all", + "build_retrieval_branch", + "execute_core", + "publish", + "publish_diagnostic", +) +URL_RE = re.compile(r"https?://[^\s'\"]+") +PYTHON_SOURCE_REF_RE = re.compile(r"(?i)(?:^|[/\\])[^\r\n'\"]+\.py(?:$|[?#])") +PLACEHOLDER_COMMENT_RE = re.compile( + r"(?im)^\s*#\s*(?:TODO|FIXME|TBD|PLACEHOLDER|OMITTED\s+BODY|IMPLEMENT\s+ME)\b" +) +PLAINTEXT_SECRET_RES = ( + re.compile(r"(?i)\bAuthorization\s*:\s*Bearer\s+\S+"), + re.compile(r"(?i)\bBearer\s+[A-Za-z0-9+/=_-]{12,}"), + re.compile( + r"(?i)\b(?:MCP_API_KEY|API_KEY|ACCESS_TOKEN|AUTH_TOKEN|CLIENT_SECRET)\s*=\s*['\"][^'\"]+['\"]" + ), +) + + +class ProjectionError(RuntimeError): + """Controlled build failure with a stable reason code.""" + + def __init__(self, code: str, detail: str) -> None: + super().__init__(f"{code}: {detail}") + self.code = code + self.detail = detail + + +if yaml is not None: + + class UniqueKeySafeLoader(yaml.SafeLoader): + """SafeLoader variant that rejects duplicate mapping keys recursively.""" + + def construct_mapping(self, node: Any, deep: bool = False) -> dict[Any, Any]: + if not isinstance(node, yaml.MappingNode): + raise ProjectionError("YAML_MAPPING_REQUIRED", repr(node)[:200]) + self.flatten_mapping(node) + result: dict[Any, Any] = {} + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + try: + duplicate = key in result + except TypeError as exc: + raise ProjectionError("YAML_UNHASHABLE_KEY", repr(key)[:200]) from exc + if duplicate: + mark = getattr(key_node, "start_mark", None) + line = mark.line + 1 if mark is not None else "?" + raise ProjectionError("YAML_DUPLICATE_KEY", f"{key!r}@line={line}") + result[key] = self.construct_object(value_node, deep=deep) + return result + +else: # pragma: no cover + + class UniqueKeySafeLoader: # type: ignore[no-redef] + pass + + +def sha256_bytes(value: bytes) -> str: + return hashlib.sha256(value).hexdigest() + + +def canonical_json_bytes(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, allow_nan=False, sort_keys=True, separators=(",", ":")) + + "\n" + ).encode("utf-8") + + +def _logical_path(path: Path) -> str: + try: + return path.resolve(strict=False).relative_to(MAIN_WORKING_DIRECTORY.resolve(strict=False)).as_posix() + except ValueError: + return path.as_posix() + + +def _require_mapping(value: Any, code: str) -> dict[str, Any]: + if not isinstance(value, dict): + raise ProjectionError(code, repr(value)[:300]) + return value + + +def _require_list(value: Any, code: str) -> list[Any]: + if not isinstance(value, list): + raise ProjectionError(code, repr(value)[:300]) + return value + + +def parse_authoring_bytes(raw: bytes, *, source: str = "") -> dict[str, Any]: + if yaml is None: + raise ProjectionError("PYYAML_REQUIRED", "install PyYAML on offline build host") + try: + text = raw.decode("utf-8") + except UnicodeDecodeError as exc: + raise ProjectionError("AUTHORING_UTF8_REQUIRED", f"{source}: {exc}") from exc + if text.startswith("\ufeff"): + raise ProjectionError("AUTHORING_UTF8_BOM_FORBIDDEN", source) + for pattern in PLAINTEXT_SECRET_RES: + if pattern.search(text): + raise ProjectionError("AUTHORING_PLAINTEXT_SECRET", pattern.pattern) + try: + document = yaml.load(text, Loader=UniqueKeySafeLoader) + except ProjectionError: + raise + except yaml.YAMLError as exc: + raise ProjectionError("AUTHORING_YAML_INVALID", f"{source}: {exc}") from exc + if not isinstance(document, dict): + raise ProjectionError("AUTHORING_ROOT_OBJECT_REQUIRED", source) + return document + + +def load_authoring(path: Path | None = None) -> tuple[bytes, dict[str, Any]]: + selected = AUTHORING_PATH if path is None else path + if not selected.is_file() or selected.is_symlink(): + raise ProjectionError("AUTHORING_YAML_MISSING_OR_SYMLINK", selected.as_posix()) + raw = selected.read_bytes() + return raw, parse_authoring_bytes(raw, source=selected.as_posix()) + + +def _find_forbidden_model_fields(value: Any, *, pointer: str = "") -> list[str]: + findings: list[str] = [] + if isinstance(value, dict): + for key, child in value.items(): + child_pointer = f"{pointer}/{key}" + if key in MODEL_FIELDS: + findings.append(child_pointer) + findings.extend(_find_forbidden_model_fields(child, pointer=child_pointer)) + elif isinstance(value, list): + for index, child in enumerate(value): + findings.extend(_find_forbidden_model_fields(child, pointer=f"{pointer}/{index}")) + return findings + + +def extract_run_code_task(document: Mapping[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]: + agent = _require_mapping(document.get("Agent"), "AGENT_OBJECT_REQUIRED") + if agent.get("name") != EXPECTED_AGENT_NAME or agent.get("version") != EXPECTED_AGENT_VERSION: + raise ProjectionError( + "AGENT_IDENTITY_MISMATCH", + f"expected={EXPECTED_AGENT_NAME}/{EXPECTED_AGENT_VERSION} observed={agent.get('name')}/{agent.get('version')}", + ) + forbidden_model_fields = _find_forbidden_model_fields(agent) + if forbidden_model_fields: + raise ProjectionError("LLM_OR_PROMPT_FIELD_FORBIDDEN", repr(forbidden_model_fields)) + stages = _require_list(agent.get("Stages"), "STAGES_ARRAY_REQUIRED") + if len(stages) != 1: + raise ProjectionError("EXACTLY_ONE_STAGE_REQUIRED", str(len(stages))) + stage = _require_mapping(stages[0], "STAGE_OBJECT_REQUIRED") + if stage.get("name") != EXPECTED_STAGE_NAME: + raise ProjectionError("S2_20_STAGE_NAME_REQUIRED", repr(stage.get("name"))) + if stage.get("skip_confirm") is not True: + raise ProjectionError("S2_20_SKIP_CONFIRM_TRUE_REQUIRED", repr(stage.get("skip_confirm"))) + if stage.get("prevs") != [] or stage.get("nexts") != []: + raise ProjectionError("STANDALONE_STAGE_EDGES_MUST_BE_EMPTY", repr(stage)) + + servers = _require_mapping( + _require_mapping(stage.get("tools"), "TOOLS_OBJECT_REQUIRED").get("mcpServers"), + "MCP_SERVERS_OBJECT_REQUIRED", + ) + if set(servers) != {"localdocs", "code-executor"}: + raise ProjectionError("MCP_SERVER_SET_MISMATCH", repr(sorted(servers))) + expected_servers = { + "localdocs": {"type": "streamable-http", "url": EXPECTED_LOCALDOCS_URL}, + "code-executor": {"type": "streamable-http", "url": EXPECTED_CODE_EXECUTOR_URL}, + } + for name, expected in expected_servers.items(): + observed = _require_mapping(servers.get(name), f"{name.upper()}_SERVER_REQUIRED") + if observed != expected: + raise ProjectionError("MCP_SERVER_BINDING_INVALID", f"{name}:{observed!r}") + + tasks = _require_list(stage.get("tasks"), "TASKS_ARRAY_REQUIRED") + if len(tasks) != 1: + raise ProjectionError("EXACTLY_ONE_TASK_REQUIRED", str(len(tasks))) + task = _require_mapping(tasks[0], "TASK_OBJECT_REQUIRED") + run_code_tasks = [ + value + for candidate_stage in stages + for value in _require_list( + _require_mapping(candidate_stage, "STAGE_OBJECT_REQUIRED").get("tasks"), + "TASKS_ARRAY_REQUIRED", + ) + if isinstance(value, dict) + and value.get("mcp") == "code-executor" + and value.get("tool_name") == "run_code" + ] + if len(run_code_tasks) != 1: + raise ProjectionError("EXACTLY_ONE_CODE_EXECUTOR_RUN_CODE_REQUIRED", str(len(run_code_tasks))) + if task.get("task_name") != EXPECTED_TASK_NAME: + raise ProjectionError("TASK_NAME_MISMATCH", repr(task.get("task_name"))) + if task.get("mcp") != "code-executor" or task.get("tool_name") != "run_code": + raise ProjectionError("RUN_CODE_TASK_BINDING_MISMATCH", repr(task)) + parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED") + if set(parameters) != set(EXPECTED_PARAMETERS) | {"code"}: + raise ProjectionError("RUN_CODE_PARAMETER_SET_MISMATCH", repr(sorted(parameters))) + for key, expected in EXPECTED_PARAMETERS.items(): + if parameters.get(key) != expected: + raise ProjectionError( + "RUN_CODE_PARAMETER_MISMATCH", f"{key}: expected={expected!r} observed={parameters.get(key)!r}" + ) + code = parameters.get("code") + if not isinstance(code, str) or not code: + raise ProjectionError("INLINE_CODE_REQUIRED", repr(code)[:200]) + if code.endswith(("\n", "\r")): + raise ProjectionError( + "INLINE_CODE_TRAILING_NEWLINE_FORBIDDEN", + "use code: |-; parser-returned code bytes must not be transformed", + ) + + expected_procedure = { + "IN": {"nexts": [EXPECTED_TASK_NAME], "wait_until": []}, + EXPECTED_TASK_NAME: {"nexts": ["OUT"], "wait_until": ["IN"]}, + "OUT": {"nexts": [], "wait_until": [EXPECTED_TASK_NAME]}, + } + procedure = _require_mapping(stage.get("task_procedure"), "TASK_PROCEDURE_REQUIRED") + if procedure != expected_procedure: + raise ProjectionError("TASK_PROCEDURE_EXACT_IN_TASK_OUT_REQUIRED", repr(procedure)[:800]) + description = stage.get("description") + if not isinstance(description, str) or not all(token in description for token in ("C20", "C21", "C25", "C26", "C22", "C27", "C28", "C29", "C30", "C35")): + raise ProjectionError("INTERNAL_DAG_DESCRIPTION_REQUIRED", repr(description)) + return stage, task + + +def extract_code(raw: bytes) -> bytes: + """Backward-compatible parser-based extraction used by existing tests.""" + + document = parse_authoring_bytes(raw) + _, task = extract_run_code_task(document) + parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED") + return parameters["code"].encode("utf-8") + + +def _import_root(name: str | None) -> str: + return (name or "").split(".", 1)[0] + + +def _assigned_names(node: ast.Assign | ast.AnnAssign) -> set[str]: + targets = node.targets if isinstance(node, ast.Assign) else [node.target] + return {target.id for target in targets if isinstance(target, ast.Name)} + + +def validate_inline_code(code: str) -> dict[str, Any]: + try: + compile(code, "", "exec", dont_inherit=True) + tree = ast.parse(code, filename="", mode="exec") + except (SyntaxError, ValueError, UnicodeError) as exc: + raise ProjectionError("INLINE_CODE_COMPILE_FAILED", str(exc)) from exc + missing_tokens = [token for token in REQUIRED_CODE_TOKENS if token not in code] + if missing_tokens: + raise ProjectionError("INLINE_CODE_REQUIRED_TOKEN_MISSING", repr(missing_tokens)) + if PLACEHOLDER_COMMENT_RE.search(code): + raise ProjectionError("INLINE_CODE_PLACEHOLDER_COMMENT", "TODO/FIXME/TBD placeholder") + for pattern in PLAINTEXT_SECRET_RES: + if pattern.search(code): + raise ProjectionError("INLINE_CODE_PLAINTEXT_SECRET", pattern.pattern) + + imports: set[str] = set() + import_aliases: dict[str, str] = {} + forbidden_callable_aliases: set[str] = set() + forbidden_nodes: list[str] = [] + external_source_refs: set[str] = set() + observed_urls: set[str] = set() + release_constants: list[str] = [] + + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + root = _import_root(alias.name) + imports.add(root) + import_aliases[alias.asname or root] = root + elif isinstance(node, ast.ImportFrom): + root = _import_root(node.module) + imports.add(root) + if node.level: + forbidden_nodes.append(f"relative-import:{node.module or ''}") + for alias in node.names: + local = alias.asname or alias.name + if root == "builtins" and alias.name in FORBIDDEN_CALL_NAMES: + forbidden_callable_aliases.add(local) + if root == "os" and ( + ("os", alias.name) in FORBIDDEN_ATTRIBUTE_CALLS or alias.name.startswith("exec") + ): + forbidden_callable_aliases.add(local) + + for node in ast.walk(tree): + if isinstance(node, (ast.Assign, ast.AnnAssign)): + value = node.value + names = _assigned_names(node) + if isinstance(value, ast.Name) and ( + value.id in FORBIDDEN_CALL_NAMES or value.id in forbidden_callable_aliases + ): + forbidden_callable_aliases.update(names) + if isinstance(value, ast.Attribute) and isinstance(value.value, ast.Name): + module = import_aliases.get(value.value.id, value.value.id) + if (module, value.attr) in FORBIDDEN_ATTRIBUTE_CALLS or ( + module == "os" and value.attr.startswith("exec") + ): + forbidden_callable_aliases.update(names) + if ( + "EXPECTED_STAGE2_RELEASE_SHA256" in names + and isinstance(value, ast.Constant) + and isinstance(value.value, str) + ): + release_constants.append(value.value) + elif isinstance(node, ast.Call): + if isinstance(node.func, ast.Name) and ( + node.func.id in FORBIDDEN_CALL_NAMES or node.func.id in forbidden_callable_aliases + ): + forbidden_nodes.append(f"call:{node.func.id}") + elif isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name): + module = import_aliases.get(node.func.value.id, node.func.value.id) + if (module, node.func.attr) in FORBIDDEN_ATTRIBUTE_CALLS or ( + module == "os" and node.func.attr.startswith("exec") + ): + forbidden_nodes.append(f"call:{module}.{node.func.attr}") + elif isinstance(node, ast.Pass): + forbidden_nodes.append("Pass") + elif isinstance(node, ast.Raise): + target = node.exc.func if isinstance(node.exc, ast.Call) else node.exc + if isinstance(target, ast.Name) and target.id == "NotImplementedError": + forbidden_nodes.append("raise:NotImplementedError") + elif isinstance(node, ast.Constant): + if node.value is Ellipsis: + forbidden_nodes.append("Ellipsis") + if isinstance(node.value, str): + observed_urls.update(match.rstrip(".,);]") for match in URL_RE.findall(node.value)) + if PYTHON_SOURCE_REF_RE.search(node.value.strip()): + external_source_refs.add(node.value[:200]) + + imports.discard("") + disallowed_imports = sorted( + value + for value in imports + if value in FORBIDDEN_IMPORT_ROOTS + or (value not in sys.stdlib_module_names and value not in ALLOWED_NON_STDLIB_IMPORTS) + ) + if disallowed_imports: + raise ProjectionError("INLINE_CODE_IMPORT_FORBIDDEN", repr(disallowed_imports)) + if forbidden_nodes: + raise ProjectionError("INLINE_CODE_DYNAMIC_OR_PLACEHOLDER_FORBIDDEN", repr(forbidden_nodes)) + if external_source_refs: + raise ProjectionError("INLINE_CODE_EXTERNAL_PY_SOURCE_REF_FORBIDDEN", repr(sorted(external_source_refs))) + if observed_urls != set(ALLOWED_CODE_URLS): + raise ProjectionError( + "INLINE_CODE_ENDPOINT_LITERAL_SET_MISMATCH", + json.dumps( + {"expected": sorted(ALLOWED_CODE_URLS), "observed": sorted(observed_urls)}, + ensure_ascii=False, + sort_keys=True, + ), + ) + if len(release_constants) != 1 or not re.fullmatch(r"[a-f0-9]{64}", release_constants[0]): + raise ProjectionError("EXPECTED_PARENT_RELEASE_CONSTANT_EXACT_ONE_REQUIRED", repr(release_constants)) + code_bytes = code.encode("utf-8") + return { + "yaml_pointer": "/Agent/Stages/0/tasks/0/parameters/code", + "encoding": "UTF-8", + "extraction_transform": "NONE", + "code_sha256": sha256_bytes(code_bytes), + "code_size_bytes": len(code_bytes), + "compile_status": "PASS", + "ast_status": "PASS", + "imports": sorted(imports), + "forbidden_import_count": 0, + "forbidden_dynamic_call_count": 0, + "external_python_source_ref_count": 0, + "plaintext_secret_count": 0, + "endpoint_literals": sorted(observed_urls), + "expected_parent_stage2_release_sha256": release_constants[0], + } + + +def validate_agent(raw: bytes, code: bytes | None = None) -> dict[str, Any]: + """Backward-compatible complete validation of authoring and extracted code.""" + + document = parse_authoring_bytes(raw) + _, task = extract_run_code_task(document) + parsed_code = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED")["code"] + parsed_bytes = parsed_code.encode("utf-8") + if code is not None and code != parsed_bytes: + raise ProjectionError("EXTRACTED_CODE_BYTES_DRIFT", sha256_bytes(code)) + return validate_inline_code(parsed_code) + + +def _canonical_task_semantics( + document: Mapping[str, Any], stage: Mapping[str, Any], task: Mapping[str, Any] +) -> dict[str, Any]: + agent = _require_mapping(document.get("Agent"), "AGENT_OBJECT_REQUIRED") + servers = _require_mapping( + _require_mapping(stage.get("tools"), "TOOLS_OBJECT_REQUIRED").get("mcpServers"), + "MCP_SERVERS_OBJECT_REQUIRED", + ) + parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED") + return { + "agent": {"name": agent.get("name"), "version": agent.get("version")}, + "stage": { + "name": stage.get("name"), + "prevs": stage.get("prevs"), + "nexts": stage.get("nexts"), + "skip_confirm": stage.get("skip_confirm"), + }, + "mcp_servers": { + name: {"type": value.get("type"), "url": value.get("url")} + for name, value in sorted(servers.items()) + if isinstance(value, dict) + }, + "task": { + "task_name": task.get("task_name"), + "mcp": task.get("mcp"), + "tool_name": task.get("tool_name"), + "parameters": {key: parameters.get(key) for key in EXPECTED_PARAMETERS}, + "code_sha256": sha256_bytes(parameters["code"].encode("utf-8")), + }, + "task_procedure": stage.get("task_procedure"), + "internal_dag": "(C20||C21)->C25->C26->(C22||(C27->C28->C29))->C30->C35", + } + + +def expected_outputs( + authoring_raw: bytes, document: Mapping[str, Any] +) -> tuple[dict[Path, bytes], dict[str, Any]]: + stage, task = extract_run_code_task(document) + parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED") + code = parameters["code"] + validation = validate_inline_code(code) + code_bytes = code.encode("utf-8") + semantics = _canonical_task_semantics(document, stage, task) + receipt = { + "schema_version": "stage2_s2_20_inline_code_receipt.v1", + "workflow_id": "S2_20", + "authoring": { + "path": _logical_path(AUTHORING_PATH), + "sha256": sha256_bytes(authoring_raw), + "size_bytes": len(authoring_raw), + }, + "deployment_projection": { + "path": _logical_path(PROJECTION_PATH), + "sha256": sha256_bytes(authoring_raw), + "size_bytes": len(authoring_raw), + }, + "canonical_code": validation, + "full_code_mirrors": [_logical_path(MIRROR_PY_PATH), _logical_path(MIRROR_TXT_PATH)], + "task_contract": { + **semantics, + "canonical_task_semantics_sha256": sha256_bytes(canonical_json_bytes(semantics)), + "exactly_one_stage": True, + "exactly_one_task": True, + "exactly_one_code_executor_run_code": True, + "authoring_rewritten": False, + "projection_byte_identical_to_authoring": True, + "code_mirrors_byte_identical": True, + "expected_parent_stage2_release_sha256": validation[ + "expected_parent_stage2_release_sha256" + ], + }, + "parity_status": "PASS", + } + outputs = { + PROJECTION_PATH: authoring_raw, + MIRROR_PY_PATH: code_bytes, + MIRROR_TXT_PATH: code_bytes, + RECEIPT_PATH: canonical_json_bytes(receipt), + } + return outputs, receipt + + +def _assert_output_target(path: Path) -> None: + root = DEPLOYMENT_ROOT.resolve(strict=True) + resolved = path.resolve(strict=False) + try: + resolved.relative_to(root) + except ValueError as exc: + raise ProjectionError("OUTPUT_OUTSIDE_DEPLOYMENT_ROOT", path.as_posix()) from exc + if path.exists() and path.is_symlink(): + raise ProjectionError("OUTPUT_SYMLINK_FORBIDDEN", path.as_posix()) + + +def _atomic_write(path: Path, payload: bytes) -> None: + _assert_output_target(path) + path.parent.mkdir(parents=True, exist_ok=True) + temporary_name: str | None = None + try: + with tempfile.NamedTemporaryFile( + mode="wb", + dir=path.parent, + prefix=f".{path.name}.", + suffix=".tmp", + delete=False, + ) as handle: + temporary_name = handle.name + handle.write(payload) + handle.flush() + os.fsync(handle.fileno()) + os.replace(temporary_name, path) + temporary_name = None + finally: + if temporary_name is not None: + try: + Path(temporary_name).unlink() + except FileNotFoundError: + pass + + +def compare_outputs(outputs: Mapping[Path, bytes]) -> list[dict[str, Any]]: + mismatches: list[dict[str, Any]] = [] + for path, expected in outputs.items(): + if not path.is_file(): + mismatches.append( + {"path": _logical_path(path), "status": "MISSING", "expected_sha256": sha256_bytes(expected)} + ) + continue + observed = path.read_bytes() + if observed != expected: + mismatches.append( + { + "path": _logical_path(path), + "status": "BYTE_MISMATCH", + "expected_sha256": sha256_bytes(expected), + "observed_sha256": sha256_bytes(observed), + } + ) + return mismatches + + +def run(*, check: bool) -> tuple[int, dict[str, Any]]: + before, document = load_authoring() + outputs, receipt = expected_outputs(before, document) + if check: + mismatches = compare_outputs(outputs) + return ( + 0 if not mismatches else 1, + { + "status": "PARITY_PASS" if not mismatches else "PARITY_DRIFT", + "mode": "CHECK_NO_WRITE", + "authoring_sha256": sha256_bytes(before), + "code_sha256": receipt["canonical_code"]["code_sha256"], + "mismatches": mismatches, + }, + ) + + receipt_payload = outputs[RECEIPT_PATH] + for path, payload in outputs.items(): + if path != RECEIPT_PATH: + _atomic_write(path, payload) + if AUTHORING_PATH.read_bytes() != before: + raise ProjectionError("AUTHORING_CHANGED_DURING_BUILD", AUTHORING_PATH.as_posix()) + _atomic_write(RECEIPT_PATH, receipt_payload) + if AUTHORING_PATH.read_bytes() != before: + raise ProjectionError("AUTHORING_CHANGED_DURING_RECEIPT_WRITE", AUTHORING_PATH.as_posix()) + mismatches = compare_outputs(outputs) + if mismatches: + raise ProjectionError("POST_BUILD_PARITY_FAILED", json.dumps(mismatches, sort_keys=True)) + return 0, { + "status": "BUILT_AND_VERIFIED", + "mode": "BUILD", + "authoring_sha256": sha256_bytes(before), + "code_sha256": receipt["canonical_code"]["code_sha256"], + "outputs": [_logical_path(path) for path in outputs], + } + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Build or verify S2_20 inline Agent projection") + parser.add_argument("--check", action="store_true", help="no-write byte parity check") + return parser + + +def main(argv: Iterable[str] | None = None) -> int: + args = _parser().parse_args(list(argv) if argv is not None else None) + try: + status, payload = run(check=args.check) + except ProjectionError as exc: + status = 3 if exc.code == "AUTHORING_YAML_MISSING_OR_SYMLINK" else 2 + payload = { + "status": "CONTROLLED_MISSING_AUTHORING" if status == 3 else "BUILD_FAILED", + "reason_code": exc.code, + "detail": exc.detail, + "mode": "CHECK_NO_WRITE" if args.check else "BUILD", + } + print(json.dumps(payload, ensure_ascii=False, allow_nan=False, sort_keys=True)) + return status + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_20_inline_projection.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_20_inline_projection.txt new file mode 100644 index 00000000..e45a0931 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/build_s2_20_inline_projection.txt @@ -0,0 +1,665 @@ +#!/usr/bin/env python3 +"""Build/check the S2_20 single-task inline Python projection. + +The authoring YAML is the sole editable source. This offline tool rejects +duplicate YAML keys, validates the exact AgentBackend task graph and MCP +``run_code`` contract, compiles and audits parser-returned inline Python, then +projects authoring/code bytes without transformation. It never executes a +matter or imports Stage 2 runtime modules. +""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +import os +from pathlib import Path +import re +import sys +import tempfile +from typing import Any, Iterable, Mapping + +try: + import yaml +except ImportError: # pragma: no cover - incomplete offline build host + yaml = None # type: ignore[assignment] + + +DEPLOYMENT_ROOT = Path(__file__).resolve().parents[1] +MAIN_WORKING_DIRECTORY = DEPLOYMENT_ROOT.parent.parent +AUTHORING_PATH = MAIN_WORKING_DIRECTORY / "Stage_2_S2_20.yml" +PROJECTION_PATH = DEPLOYMENT_ROOT / "agent_scripts/Stage_2_S2_20.yml" +MIRROR_PY_PATH = DEPLOYMENT_ROOT / "runtime/s2_20_reduce.py" +MIRROR_TXT_PATH = DEPLOYMENT_ROOT / "runtime/s2_20_reduce.txt" +RECEIPT_PATH = DEPLOYMENT_ROOT / "manifest/s2_20_inline_code_receipt.json" + +EXPECTED_AGENT_NAME = "Stage_2_S2_20" +EXPECTED_AGENT_VERSION = "1.0.0" +EXPECTED_STAGE_NAME = "S2_20" +EXPECTED_TASK_NAME = "Task_S2_20_deterministic_relief_plan" +EXPECTED_PARAMETERS = { + "language": "python", + "requirements": "httpx==0.28.1", + "network": "agent-network", + "timeout": 300, +} +EXPECTED_LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp" +EXPECTED_CODE_EXECUTOR_URL = "https://code-executor.mcp.eroomai.com/mcp" +EXPECTED_WEAVIATE_URL = "https://weaviate.eroomai.com/mcp" +ALLOWED_CODE_URLS = frozenset({EXPECTED_LOCALDOCS_URL, EXPECTED_WEAVIATE_URL}) +ALLOWED_NON_STDLIB_IMPORTS = frozenset({"httpx"}) +FORBIDDEN_IMPORT_ROOTS = frozenset( + {"ftplib", "http", "importlib", "smtplib", "socket", "subprocess", "telnetlib", "urllib", "xmlrpc"} +) +FORBIDDEN_CALL_NAMES = frozenset({"__import__", "compile", "eval", "exec", "open"}) +FORBIDDEN_ATTRIBUTE_CALLS = frozenset( + { + ("os", "popen"), + ("os", "system"), + ("os", "spawnl"), + ("os", "spawnle"), + ("os", "spawnlp"), + ("os", "spawnlpe"), + ("os", "spawnv"), + ("os", "spawnve"), + ("os", "spawnvp"), + ("os", "spawnvpe"), + } +) +MODEL_FIELDS = frozenset({"llm_provider", "llm_model", "llm_reasoning", "llm_verbosity", "prompt"}) +REQUIRED_CODE_TOKENS = ( + EXPECTED_LOCALDOCS_URL, + EXPECTED_WEAVIATE_URL, + "EXPECTED_STAGE2_RELEASE_SHA256", + "{{__user_hash__}}", + "{{__workspace_hash__}}", + "read_binary_doc", + "write_binary_file", + "plan_publish_status.json", + "build_portfolio", + "build_party_projection", + "calculate_all", + "build_retrieval_branch", + "execute_core", + "publish", + "publish_diagnostic", +) +URL_RE = re.compile(r"https?://[^\s'\"]+") +PYTHON_SOURCE_REF_RE = re.compile(r"(?i)(?:^|[/\\])[^\r\n'\"]+\.py(?:$|[?#])") +PLACEHOLDER_COMMENT_RE = re.compile( + r"(?im)^\s*#\s*(?:TODO|FIXME|TBD|PLACEHOLDER|OMITTED\s+BODY|IMPLEMENT\s+ME)\b" +) +PLAINTEXT_SECRET_RES = ( + re.compile(r"(?i)\bAuthorization\s*:\s*Bearer\s+\S+"), + re.compile(r"(?i)\bBearer\s+[A-Za-z0-9+/=_-]{12,}"), + re.compile( + r"(?i)\b(?:MCP_API_KEY|API_KEY|ACCESS_TOKEN|AUTH_TOKEN|CLIENT_SECRET)\s*=\s*['\"][^'\"]+['\"]" + ), +) + + +class ProjectionError(RuntimeError): + """Controlled build failure with a stable reason code.""" + + def __init__(self, code: str, detail: str) -> None: + super().__init__(f"{code}: {detail}") + self.code = code + self.detail = detail + + +if yaml is not None: + + class UniqueKeySafeLoader(yaml.SafeLoader): + """SafeLoader variant that rejects duplicate mapping keys recursively.""" + + def construct_mapping(self, node: Any, deep: bool = False) -> dict[Any, Any]: + if not isinstance(node, yaml.MappingNode): + raise ProjectionError("YAML_MAPPING_REQUIRED", repr(node)[:200]) + self.flatten_mapping(node) + result: dict[Any, Any] = {} + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + try: + duplicate = key in result + except TypeError as exc: + raise ProjectionError("YAML_UNHASHABLE_KEY", repr(key)[:200]) from exc + if duplicate: + mark = getattr(key_node, "start_mark", None) + line = mark.line + 1 if mark is not None else "?" + raise ProjectionError("YAML_DUPLICATE_KEY", f"{key!r}@line={line}") + result[key] = self.construct_object(value_node, deep=deep) + return result + +else: # pragma: no cover + + class UniqueKeySafeLoader: # type: ignore[no-redef] + pass + + +def sha256_bytes(value: bytes) -> str: + return hashlib.sha256(value).hexdigest() + + +def canonical_json_bytes(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, allow_nan=False, sort_keys=True, separators=(",", ":")) + + "\n" + ).encode("utf-8") + + +def _logical_path(path: Path) -> str: + try: + return path.resolve(strict=False).relative_to(MAIN_WORKING_DIRECTORY.resolve(strict=False)).as_posix() + except ValueError: + return path.as_posix() + + +def _require_mapping(value: Any, code: str) -> dict[str, Any]: + if not isinstance(value, dict): + raise ProjectionError(code, repr(value)[:300]) + return value + + +def _require_list(value: Any, code: str) -> list[Any]: + if not isinstance(value, list): + raise ProjectionError(code, repr(value)[:300]) + return value + + +def parse_authoring_bytes(raw: bytes, *, source: str = "") -> dict[str, Any]: + if yaml is None: + raise ProjectionError("PYYAML_REQUIRED", "install PyYAML on offline build host") + try: + text = raw.decode("utf-8") + except UnicodeDecodeError as exc: + raise ProjectionError("AUTHORING_UTF8_REQUIRED", f"{source}: {exc}") from exc + if text.startswith("\ufeff"): + raise ProjectionError("AUTHORING_UTF8_BOM_FORBIDDEN", source) + for pattern in PLAINTEXT_SECRET_RES: + if pattern.search(text): + raise ProjectionError("AUTHORING_PLAINTEXT_SECRET", pattern.pattern) + try: + document = yaml.load(text, Loader=UniqueKeySafeLoader) + except ProjectionError: + raise + except yaml.YAMLError as exc: + raise ProjectionError("AUTHORING_YAML_INVALID", f"{source}: {exc}") from exc + if not isinstance(document, dict): + raise ProjectionError("AUTHORING_ROOT_OBJECT_REQUIRED", source) + return document + + +def load_authoring(path: Path | None = None) -> tuple[bytes, dict[str, Any]]: + selected = AUTHORING_PATH if path is None else path + if not selected.is_file() or selected.is_symlink(): + raise ProjectionError("AUTHORING_YAML_MISSING_OR_SYMLINK", selected.as_posix()) + raw = selected.read_bytes() + return raw, parse_authoring_bytes(raw, source=selected.as_posix()) + + +def _find_forbidden_model_fields(value: Any, *, pointer: str = "") -> list[str]: + findings: list[str] = [] + if isinstance(value, dict): + for key, child in value.items(): + child_pointer = f"{pointer}/{key}" + if key in MODEL_FIELDS: + findings.append(child_pointer) + findings.extend(_find_forbidden_model_fields(child, pointer=child_pointer)) + elif isinstance(value, list): + for index, child in enumerate(value): + findings.extend(_find_forbidden_model_fields(child, pointer=f"{pointer}/{index}")) + return findings + + +def extract_run_code_task(document: Mapping[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]: + agent = _require_mapping(document.get("Agent"), "AGENT_OBJECT_REQUIRED") + if agent.get("name") != EXPECTED_AGENT_NAME or agent.get("version") != EXPECTED_AGENT_VERSION: + raise ProjectionError( + "AGENT_IDENTITY_MISMATCH", + f"expected={EXPECTED_AGENT_NAME}/{EXPECTED_AGENT_VERSION} observed={agent.get('name')}/{agent.get('version')}", + ) + forbidden_model_fields = _find_forbidden_model_fields(agent) + if forbidden_model_fields: + raise ProjectionError("LLM_OR_PROMPT_FIELD_FORBIDDEN", repr(forbidden_model_fields)) + stages = _require_list(agent.get("Stages"), "STAGES_ARRAY_REQUIRED") + if len(stages) != 1: + raise ProjectionError("EXACTLY_ONE_STAGE_REQUIRED", str(len(stages))) + stage = _require_mapping(stages[0], "STAGE_OBJECT_REQUIRED") + if stage.get("name") != EXPECTED_STAGE_NAME: + raise ProjectionError("S2_20_STAGE_NAME_REQUIRED", repr(stage.get("name"))) + if stage.get("skip_confirm") is not True: + raise ProjectionError("S2_20_SKIP_CONFIRM_TRUE_REQUIRED", repr(stage.get("skip_confirm"))) + if stage.get("prevs") != [] or stage.get("nexts") != []: + raise ProjectionError("STANDALONE_STAGE_EDGES_MUST_BE_EMPTY", repr(stage)) + + servers = _require_mapping( + _require_mapping(stage.get("tools"), "TOOLS_OBJECT_REQUIRED").get("mcpServers"), + "MCP_SERVERS_OBJECT_REQUIRED", + ) + if set(servers) != {"localdocs", "code-executor"}: + raise ProjectionError("MCP_SERVER_SET_MISMATCH", repr(sorted(servers))) + expected_servers = { + "localdocs": {"type": "streamable-http", "url": EXPECTED_LOCALDOCS_URL}, + "code-executor": {"type": "streamable-http", "url": EXPECTED_CODE_EXECUTOR_URL}, + } + for name, expected in expected_servers.items(): + observed = _require_mapping(servers.get(name), f"{name.upper()}_SERVER_REQUIRED") + if observed != expected: + raise ProjectionError("MCP_SERVER_BINDING_INVALID", f"{name}:{observed!r}") + + tasks = _require_list(stage.get("tasks"), "TASKS_ARRAY_REQUIRED") + if len(tasks) != 1: + raise ProjectionError("EXACTLY_ONE_TASK_REQUIRED", str(len(tasks))) + task = _require_mapping(tasks[0], "TASK_OBJECT_REQUIRED") + run_code_tasks = [ + value + for candidate_stage in stages + for value in _require_list( + _require_mapping(candidate_stage, "STAGE_OBJECT_REQUIRED").get("tasks"), + "TASKS_ARRAY_REQUIRED", + ) + if isinstance(value, dict) + and value.get("mcp") == "code-executor" + and value.get("tool_name") == "run_code" + ] + if len(run_code_tasks) != 1: + raise ProjectionError("EXACTLY_ONE_CODE_EXECUTOR_RUN_CODE_REQUIRED", str(len(run_code_tasks))) + if task.get("task_name") != EXPECTED_TASK_NAME: + raise ProjectionError("TASK_NAME_MISMATCH", repr(task.get("task_name"))) + if task.get("mcp") != "code-executor" or task.get("tool_name") != "run_code": + raise ProjectionError("RUN_CODE_TASK_BINDING_MISMATCH", repr(task)) + parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED") + if set(parameters) != set(EXPECTED_PARAMETERS) | {"code"}: + raise ProjectionError("RUN_CODE_PARAMETER_SET_MISMATCH", repr(sorted(parameters))) + for key, expected in EXPECTED_PARAMETERS.items(): + if parameters.get(key) != expected: + raise ProjectionError( + "RUN_CODE_PARAMETER_MISMATCH", f"{key}: expected={expected!r} observed={parameters.get(key)!r}" + ) + code = parameters.get("code") + if not isinstance(code, str) or not code: + raise ProjectionError("INLINE_CODE_REQUIRED", repr(code)[:200]) + if code.endswith(("\n", "\r")): + raise ProjectionError( + "INLINE_CODE_TRAILING_NEWLINE_FORBIDDEN", + "use code: |-; parser-returned code bytes must not be transformed", + ) + + expected_procedure = { + "IN": {"nexts": [EXPECTED_TASK_NAME], "wait_until": []}, + EXPECTED_TASK_NAME: {"nexts": ["OUT"], "wait_until": ["IN"]}, + "OUT": {"nexts": [], "wait_until": [EXPECTED_TASK_NAME]}, + } + procedure = _require_mapping(stage.get("task_procedure"), "TASK_PROCEDURE_REQUIRED") + if procedure != expected_procedure: + raise ProjectionError("TASK_PROCEDURE_EXACT_IN_TASK_OUT_REQUIRED", repr(procedure)[:800]) + description = stage.get("description") + if not isinstance(description, str) or not all(token in description for token in ("C20", "C21", "C25", "C26", "C22", "C27", "C28", "C29", "C30", "C35")): + raise ProjectionError("INTERNAL_DAG_DESCRIPTION_REQUIRED", repr(description)) + return stage, task + + +def extract_code(raw: bytes) -> bytes: + """Backward-compatible parser-based extraction used by existing tests.""" + + document = parse_authoring_bytes(raw) + _, task = extract_run_code_task(document) + parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED") + return parameters["code"].encode("utf-8") + + +def _import_root(name: str | None) -> str: + return (name or "").split(".", 1)[0] + + +def _assigned_names(node: ast.Assign | ast.AnnAssign) -> set[str]: + targets = node.targets if isinstance(node, ast.Assign) else [node.target] + return {target.id for target in targets if isinstance(target, ast.Name)} + + +def validate_inline_code(code: str) -> dict[str, Any]: + try: + compile(code, "", "exec", dont_inherit=True) + tree = ast.parse(code, filename="", mode="exec") + except (SyntaxError, ValueError, UnicodeError) as exc: + raise ProjectionError("INLINE_CODE_COMPILE_FAILED", str(exc)) from exc + missing_tokens = [token for token in REQUIRED_CODE_TOKENS if token not in code] + if missing_tokens: + raise ProjectionError("INLINE_CODE_REQUIRED_TOKEN_MISSING", repr(missing_tokens)) + if PLACEHOLDER_COMMENT_RE.search(code): + raise ProjectionError("INLINE_CODE_PLACEHOLDER_COMMENT", "TODO/FIXME/TBD placeholder") + for pattern in PLAINTEXT_SECRET_RES: + if pattern.search(code): + raise ProjectionError("INLINE_CODE_PLAINTEXT_SECRET", pattern.pattern) + + imports: set[str] = set() + import_aliases: dict[str, str] = {} + forbidden_callable_aliases: set[str] = set() + forbidden_nodes: list[str] = [] + external_source_refs: set[str] = set() + observed_urls: set[str] = set() + release_constants: list[str] = [] + + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + root = _import_root(alias.name) + imports.add(root) + import_aliases[alias.asname or root] = root + elif isinstance(node, ast.ImportFrom): + root = _import_root(node.module) + imports.add(root) + if node.level: + forbidden_nodes.append(f"relative-import:{node.module or ''}") + for alias in node.names: + local = alias.asname or alias.name + if root == "builtins" and alias.name in FORBIDDEN_CALL_NAMES: + forbidden_callable_aliases.add(local) + if root == "os" and ( + ("os", alias.name) in FORBIDDEN_ATTRIBUTE_CALLS or alias.name.startswith("exec") + ): + forbidden_callable_aliases.add(local) + + for node in ast.walk(tree): + if isinstance(node, (ast.Assign, ast.AnnAssign)): + value = node.value + names = _assigned_names(node) + if isinstance(value, ast.Name) and ( + value.id in FORBIDDEN_CALL_NAMES or value.id in forbidden_callable_aliases + ): + forbidden_callable_aliases.update(names) + if isinstance(value, ast.Attribute) and isinstance(value.value, ast.Name): + module = import_aliases.get(value.value.id, value.value.id) + if (module, value.attr) in FORBIDDEN_ATTRIBUTE_CALLS or ( + module == "os" and value.attr.startswith("exec") + ): + forbidden_callable_aliases.update(names) + if ( + "EXPECTED_STAGE2_RELEASE_SHA256" in names + and isinstance(value, ast.Constant) + and isinstance(value.value, str) + ): + release_constants.append(value.value) + elif isinstance(node, ast.Call): + if isinstance(node.func, ast.Name) and ( + node.func.id in FORBIDDEN_CALL_NAMES or node.func.id in forbidden_callable_aliases + ): + forbidden_nodes.append(f"call:{node.func.id}") + elif isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name): + module = import_aliases.get(node.func.value.id, node.func.value.id) + if (module, node.func.attr) in FORBIDDEN_ATTRIBUTE_CALLS or ( + module == "os" and node.func.attr.startswith("exec") + ): + forbidden_nodes.append(f"call:{module}.{node.func.attr}") + elif isinstance(node, ast.Pass): + forbidden_nodes.append("Pass") + elif isinstance(node, ast.Raise): + target = node.exc.func if isinstance(node.exc, ast.Call) else node.exc + if isinstance(target, ast.Name) and target.id == "NotImplementedError": + forbidden_nodes.append("raise:NotImplementedError") + elif isinstance(node, ast.Constant): + if node.value is Ellipsis: + forbidden_nodes.append("Ellipsis") + if isinstance(node.value, str): + observed_urls.update(match.rstrip(".,);]") for match in URL_RE.findall(node.value)) + if PYTHON_SOURCE_REF_RE.search(node.value.strip()): + external_source_refs.add(node.value[:200]) + + imports.discard("") + disallowed_imports = sorted( + value + for value in imports + if value in FORBIDDEN_IMPORT_ROOTS + or (value not in sys.stdlib_module_names and value not in ALLOWED_NON_STDLIB_IMPORTS) + ) + if disallowed_imports: + raise ProjectionError("INLINE_CODE_IMPORT_FORBIDDEN", repr(disallowed_imports)) + if forbidden_nodes: + raise ProjectionError("INLINE_CODE_DYNAMIC_OR_PLACEHOLDER_FORBIDDEN", repr(forbidden_nodes)) + if external_source_refs: + raise ProjectionError("INLINE_CODE_EXTERNAL_PY_SOURCE_REF_FORBIDDEN", repr(sorted(external_source_refs))) + if observed_urls != set(ALLOWED_CODE_URLS): + raise ProjectionError( + "INLINE_CODE_ENDPOINT_LITERAL_SET_MISMATCH", + json.dumps( + {"expected": sorted(ALLOWED_CODE_URLS), "observed": sorted(observed_urls)}, + ensure_ascii=False, + sort_keys=True, + ), + ) + if len(release_constants) != 1 or not re.fullmatch(r"[a-f0-9]{64}", release_constants[0]): + raise ProjectionError("EXPECTED_PARENT_RELEASE_CONSTANT_EXACT_ONE_REQUIRED", repr(release_constants)) + code_bytes = code.encode("utf-8") + return { + "yaml_pointer": "/Agent/Stages/0/tasks/0/parameters/code", + "encoding": "UTF-8", + "extraction_transform": "NONE", + "code_sha256": sha256_bytes(code_bytes), + "code_size_bytes": len(code_bytes), + "compile_status": "PASS", + "ast_status": "PASS", + "imports": sorted(imports), + "forbidden_import_count": 0, + "forbidden_dynamic_call_count": 0, + "external_python_source_ref_count": 0, + "plaintext_secret_count": 0, + "endpoint_literals": sorted(observed_urls), + "expected_parent_stage2_release_sha256": release_constants[0], + } + + +def validate_agent(raw: bytes, code: bytes | None = None) -> dict[str, Any]: + """Backward-compatible complete validation of authoring and extracted code.""" + + document = parse_authoring_bytes(raw) + _, task = extract_run_code_task(document) + parsed_code = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED")["code"] + parsed_bytes = parsed_code.encode("utf-8") + if code is not None and code != parsed_bytes: + raise ProjectionError("EXTRACTED_CODE_BYTES_DRIFT", sha256_bytes(code)) + return validate_inline_code(parsed_code) + + +def _canonical_task_semantics( + document: Mapping[str, Any], stage: Mapping[str, Any], task: Mapping[str, Any] +) -> dict[str, Any]: + agent = _require_mapping(document.get("Agent"), "AGENT_OBJECT_REQUIRED") + servers = _require_mapping( + _require_mapping(stage.get("tools"), "TOOLS_OBJECT_REQUIRED").get("mcpServers"), + "MCP_SERVERS_OBJECT_REQUIRED", + ) + parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED") + return { + "agent": {"name": agent.get("name"), "version": agent.get("version")}, + "stage": { + "name": stage.get("name"), + "prevs": stage.get("prevs"), + "nexts": stage.get("nexts"), + "skip_confirm": stage.get("skip_confirm"), + }, + "mcp_servers": { + name: {"type": value.get("type"), "url": value.get("url")} + for name, value in sorted(servers.items()) + if isinstance(value, dict) + }, + "task": { + "task_name": task.get("task_name"), + "mcp": task.get("mcp"), + "tool_name": task.get("tool_name"), + "parameters": {key: parameters.get(key) for key in EXPECTED_PARAMETERS}, + "code_sha256": sha256_bytes(parameters["code"].encode("utf-8")), + }, + "task_procedure": stage.get("task_procedure"), + "internal_dag": "(C20||C21)->C25->C26->(C22||(C27->C28->C29))->C30->C35", + } + + +def expected_outputs( + authoring_raw: bytes, document: Mapping[str, Any] +) -> tuple[dict[Path, bytes], dict[str, Any]]: + stage, task = extract_run_code_task(document) + parameters = _require_mapping(task.get("parameters"), "TASK_PARAMETERS_REQUIRED") + code = parameters["code"] + validation = validate_inline_code(code) + code_bytes = code.encode("utf-8") + semantics = _canonical_task_semantics(document, stage, task) + receipt = { + "schema_version": "stage2_s2_20_inline_code_receipt.v1", + "workflow_id": "S2_20", + "authoring": { + "path": _logical_path(AUTHORING_PATH), + "sha256": sha256_bytes(authoring_raw), + "size_bytes": len(authoring_raw), + }, + "deployment_projection": { + "path": _logical_path(PROJECTION_PATH), + "sha256": sha256_bytes(authoring_raw), + "size_bytes": len(authoring_raw), + }, + "canonical_code": validation, + "full_code_mirrors": [_logical_path(MIRROR_PY_PATH), _logical_path(MIRROR_TXT_PATH)], + "task_contract": { + **semantics, + "canonical_task_semantics_sha256": sha256_bytes(canonical_json_bytes(semantics)), + "exactly_one_stage": True, + "exactly_one_task": True, + "exactly_one_code_executor_run_code": True, + "authoring_rewritten": False, + "projection_byte_identical_to_authoring": True, + "code_mirrors_byte_identical": True, + "expected_parent_stage2_release_sha256": validation[ + "expected_parent_stage2_release_sha256" + ], + }, + "parity_status": "PASS", + } + outputs = { + PROJECTION_PATH: authoring_raw, + MIRROR_PY_PATH: code_bytes, + MIRROR_TXT_PATH: code_bytes, + RECEIPT_PATH: canonical_json_bytes(receipt), + } + return outputs, receipt + + +def _assert_output_target(path: Path) -> None: + root = DEPLOYMENT_ROOT.resolve(strict=True) + resolved = path.resolve(strict=False) + try: + resolved.relative_to(root) + except ValueError as exc: + raise ProjectionError("OUTPUT_OUTSIDE_DEPLOYMENT_ROOT", path.as_posix()) from exc + if path.exists() and path.is_symlink(): + raise ProjectionError("OUTPUT_SYMLINK_FORBIDDEN", path.as_posix()) + + +def _atomic_write(path: Path, payload: bytes) -> None: + _assert_output_target(path) + path.parent.mkdir(parents=True, exist_ok=True) + temporary_name: str | None = None + try: + with tempfile.NamedTemporaryFile( + mode="wb", + dir=path.parent, + prefix=f".{path.name}.", + suffix=".tmp", + delete=False, + ) as handle: + temporary_name = handle.name + handle.write(payload) + handle.flush() + os.fsync(handle.fileno()) + os.replace(temporary_name, path) + temporary_name = None + finally: + if temporary_name is not None: + try: + Path(temporary_name).unlink() + except FileNotFoundError: + pass + + +def compare_outputs(outputs: Mapping[Path, bytes]) -> list[dict[str, Any]]: + mismatches: list[dict[str, Any]] = [] + for path, expected in outputs.items(): + if not path.is_file(): + mismatches.append( + {"path": _logical_path(path), "status": "MISSING", "expected_sha256": sha256_bytes(expected)} + ) + continue + observed = path.read_bytes() + if observed != expected: + mismatches.append( + { + "path": _logical_path(path), + "status": "BYTE_MISMATCH", + "expected_sha256": sha256_bytes(expected), + "observed_sha256": sha256_bytes(observed), + } + ) + return mismatches + + +def run(*, check: bool) -> tuple[int, dict[str, Any]]: + before, document = load_authoring() + outputs, receipt = expected_outputs(before, document) + if check: + mismatches = compare_outputs(outputs) + return ( + 0 if not mismatches else 1, + { + "status": "PARITY_PASS" if not mismatches else "PARITY_DRIFT", + "mode": "CHECK_NO_WRITE", + "authoring_sha256": sha256_bytes(before), + "code_sha256": receipt["canonical_code"]["code_sha256"], + "mismatches": mismatches, + }, + ) + + receipt_payload = outputs[RECEIPT_PATH] + for path, payload in outputs.items(): + if path != RECEIPT_PATH: + _atomic_write(path, payload) + if AUTHORING_PATH.read_bytes() != before: + raise ProjectionError("AUTHORING_CHANGED_DURING_BUILD", AUTHORING_PATH.as_posix()) + _atomic_write(RECEIPT_PATH, receipt_payload) + if AUTHORING_PATH.read_bytes() != before: + raise ProjectionError("AUTHORING_CHANGED_DURING_RECEIPT_WRITE", AUTHORING_PATH.as_posix()) + mismatches = compare_outputs(outputs) + if mismatches: + raise ProjectionError("POST_BUILD_PARITY_FAILED", json.dumps(mismatches, sort_keys=True)) + return 0, { + "status": "BUILT_AND_VERIFIED", + "mode": "BUILD", + "authoring_sha256": sha256_bytes(before), + "code_sha256": receipt["canonical_code"]["code_sha256"], + "outputs": [_logical_path(path) for path in outputs], + } + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Build or verify S2_20 inline Agent projection") + parser.add_argument("--check", action="store_true", help="no-write byte parity check") + return parser + + +def main(argv: Iterable[str] | None = None) -> int: + args = _parser().parse_args(list(argv) if argv is not None else None) + try: + status, payload = run(check=args.check) + except ProjectionError as exc: + status = 3 if exc.code == "AUTHORING_YAML_MISSING_OR_SYMLINK" else 2 + payload = { + "status": "CONTROLLED_MISSING_AUTHORING" if status == 3 else "BUILD_FAILED", + "reason_code": exc.code, + "detail": exc.detail, + "mode": "CHECK_NO_WRITE" if args.check else "BUILD", + } + print(json.dumps(payload, ensure_ascii=False, allow_nan=False, sort_keys=True)) + return status + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_case_type_registry.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_case_type_registry.py new file mode 100644 index 00000000..e5ace8db --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_case_type_registry.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python3 +"""Compile ``case_kinds.md`` into the canonical 137-row Stage 2 registry. + +This is an offline, deterministic compiler. It reads the three-column source +table, performs only Unicode/whitespace normalization and comma expansion, and +emits legally *unclassified* rows. It must not infer claim capability, +predicates, renderers, or any other legal content. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path +import tempfile +from typing import Any, Iterable +import unicodedata + + +EXPECTED_HEADERS = ("소송 대분류", "분쟁 유형", "사건 종류") +EXCLUDED_MAJOR_CATEGORIES = ("가사소송", "가족관계등록", "행정소송") +EXPECTED_ROW_COUNT = 137 +DEFAULT_LOGICAL_SOURCE_PATH = "Case_02_Comparison_Research/case_kinds.md" + + +class CatalogCompileError(ValueError): + """Controlled source/compile failure with a stable reason code.""" + + def __init__(self, code: str, detail: str) -> None: + super().__init__(f"{code}: {detail}") + self.code = code + self.detail = detail + + +def nfc(value: str) -> str: + return unicodedata.normalize("NFC", value.strip()) + + +def sha256_bytes(value: bytes) -> str: + return hashlib.sha256(value).hexdigest() + + +def canonical_json_bytes(value: Any) -> bytes: + return ( + json.dumps( + value, + ensure_ascii=False, + allow_nan=False, + indent=2, + separators=(",", ": "), + ) + + "\n" + ).encode("utf-8") + + +def _markdown_cells(line: str) -> tuple[str, str, str] | None: + stripped = line.strip() + if not (stripped.startswith("|") and stripped.endswith("|")): + return None + cells = tuple(nfc(cell) for cell in stripped[1:-1].split("|")) + if len(cells) != 3: + raise CatalogCompileError("CATALOG_TABLE_COLUMN_COUNT", repr(cells)) + return cells # type: ignore[return-value] + + +def _is_separator_row(cells: tuple[str, str, str]) -> bool: + return all(cell and set(cell) <= {"-", ":"} for cell in cells) + + +def parse_catalog(path: Path) -> list[dict[str, Any]]: + """Return the 137 expanded source rows in physical table order.""" + + try: + text = path.read_text(encoding="utf-8") + except UnicodeDecodeError as exc: + raise CatalogCompileError("CATALOG_UTF8_REQUIRED", str(exc)) from exc + if text.startswith("\ufeff"): + raise CatalogCompileError("CATALOG_UTF8_BOM_FORBIDDEN", path.as_posix()) + + rows = [cells for line in text.splitlines() if (cells := _markdown_cells(line))] + if len(rows) < 2 or rows[0] != EXPECTED_HEADERS or not _is_separator_row(rows[1]): + raise CatalogCompileError("CATALOG_TABLE_HEADER_MISMATCH", repr(rows[:2])) + + expanded: list[dict[str, Any]] = [] + source_table_rows = rows[2:] + for table_data_row, (major_category, dispute_type, case_kinds) in enumerate( + source_table_rows, 1 + ): + if major_category in EXCLUDED_MAJOR_CATEGORIES: + continue + if not major_category: + raise CatalogCompileError( + "CATALOG_MAJOR_CATEGORY_REQUIRED", f"table_data_row={table_data_row}" + ) + if case_kinds: + names = [nfc(value) for value in case_kinds.split(",")] + if not all(names): + raise CatalogCompileError( + "CATALOG_EMPTY_EXPANDED_NAME", f"table_data_row={table_data_row}" + ) + source_entry_kind = "CATALOG_NAMED_ENTRY" + else: + if not dispute_type: + raise CatalogCompileError( + "CATALOG_GENERIC_SOURCE_LABEL_REQUIRED", + f"table_data_row={table_data_row}", + ) + names = [dispute_type] + source_entry_kind = "GENERIC_SOURCE_ROW" + + for expanded_item_ordinal, canonical_name in enumerate(names, 1): + if nfc(canonical_name) != canonical_name: + raise CatalogCompileError("CATALOG_NFC_NORMALIZATION_FAILED", canonical_name) + expanded.append( + { + "canonical_name": canonical_name, + "source_ref": { + "table_data_row": table_data_row, + "expanded_item_ordinal": expanded_item_ordinal, + "major_category": major_category, + "dispute_type": dispute_type, + "source_entry_kind": source_entry_kind, + }, + } + ) + + names = [row["canonical_name"] for row in expanded] + if len(expanded) != EXPECTED_ROW_COUNT: + raise CatalogCompileError( + "CASE_TYPE_CATALOG_EXACT_137_REQUIRED", f"observed={len(expanded)}" + ) + if len(names) != len(set(names)): + duplicates = sorted({name for name in names if names.count(name) > 1}) + raise CatalogCompileError("CASE_TYPE_CANONICAL_NAME_DUPLICATE", repr(duplicates)) + return expanded + + +def compile_rows( + source: Path, + *, + logical_source_path: str = DEFAULT_LOGICAL_SOURCE_PATH, +) -> dict[str, Any]: + raw = source.read_bytes() + parsed = parse_catalog(source) + case_types: list[dict[str, Any]] = [] + for ordinal, source_row in enumerate(parsed, 1): + case_type_id = f"CT-{ordinal:03d}" + case_types.append( + { + "catalog_row_id": f"CAT-{ordinal:03d}", + "source_ordinal": ordinal, + "source_label": source_row["canonical_name"], + "case_type_id": case_type_id, + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": None, + "legal_review_receipt_sha256": None, + } + ) + + catalog_row_ids = [row["catalog_row_id"] for row in case_types] + if [row["case_type_id"] for row in case_types] != [ + f"CT-{ordinal:03d}" for ordinal in range(1, 138) + ]: + raise CatalogCompileError("CASE_TYPE_ID_SEQUENCE_DRIFT", repr(case_types)) + + return { + "schema_version": "stage2_case_type_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_137_V1", + "catalog_source_ref": logical_source_path, + "catalog_source_sha256": sha256_bytes(raw), + "catalog_row_ids": catalog_row_ids, + "rows": case_types, + "claim_capable_case_type_ids": [], + "pending_legal_classification_case_type_ids": [ + row["case_type_id"] for row in case_types + ], + "fuzzy_match_allowed": False, + "catch_all_fallback_allowed": False, + "registry_status": "PENDING_LEGAL_CLASSIFICATION", + } + + +def _atomic_write(path: Path, payload: bytes) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + temporary_name: str | None = None + try: + with tempfile.NamedTemporaryFile( + mode="wb", + dir=path.parent, + prefix=f".{path.name}.", + suffix=".tmp", + delete=False, + ) as handle: + temporary_name = handle.name + handle.write(payload) + handle.flush() + os.fsync(handle.fileno()) + os.replace(temporary_name, path) + temporary_name = None + finally: + if temporary_name is not None: + try: + Path(temporary_name).unlink() + except FileNotFoundError: + pass + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Compile the canonical 137-kind civil-case catalog" + ) + parser.add_argument("source", type=Path) + parser.add_argument("output", type=Path) + parser.add_argument( + "--source-logical-path", + default=DEFAULT_LOGICAL_SOURCE_PATH, + help="version-free path recorded in the emitted registry", + ) + parser.add_argument( + "--check", + action="store_true", + help="do not write; require output bytes to equal the deterministic projection", + ) + return parser + + +def main(argv: Iterable[str] | None = None) -> int: + args = _parser().parse_args(list(argv) if argv is not None else None) + try: + payload = canonical_json_bytes( + compile_rows(args.source, logical_source_path=args.source_logical_path) + ) + if args.check: + if not args.output.is_file() or args.output.read_bytes() != payload: + raise CatalogCompileError( + "CASE_TYPE_REGISTRY_PARITY_DRIFT", args.output.as_posix() + ) + result = {"status": "PARITY_PASS", "sha256": sha256_bytes(payload)} + else: + _atomic_write(args.output, payload) + result = { + "status": "COMPILED", + "row_count": EXPECTED_ROW_COUNT, + "source_sha256": sha256_bytes(args.source.read_bytes()), + "output_sha256": sha256_bytes(payload), + } + except (CatalogCompileError, OSError) as exc: + result = { + "status": "COMPILE_FAILED", + "reason_code": getattr(exc, "code", type(exc).__name__), + "detail": getattr(exc, "detail", str(exc)), + } + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + return 2 + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_case_type_registry.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_case_type_registry.txt new file mode 100644 index 00000000..e5ace8db --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_case_type_registry.txt @@ -0,0 +1,267 @@ +#!/usr/bin/env python3 +"""Compile ``case_kinds.md`` into the canonical 137-row Stage 2 registry. + +This is an offline, deterministic compiler. It reads the three-column source +table, performs only Unicode/whitespace normalization and comma expansion, and +emits legally *unclassified* rows. It must not infer claim capability, +predicates, renderers, or any other legal content. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path +import tempfile +from typing import Any, Iterable +import unicodedata + + +EXPECTED_HEADERS = ("소송 대분류", "분쟁 유형", "사건 종류") +EXCLUDED_MAJOR_CATEGORIES = ("가사소송", "가족관계등록", "행정소송") +EXPECTED_ROW_COUNT = 137 +DEFAULT_LOGICAL_SOURCE_PATH = "Case_02_Comparison_Research/case_kinds.md" + + +class CatalogCompileError(ValueError): + """Controlled source/compile failure with a stable reason code.""" + + def __init__(self, code: str, detail: str) -> None: + super().__init__(f"{code}: {detail}") + self.code = code + self.detail = detail + + +def nfc(value: str) -> str: + return unicodedata.normalize("NFC", value.strip()) + + +def sha256_bytes(value: bytes) -> str: + return hashlib.sha256(value).hexdigest() + + +def canonical_json_bytes(value: Any) -> bytes: + return ( + json.dumps( + value, + ensure_ascii=False, + allow_nan=False, + indent=2, + separators=(",", ": "), + ) + + "\n" + ).encode("utf-8") + + +def _markdown_cells(line: str) -> tuple[str, str, str] | None: + stripped = line.strip() + if not (stripped.startswith("|") and stripped.endswith("|")): + return None + cells = tuple(nfc(cell) for cell in stripped[1:-1].split("|")) + if len(cells) != 3: + raise CatalogCompileError("CATALOG_TABLE_COLUMN_COUNT", repr(cells)) + return cells # type: ignore[return-value] + + +def _is_separator_row(cells: tuple[str, str, str]) -> bool: + return all(cell and set(cell) <= {"-", ":"} for cell in cells) + + +def parse_catalog(path: Path) -> list[dict[str, Any]]: + """Return the 137 expanded source rows in physical table order.""" + + try: + text = path.read_text(encoding="utf-8") + except UnicodeDecodeError as exc: + raise CatalogCompileError("CATALOG_UTF8_REQUIRED", str(exc)) from exc + if text.startswith("\ufeff"): + raise CatalogCompileError("CATALOG_UTF8_BOM_FORBIDDEN", path.as_posix()) + + rows = [cells for line in text.splitlines() if (cells := _markdown_cells(line))] + if len(rows) < 2 or rows[0] != EXPECTED_HEADERS or not _is_separator_row(rows[1]): + raise CatalogCompileError("CATALOG_TABLE_HEADER_MISMATCH", repr(rows[:2])) + + expanded: list[dict[str, Any]] = [] + source_table_rows = rows[2:] + for table_data_row, (major_category, dispute_type, case_kinds) in enumerate( + source_table_rows, 1 + ): + if major_category in EXCLUDED_MAJOR_CATEGORIES: + continue + if not major_category: + raise CatalogCompileError( + "CATALOG_MAJOR_CATEGORY_REQUIRED", f"table_data_row={table_data_row}" + ) + if case_kinds: + names = [nfc(value) for value in case_kinds.split(",")] + if not all(names): + raise CatalogCompileError( + "CATALOG_EMPTY_EXPANDED_NAME", f"table_data_row={table_data_row}" + ) + source_entry_kind = "CATALOG_NAMED_ENTRY" + else: + if not dispute_type: + raise CatalogCompileError( + "CATALOG_GENERIC_SOURCE_LABEL_REQUIRED", + f"table_data_row={table_data_row}", + ) + names = [dispute_type] + source_entry_kind = "GENERIC_SOURCE_ROW" + + for expanded_item_ordinal, canonical_name in enumerate(names, 1): + if nfc(canonical_name) != canonical_name: + raise CatalogCompileError("CATALOG_NFC_NORMALIZATION_FAILED", canonical_name) + expanded.append( + { + "canonical_name": canonical_name, + "source_ref": { + "table_data_row": table_data_row, + "expanded_item_ordinal": expanded_item_ordinal, + "major_category": major_category, + "dispute_type": dispute_type, + "source_entry_kind": source_entry_kind, + }, + } + ) + + names = [row["canonical_name"] for row in expanded] + if len(expanded) != EXPECTED_ROW_COUNT: + raise CatalogCompileError( + "CASE_TYPE_CATALOG_EXACT_137_REQUIRED", f"observed={len(expanded)}" + ) + if len(names) != len(set(names)): + duplicates = sorted({name for name in names if names.count(name) > 1}) + raise CatalogCompileError("CASE_TYPE_CANONICAL_NAME_DUPLICATE", repr(duplicates)) + return expanded + + +def compile_rows( + source: Path, + *, + logical_source_path: str = DEFAULT_LOGICAL_SOURCE_PATH, +) -> dict[str, Any]: + raw = source.read_bytes() + parsed = parse_catalog(source) + case_types: list[dict[str, Any]] = [] + for ordinal, source_row in enumerate(parsed, 1): + case_type_id = f"CT-{ordinal:03d}" + case_types.append( + { + "catalog_row_id": f"CAT-{ordinal:03d}", + "source_ordinal": ordinal, + "source_label": source_row["canonical_name"], + "case_type_id": case_type_id, + "row_kind": "PENDING_LEGAL_CLASSIFICATION", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "case_type_predicates": [], + "companion_case_type_ids": [], + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "legal_review_receipt_ref": None, + "legal_review_receipt_sha256": None, + } + ) + + catalog_row_ids = [row["catalog_row_id"] for row in case_types] + if [row["case_type_id"] for row in case_types] != [ + f"CT-{ordinal:03d}" for ordinal in range(1, 138) + ]: + raise CatalogCompileError("CASE_TYPE_ID_SEQUENCE_DRIFT", repr(case_types)) + + return { + "schema_version": "stage2_case_type_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_137_V1", + "catalog_source_ref": logical_source_path, + "catalog_source_sha256": sha256_bytes(raw), + "catalog_row_ids": catalog_row_ids, + "rows": case_types, + "claim_capable_case_type_ids": [], + "pending_legal_classification_case_type_ids": [ + row["case_type_id"] for row in case_types + ], + "fuzzy_match_allowed": False, + "catch_all_fallback_allowed": False, + "registry_status": "PENDING_LEGAL_CLASSIFICATION", + } + + +def _atomic_write(path: Path, payload: bytes) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + temporary_name: str | None = None + try: + with tempfile.NamedTemporaryFile( + mode="wb", + dir=path.parent, + prefix=f".{path.name}.", + suffix=".tmp", + delete=False, + ) as handle: + temporary_name = handle.name + handle.write(payload) + handle.flush() + os.fsync(handle.fileno()) + os.replace(temporary_name, path) + temporary_name = None + finally: + if temporary_name is not None: + try: + Path(temporary_name).unlink() + except FileNotFoundError: + pass + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Compile the canonical 137-kind civil-case catalog" + ) + parser.add_argument("source", type=Path) + parser.add_argument("output", type=Path) + parser.add_argument( + "--source-logical-path", + default=DEFAULT_LOGICAL_SOURCE_PATH, + help="version-free path recorded in the emitted registry", + ) + parser.add_argument( + "--check", + action="store_true", + help="do not write; require output bytes to equal the deterministic projection", + ) + return parser + + +def main(argv: Iterable[str] | None = None) -> int: + args = _parser().parse_args(list(argv) if argv is not None else None) + try: + payload = canonical_json_bytes( + compile_rows(args.source, logical_source_path=args.source_logical_path) + ) + if args.check: + if not args.output.is_file() or args.output.read_bytes() != payload: + raise CatalogCompileError( + "CASE_TYPE_REGISTRY_PARITY_DRIFT", args.output.as_posix() + ) + result = {"status": "PARITY_PASS", "sha256": sha256_bytes(payload)} + else: + _atomic_write(args.output, payload) + result = { + "status": "COMPILED", + "row_count": EXPECTED_ROW_COUNT, + "source_sha256": sha256_bytes(args.source.read_bytes()), + "output_sha256": sha256_bytes(payload), + } + except (CatalogCompileError, OSError) as exc: + result = { + "status": "COMPILE_FAILED", + "reason_code": getattr(exc, "code", type(exc).__name__), + "detail": getattr(exc, "detail", str(exc)), + } + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + return 2 + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_relief_rule_projection.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_relief_rule_projection.py new file mode 100644 index 00000000..5fdfd55c --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_relief_rule_projection.py @@ -0,0 +1,1006 @@ +#!/usr/bin/env python3 +"""Compile 137 reviewed relief-rule Markdown files into executable branches. + +The compiler never derives legal rules from prose. A document contributes an +executable branch only when its strict YAML front matter declares the branch +``APPROVED`` and every predicate, foreign key, source hash, and Korean-lawyer +approval receipt is independently verifiable. Incomplete documents remain +explicitly pending; a false ``APPROVED`` declaration is a hard failure rather +than a silent downgrade. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path, PurePosixPath +import re +import tempfile +from typing import Any, Iterable, Mapping, Sequence + +try: + import yaml +except ImportError: # pragma: no cover - incomplete offline build host + yaml = None # type: ignore[assignment] + + +DEPLOYMENT_ROOT = Path(__file__).resolve().parents[1] +EXPECTED_DOCUMENT_COUNT = 137 +METADATA_SCHEMA = "stage2_relief_rule_markdown_metadata.v1" +RECEIPT_SCHEMA = "stage2_relief_rule_legal_review_receipt.v1" +SHA256_RE = re.compile(r"^[a-f0-9]{64}$") +CASE_TYPE_RE = re.compile(r"^CT-[0-9]{3}$") +IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$") +LEGAL_REVIEW_PATH_RE = re.compile(r"^legal_review/[A-Za-z0-9][A-Za-z0-9._-]{0,191}\.json$") +ALLOWED_PREDICATE_FIELDS = frozenset( + { + "claim_family", + "legal_effect", + "performance_kind", + "object_kind", + "party_role_pattern", + "source_obligation_kind", + "remedy_mode", + "registry_action", + "procedural_posture", + "special_statute_tags", + } +) +ALLOWED_PREDICATE_OPERATORS = frozenset({"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"}) +BRANCH_REQUIRED_KEYS = frozenset( + { + "sub_rule_id", + "predicate", + "structured_relief_rule_id", + "renderer_ids", + "required_slot_ids", + "corpus_scope_id", + "element_set_id", + "ce13_branch_id", + "party_set_rule_id", + "review_policy_id", + "authority_refs", + "review_status", + "legal_review_receipt_ref", + "legal_review_receipt_sha256", + } +) +METADATA_REQUIRED_KEYS = frozenset( + {"schema_version", "case_type_id", "document_status", "rule_branches"} +) + + +class RuleCompileError(ValueError): + """Controlled compiler failure with a stable reason code.""" + + def __init__(self, code: str, detail: str) -> None: + super().__init__(f"{code}: {detail}") + self.code = code + self.detail = detail + + +if yaml is not None: + + class UniqueKeySafeLoader(yaml.SafeLoader): + """SafeLoader that rejects duplicate keys at every mapping depth.""" + + def construct_mapping(self, node: Any, deep: bool = False) -> dict[Any, Any]: + if not isinstance(node, yaml.MappingNode): + raise RuleCompileError("YAML_MAPPING_REQUIRED", repr(node)[:200]) + self.flatten_mapping(node) + result: dict[Any, Any] = {} + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + try: + duplicate = key in result + except TypeError as exc: + raise RuleCompileError("YAML_UNHASHABLE_KEY", repr(key)[:200]) from exc + if duplicate: + mark = getattr(key_node, "start_mark", None) + line = mark.line + 1 if mark is not None else "?" + raise RuleCompileError("YAML_DUPLICATE_KEY", f"{key!r}@line={line}") + result[key] = self.construct_object(value_node, deep=deep) + return result + +else: # pragma: no cover + + class UniqueKeySafeLoader: # type: ignore[no-redef] + pass + + +def sha256_bytes(value: bytes) -> str: + return hashlib.sha256(value).hexdigest() + + +def canonical_json_bytes(value: Any) -> bytes: + return ( + json.dumps( + value, + ensure_ascii=False, + allow_nan=False, + indent=2, + separators=(",", ": "), + ) + + "\n" + ).encode("utf-8") + + +def _strict_yaml(text: str, *, source: str) -> dict[str, Any]: + if yaml is None: + raise RuleCompileError("PYYAML_REQUIRED", "install PyYAML on offline build host") + try: + loaded = yaml.load(text, Loader=UniqueKeySafeLoader) + except RuleCompileError: + raise + except yaml.YAMLError as exc: + raise RuleCompileError("YAML_INVALID", f"{source}: {exc}") from exc + if not isinstance(loaded, dict): + raise RuleCompileError("YAML_ROOT_OBJECT_REQUIRED", source) + return loaded + + +def _strict_json(raw: bytes, *, source: str) -> dict[str, Any]: + def reject_duplicate(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise RuleCompileError("JSON_DUPLICATE_KEY", f"{source}:{key}") + result[key] = value + return result + + try: + loaded = json.loads( + raw.decode("utf-8"), + object_pairs_hook=reject_duplicate, + parse_constant=lambda value: (_ for _ in ()).throw( + RuleCompileError("JSON_NONFINITE_NUMBER", f"{source}:{value}") + ), + ) + except RuleCompileError: + raise + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise RuleCompileError("JSON_INVALID", f"{source}: {exc}") from exc + if not isinstance(loaded, dict): + raise RuleCompileError("JSON_ROOT_OBJECT_REQUIRED", source) + return loaded + + +def _require_exact_keys(value: Mapping[str, Any], expected: set[str] | frozenset[str], code: str) -> None: + observed = set(value) + if observed != set(expected): + raise RuleCompileError( + code, + json.dumps( + {"missing": sorted(set(expected) - observed), "extra": sorted(observed - set(expected))}, + ensure_ascii=False, + sort_keys=True, + ), + ) + + +def _safe_relative_path(value: Any, code: str) -> str: + if not isinstance(value, str) or not value or "\\" in value or "\x00" in value: + raise RuleCompileError(code, repr(value)) + path = PurePosixPath(value) + if path.is_absolute() or any(part in {"", ".", ".."} for part in path.parts): + raise RuleCompileError(code, value) + return path.as_posix() + + +def _safe_legal_review_path(value: Any) -> str: + path = _safe_relative_path(value, "LEGAL_REVIEW_RECEIPT_REF_INVALID") + if LEGAL_REVIEW_PATH_RE.fullmatch(path) is None: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_PATH_FAMILY", path) + return path + + +def _require_identifier(value: Any, code: str) -> str: + if not isinstance(value, str) or not IDENTIFIER_RE.fullmatch(value): + raise RuleCompileError(code, repr(value)) + return value + + +def _require_sha256(value: Any, code: str) -> str: + if not isinstance(value, str) or not SHA256_RE.fullmatch(value): + raise RuleCompileError(code, repr(value)) + return value + + +def _front_matter(path: Path, raw: bytes) -> tuple[dict[str, Any], bytes]: + try: + text = raw.decode("utf-8") + except UnicodeDecodeError as exc: + raise RuleCompileError("MARKDOWN_UTF8_REQUIRED", f"{path}: {exc}") from exc + if text.startswith("\ufeff"): + raise RuleCompileError("MARKDOWN_UTF8_BOM_FORBIDDEN", path.as_posix()) + lines = text.splitlines(keepends=True) + if not lines or lines[0].strip() != "---": + raise RuleCompileError("RULE_METADATA_FRONT_MATTER_REQUIRED", path.as_posix()) + try: + closing = next(index for index, line in enumerate(lines[1:], 1) if line.strip() == "---") + except StopIteration as exc: + raise RuleCompileError("RULE_METADATA_FRONT_MATTER_UNCLOSED", path.as_posix()) from exc + metadata_text = "".join(lines[1:closing]) + body_bytes = "".join(lines[closing + 1 :]).encode("utf-8") + return _strict_yaml(metadata_text, source=path.as_posix()), body_bytes + + +def _review_scope_sha256(metadata: Mapping[str, Any], body_bytes: bytes) -> str: + """Bind legal metadata/body without creating a receipt-hash cycle. + + The receipt SHA fields are the only removed values. Receipt paths, + predicates, legal slots, foreign keys, statuses, and the exact Markdown body + digest remain in scope, so a receipt cannot be replayed after legal content + changes. + """ + + def strip_receipt_hash(value: Any) -> Any: + if isinstance(value, dict): + return { + key: strip_receipt_hash(child) + for key, child in value.items() + if key != "legal_review_receipt_sha256" + } + if isinstance(value, list): + return [strip_receipt_hash(child) for child in value] + return value + + scope = { + "metadata_without_receipt_sha256": strip_receipt_hash(dict(metadata)), + "markdown_body_sha256": sha256_bytes(body_bytes), + } + return sha256_bytes(canonical_json_bytes(scope)) + + +def _collect_values(value: Any, keys: frozenset[str]) -> set[str]: + found: set[str] = set() + if isinstance(value, dict): + for key, child in value.items(): + if key in keys and isinstance(child, str) and child: + found.add(child) + found.update(_collect_values(child, keys)) + elif isinstance(value, list): + for child in value: + found.update(_collect_values(child, keys)) + return found + + +def _record_is_approved(value: Mapping[str, Any]) -> bool: + """Return true only when every present admission marker is affirmative.""" + + markers: list[bool] = [] + for key in ( + "status", + "review_status", + "legal_review_status", + "legal_classification_status", + "legal_content_status", + "implementation_status", + "semantic_review_status", + ): + if key in value: + markers.append(value.get(key) == "APPROVED") + if "execution_eligible" in value: + markers.append(value.get("execution_eligible") is True) + return bool(markers) and all(markers) + + +def _collect_approved_values(value: Any, keys: frozenset[str], *, inherited: bool = False) -> set[str]: + found: set[str] = set() + if isinstance(value, dict): + admitted = inherited or _record_is_approved(value) + for key, child in value.items(): + if admitted and key in keys and isinstance(child, str) and child: + found.add(child) + found.update(_collect_approved_values(child, keys, inherited=admitted)) + elif isinstance(value, list): + for child in value: + found.update(_collect_approved_values(child, keys, inherited=inherited)) + return found + + +def _load_registry(path: Path) -> dict[str, Any]: + if not path.is_file() or path.is_symlink(): + raise RuleCompileError("FK_REGISTRY_UNAVAILABLE", path.as_posix()) + raw = path.read_bytes() + try: + return _strict_json(raw, source=path.as_posix()) + except RuleCompileError as json_error: + try: + return _strict_yaml(raw.decode("utf-8"), source=path.as_posix()) + except (UnicodeDecodeError, RuleCompileError) as yaml_error: + raise RuleCompileError( + "FK_REGISTRY_PARSE_FAILED", f"{path}: json={json_error}; yaml={yaml_error}" + ) from yaml_error + + +def _logical_asset_path(path: Path, deployment_root: Path) -> str: + try: + return path.resolve(strict=True).relative_to(deployment_root.resolve(strict=True)).as_posix() + except ValueError as exc: + raise RuleCompileError("FK_ASSET_OUTSIDE_DEPLOYMENT_ROOT", path.as_posix()) from exc + + +def _add_index_values( + index: dict[str, list[dict[str, str]]], + ids: Sequence[str] | set[str], + path: Path, + deployment_root: Path, +) -> None: + raw = path.read_bytes() + ref = { + "path": _logical_asset_path(path, deployment_root), + "sha256": sha256_bytes(raw), + } + for value in ids: + refs = index.setdefault(value, []) + if ref not in refs: + refs.append(ref) + + +def build_foreign_key_indexes(deployment_root: Path) -> dict[str, dict[str, list[dict[str, str]]]]: + """Build exact ID->physical-asset/hash indexes from canonical registries.""" + + indexes: dict[str, dict[str, list[dict[str, str]]]] = { + "case_type": {}, + "renderer": {}, + "corpus_scope": {}, + "element_set": {}, + "ce_13_branch": {}, + "party_set_rule": {}, + "review_policy": {}, + "authority": {}, + } + + case_path = deployment_root / "manifest/case_type_registry.yml" + case_doc = _load_registry(case_path) + if ( + case_doc.get("schema_version") != "stage2_case_type_registry.v1" + or case_doc.get("registry_id") != "STAGE2_CASE_TYPE_137_V1" + ): + raise RuleCompileError("CASE_TYPE_REGISTRY_IDENTITY_MISMATCH", repr(case_doc)[:200]) + case_rows = case_doc.get("rows") + if not isinstance(case_rows, list) or len(case_rows) != EXPECTED_DOCUMENT_COUNT: + raise RuleCompileError("CASE_TYPE_REGISTRY_EXACT_137_REQUIRED", repr(case_rows)[:200]) + admitted_case_type_ids = { + str(row.get("case_type_id")) + for row in case_rows + if isinstance(row, dict) + and CASE_TYPE_RE.fullmatch(str(row.get("case_type_id", ""))) + and row.get("row_kind") in {"CLAIM", "GENERIC"} + and row.get("claim_capable_disposition") == "CLAIM_CAPABLE" + and row.get("legal_classification_status") == "APPROVED" + and row.get("legal_content_status") == "APPROVED" + and isinstance(row.get("legal_review_receipt_ref"), str) + and isinstance(row.get("legal_review_receipt_sha256"), str) + } + _add_index_values(indexes["case_type"], admitted_case_type_ids, case_path, deployment_root) + + renderer_paths = sorted((deployment_root / "renderers").glob("R*.yml")) + if not renderer_paths: + raise RuleCompileError("RENDERER_REGISTRY_UNAVAILABLE", "renderers/R*.yml") + for path in renderer_paths: + document = _load_registry(path) + renderer_id = document.get("renderer_id") + if isinstance(renderer_id, str) and renderer_id and _record_is_approved(document): + _add_index_values(indexes["renderer"], {renderer_id}, path, deployment_root) + + scope_path = deployment_root / "registry/corpus/requirement_fact_scope.yml" + scope_document = _load_registry(scope_path) + if ( + scope_document.get("schema_version") + != "stage2_requirement_fact_scope_registry.v1" + or scope_document.get("registry_id") != "STAGE2_REQUIREMENT_FACT_SCOPE_V1" + or scope_document.get("registry_status") + not in {"PENDING_LEGAL_CONTENT", "PARTIALLY_APPROVED", "APPROVED"} + ): + raise RuleCompileError( + "REQUIREMENT_FACT_SCOPE_REGISTRY_IDENTITY_MISMATCH", + repr(scope_document)[:200], + ) + scope_rows = scope_document.get("rows") + if not isinstance(scope_rows, list): + raise RuleCompileError("REQUIREMENT_FACT_SCOPE_ROWS_REQUIRED", repr(scope_rows)[:200]) + for row in scope_rows: + if not isinstance(row, dict) or row.get("approval_status") != "APPROVED": + continue + if ( + not isinstance(row.get("legal_review_receipt_ref"), str) + or not isinstance(row.get("legal_review_receipt_sha256"), str) + ): + raise RuleCompileError( + "APPROVED_REQUIREMENT_FACT_SCOPE_RECEIPT_REQUIRED", + repr(row.get("corpus_scope_id")), + ) + corpus_scope_id = row.get("corpus_scope_id") + element_set_id = row.get("element_set_id") + if isinstance(corpus_scope_id, str) and corpus_scope_id: + _add_index_values( + indexes["corpus_scope"], {corpus_scope_id}, scope_path, deployment_root + ) + if isinstance(element_set_id, str) and element_set_id: + _add_index_values( + indexes["element_set"], {element_set_id}, scope_path, deployment_root + ) + + specs = ( + ( + "registry/calculations/CE-13_court_value_cost.yml", + (("ce_13_branch", frozenset({"ce_13_branch_id", "court_value_rule_id", "branch_id", "formula_id"})),), + ), + ( + "registry/party/party_set_rules.yml", + (("party_set_rule", frozenset({"party_set_rule_id"})),), + ), + ( + "registry/review/review_policy_registry.yml", + (("review_policy", frozenset({"review_policy_id", "policy_id"})),), + ), + ( + "registry/authority/authority_registry.yml", + (("authority", frozenset({"authority_id", "authority_ref_id", "proposition_id"})),), + ), + ) + for relative_path, targets in specs: + path = deployment_root / relative_path + document = _load_registry(path) + for index_name, keys in targets: + _add_index_values( + indexes[index_name], + _collect_approved_values(document, keys), + path, + deployment_root, + ) + return indexes + + +def _exact_fk( + indexes: Mapping[str, Mapping[str, list[dict[str, str]]]], + index_name: str, + value: str, +) -> dict[str, str]: + matches = indexes.get(index_name, {}).get(value, []) + if len(matches) != 1: + raise RuleCompileError( + "RULE_BRANCH_FK_EXACT_ONE_REQUIRED", + f"kind={index_name} id={value!r} matches={len(matches)}", + ) + return {"asset_id": value, **matches[0]} + + +def _predicate( + value: Any, + *, + branch_receipt_ref: str, + branch_receipt_sha256: str, +) -> tuple[str, list[dict[str, Any]], dict[str, Any]]: + if not isinstance(value, dict): + raise RuleCompileError("RULE_BRANCH_PREDICATE_OBJECT_REQUIRED", repr(value)) + required = { + "predicate_id", + "clauses", + "priority", + "legal_review_status", + "legal_review_receipt_ref", + "legal_review_receipt_sha256", + } + _require_exact_keys(value, required, "RULE_BRANCH_PREDICATE_KEYS") + predicate_id = _require_identifier(value.get("predicate_id"), "PREDICATE_ID_INVALID") + clauses = value.get("clauses") + if not isinstance(clauses, list) or not clauses: + raise RuleCompileError("RULE_BRANCH_PREDICATE_CLAUSES_REQUIRED", predicate_id) + normalized: list[dict[str, Any]] = [] + for ordinal, clause in enumerate(clauses, 1): + if not isinstance(clause, dict): + raise RuleCompileError("PREDICATE_CLAUSE_OBJECT_REQUIRED", f"{predicate_id}:{ordinal}") + _require_exact_keys(clause, {"field", "operator", "value"}, "PREDICATE_CLAUSE_KEYS") + field = clause.get("field") + operator = clause.get("operator") + operand = clause.get("value") + if field not in ALLOWED_PREDICATE_FIELDS or operator not in ALLOWED_PREDICATE_OPERATORS: + raise RuleCompileError("PREDICATE_CLAUSE_CLOSED_VOCABULARY", repr(clause)) + if operator == "EXISTS" and not isinstance(operand, bool): + raise RuleCompileError("PREDICATE_EXISTS_BOOLEAN_REQUIRED", repr(clause)) + if operator in {"IN", "ALL_OF", "NONE_OF"} and ( + not isinstance(operand, list) + or not operand + or not all(isinstance(item, str) and item for item in operand) + or len(operand) != len(set(operand)) + ): + raise RuleCompileError("PREDICATE_SET_VALUE_INVALID", repr(clause)) + if operator == "EQ" and not isinstance(operand, (str, bool)): + raise RuleCompileError("PREDICATE_EQ_VALUE_INVALID", repr(clause)) + normalized.append({"field": field, "operator": operator, "value": operand}) + priority = value.get("priority") + if not isinstance(priority, int) or isinstance(priority, bool) or priority < 0: + raise RuleCompileError("PREDICATE_PRIORITY_INVALID", repr(priority)) + if value.get("legal_review_status") != "APPROVED": + raise RuleCompileError("PREDICATE_LAWYER_APPROVAL_REQUIRED", predicate_id) + if value.get("legal_review_receipt_ref") != branch_receipt_ref or value.get( + "legal_review_receipt_sha256" + ) != branch_receipt_sha256: + raise RuleCompileError("PREDICATE_RECEIPT_MUST_EQUAL_BRANCH_RECEIPT", predicate_id) + closed = { + "predicate_id": predicate_id, + "clauses": normalized, + "priority": priority, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": branch_receipt_ref, + "legal_review_receipt_sha256": branch_receipt_sha256, + } + return predicate_id, normalized, closed + + +def _verify_lawyer_receipt( + deployment_root: Path, + *, + receipt_ref: str, + receipt_sha256: str, + rule_branch_key: str, + review_scope_sha256: str, +) -> None: + receipt_path = deployment_root / receipt_ref + try: + receipt_path.resolve(strict=True).relative_to(deployment_root.resolve(strict=True)) + except (FileNotFoundError, ValueError) as exc: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_UNAVAILABLE", receipt_ref) from exc + if receipt_path.is_symlink() or not receipt_path.is_file(): + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_UNAVAILABLE", receipt_ref) + raw = receipt_path.read_bytes() + if sha256_bytes(raw) != receipt_sha256: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_HASH_MISMATCH", receipt_ref) + receipt = _strict_json(raw, source=receipt_ref) + required = { + "schema_version", + "status", + "reviewer_role", + "signed_by", + "signature_ref", + "signature_sha256", + "review_scope_sha256", + "approved_rule_branch_keys", + } + _require_exact_keys(receipt, required, "LEGAL_REVIEW_RECEIPT_KEYS") + if receipt.get("schema_version") != RECEIPT_SCHEMA or receipt.get("status") != "APPROVED": + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_NOT_APPROVED", receipt_ref) + if receipt.get("reviewer_role") not in {"KOREAN_LAWYER", "KOREAN_ATTORNEY"}: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_ROLE_INVALID", receipt_ref) + if not isinstance(receipt.get("signed_by"), str) or not receipt["signed_by"]: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_SIGNER_REQUIRED", receipt_ref) + signature_ref = _safe_relative_path( + receipt.get("signature_ref"), "LEGAL_REVIEW_SIGNATURE_REF_INVALID" + ) + signature_sha256 = _require_sha256( + receipt.get("signature_sha256"), "LEGAL_REVIEW_SIGNATURE_SHA256_INVALID" + ) + signature_path = deployment_root / signature_ref + try: + signature_path.resolve(strict=True).relative_to(deployment_root.resolve(strict=True)) + except (FileNotFoundError, ValueError) as exc: + raise RuleCompileError("LEGAL_REVIEW_SIGNATURE_UNAVAILABLE", signature_ref) from exc + if signature_path.is_symlink() or not signature_path.is_file(): + raise RuleCompileError("LEGAL_REVIEW_SIGNATURE_UNAVAILABLE", signature_ref) + if sha256_bytes(signature_path.read_bytes()) != signature_sha256: + raise RuleCompileError("LEGAL_REVIEW_SIGNATURE_HASH_MISMATCH", signature_ref) + if receipt.get("review_scope_sha256") != review_scope_sha256: + raise RuleCompileError("LEGAL_REVIEW_SCOPE_HASH_MISMATCH", receipt_ref) + branch_keys = receipt.get("approved_rule_branch_keys") + if ( + not isinstance(branch_keys, list) + or not all(isinstance(item, str) and item for item in branch_keys) + or len(branch_keys) != len(set(branch_keys)) + or rule_branch_key not in branch_keys + ): + raise RuleCompileError("LEGAL_REVIEW_BRANCH_SCOPE_MISMATCH", rule_branch_key) + + +def compile_document( + path: Path, + *, + deployment_root: Path = DEPLOYMENT_ROOT, + foreign_key_indexes: Mapping[str, Mapping[str, list[dict[str, str]]]] | None = None, + logical_rule_root: str = "rules/relief", +) -> dict[str, Any]: + raw = path.read_bytes() + markdown_sha256 = sha256_bytes(raw) + metadata, body_bytes = _front_matter(path, raw) + _require_exact_keys(metadata, METADATA_REQUIRED_KEYS, "RULE_METADATA_KEYS") + if metadata.get("schema_version") != METADATA_SCHEMA: + raise RuleCompileError("RULE_METADATA_SCHEMA_MISMATCH", repr(metadata.get("schema_version"))) + case_type_id = metadata.get("case_type_id") + if not isinstance(case_type_id, str) or not CASE_TYPE_RE.fullmatch(case_type_id): + raise RuleCompileError("CASE_TYPE_ID_INVALID", repr(case_type_id)) + if path.name != f"{case_type_id}.md": + raise RuleCompileError("RULE_DOCUMENT_FILENAME_ID_MISMATCH", path.as_posix()) + status = metadata.get("document_status") + if status not in {"PENDING_LEGAL_CONTENT", "APPROVED"}: + raise RuleCompileError("RULE_DOCUMENT_STATUS_INVALID", repr(status)) + raw_branches = metadata.get("rule_branches") + if not isinstance(raw_branches, list): + raise RuleCompileError("RULE_BRANCHES_ARRAY_REQUIRED", case_type_id) + if status == "PENDING_LEGAL_CONTENT": + if raw_branches: + raise RuleCompileError("PENDING_DOCUMENT_BRANCHES_MUST_BE_EMPTY", case_type_id) + return { + "case_type_id": case_type_id, + "document_status": status, + "markdown_source_path": f"{logical_rule_root}/{path.name}", + "markdown_source_sha256": markdown_sha256, + "rule_branches": [], + } + if not raw_branches: + raise RuleCompileError("APPROVED_DOCUMENT_BRANCH_REQUIRED", case_type_id) + + indexes = foreign_key_indexes or build_foreign_key_indexes(deployment_root) + case_type_ref = _exact_fk(indexes, "case_type", case_type_id) + review_scope_sha256 = _review_scope_sha256(metadata, body_bytes) + compiled: list[dict[str, Any]] = [] + seen_sub_rules: set[str] = set() + seen_predicates: set[str] = set() + for ordinal, value in enumerate(raw_branches, 1): + if not isinstance(value, dict): + raise RuleCompileError("RULE_BRANCH_OBJECT_REQUIRED", f"{case_type_id}:{ordinal}") + _require_exact_keys(value, BRANCH_REQUIRED_KEYS, "RULE_BRANCH_KEYS") + sub_rule_id = _require_identifier(value.get("sub_rule_id"), "SUB_RULE_ID_INVALID") + if sub_rule_id in seen_sub_rules: + raise RuleCompileError("SUB_RULE_ID_DUPLICATE_IN_DOCUMENT", sub_rule_id) + seen_sub_rules.add(sub_rule_id) + rule_branch_key = f"{case_type_id}::{sub_rule_id}" + if value.get("review_status") != "APPROVED": + raise RuleCompileError("RULE_BRANCH_LAWYER_APPROVAL_REQUIRED", rule_branch_key) + receipt_ref = _safe_legal_review_path(value.get("legal_review_receipt_ref")) + receipt_sha256 = _require_sha256( + value.get("legal_review_receipt_sha256"), "LEGAL_REVIEW_RECEIPT_SHA256_INVALID" + ) + _verify_lawyer_receipt( + deployment_root, + receipt_ref=receipt_ref, + receipt_sha256=receipt_sha256, + rule_branch_key=rule_branch_key, + review_scope_sha256=review_scope_sha256, + ) + predicate_id, clauses, closed_predicate = _predicate( + value.get("predicate"), + branch_receipt_ref=receipt_ref, + branch_receipt_sha256=receipt_sha256, + ) + if predicate_id in seen_predicates: + raise RuleCompileError("PREDICATE_ID_DUPLICATE_IN_DOCUMENT", predicate_id) + seen_predicates.add(predicate_id) + + structured_relief_rule_id = _require_identifier( + value.get("structured_relief_rule_id"), "STRUCTURED_RELIEF_RULE_ID_INVALID" + ) + slots = value.get("required_slot_ids") + if not isinstance(slots, list) or not slots or not all( + isinstance(item, str) and item for item in slots + ) or len(slots) != len(set(slots)): + raise RuleCompileError("REQUIRED_SLOT_IDS_INVALID", rule_branch_key) + renderer_ids = value.get("renderer_ids") + if not isinstance(renderer_ids, list) or not renderer_ids or not all( + isinstance(item, str) and item for item in renderer_ids + ) or len(renderer_ids) != len(set(renderer_ids)): + raise RuleCompileError("RENDERER_IDS_INVALID", rule_branch_key) + authority_ids = value.get("authority_refs") + if not isinstance(authority_ids, list) or not authority_ids or not all( + isinstance(item, str) and item for item in authority_ids + ) or len(authority_ids) != len(set(authority_ids)): + raise RuleCompileError("AUTHORITY_REFS_INVALID", rule_branch_key) + + corpus_scope_id = _require_identifier(value.get("corpus_scope_id"), "CORPUS_SCOPE_ID_INVALID") + element_set_id = _require_identifier(value.get("element_set_id"), "ELEMENT_SET_ID_INVALID") + ce13_branch_id = _require_identifier(value.get("ce13_branch_id"), "CE13_BRANCH_ID_INVALID") + party_set_rule_id = _require_identifier(value.get("party_set_rule_id"), "PARTY_SET_RULE_ID_INVALID") + review_policy_id = _require_identifier(value.get("review_policy_id"), "REVIEW_POLICY_ID_INVALID") + foreign_key_refs = { + "case_type": case_type_ref, + "renderers": [_exact_fk(indexes, "renderer", item) for item in renderer_ids], + "corpus_scope": _exact_fk(indexes, "corpus_scope", corpus_scope_id), + "element_set": _exact_fk(indexes, "element_set", element_set_id), + "ce_13_branch": _exact_fk(indexes, "ce_13_branch", ce13_branch_id), + "party_set_rule": _exact_fk(indexes, "party_set_rule", party_set_rule_id), + "review_policy": _exact_fk(indexes, "review_policy", review_policy_id), + "authorities": [_exact_fk(indexes, "authority", item) for item in authority_ids], + } + compiled.append( + { + "sub_rule_id": sub_rule_id, + "rule_branch_key": rule_branch_key, + "sub_rule_predicates": [closed_predicate], + "structured_relief_rule_id": structured_relief_rule_id, + "markdown_source_path": f"{logical_rule_root}/{path.name}", + "markdown_source_sha256": markdown_sha256, + "renderer_ids": renderer_ids, + "required_slot_ids": slots, + "corpus_scope_id": corpus_scope_id, + "element_set_id": element_set_id, + "ce13_branch_id": ce13_branch_id, + "party_set_rule_id": party_set_rule_id, + "review_policy_id": review_policy_id, + "legal_content_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": receipt_sha256, + } + ) + return { + "case_type_id": case_type_id, + "document_status": status, + "markdown_source_path": f"{logical_rule_root}/{path.name}", + "markdown_source_sha256": markdown_sha256, + "rule_branches": compiled, + } + + +def compile_projection( + rule_root: Path, + *, + deployment_root: Path = DEPLOYMENT_ROOT, + logical_rule_root: str = "rules/relief", +) -> dict[str, Any]: + """Compile reviewed Markdown directly into the canonical rule registry.""" + + documents = sorted(rule_root.glob("CT-[0-9][0-9][0-9].md")) + if len(documents) != EXPECTED_DOCUMENT_COUNT: + raise RuleCompileError("RELIEF_RULE_DOCUMENT_EXACT_137_REQUIRED", str(len(documents))) + expected_names = [f"CT-{ordinal:03d}.md" for ordinal in range(1, 138)] + if [path.name for path in documents] != expected_names: + raise RuleCompileError("RELIEF_RULE_DOCUMENT_ID_SET_MISMATCH", repr([p.name for p in documents])) + + case_registry_path = deployment_root / "manifest/case_type_registry.yml" + case_registry_raw = case_registry_path.read_bytes() + case_registry = _strict_json(case_registry_raw, source=case_registry_path.as_posix()) + if ( + case_registry.get("schema_version") != "stage2_case_type_registry.v1" + or case_registry.get("registry_id") != "STAGE2_CASE_TYPE_137_V1" + ): + raise RuleCompileError("CASE_TYPE_REGISTRY_IDENTITY_MISMATCH", repr(case_registry)[:200]) + case_rows = case_registry.get("rows") + if not isinstance(case_rows, list) or len(case_rows) != EXPECTED_DOCUMENT_COUNT: + raise RuleCompileError("CASE_TYPE_REGISTRY_EXACT_137_REQUIRED", repr(case_rows)[:200]) + case_by_id = { + row.get("case_type_id"): row for row in case_rows if isinstance(row, dict) + } + expected_ids = [f"CT-{ordinal:03d}" for ordinal in range(1, 138)] + if list(case_by_id) != expected_ids: + raise RuleCompileError("CASE_TYPE_ID_SET_MISMATCH", repr(list(case_by_id))) + + indexes = build_foreign_key_indexes(deployment_root) + compiled_documents = [ + compile_document( + path, + deployment_root=deployment_root, + foreign_key_indexes=indexes, + logical_rule_root=logical_rule_root, + ) + for path in documents + ] + branches = [branch for document in compiled_documents for branch in document["rule_branches"]] + branch_keys = [branch["rule_branch_key"] for branch in branches] + predicate_ids = [ + predicate["predicate_id"] + for branch in branches + for predicate in branch["sub_rule_predicates"] + ] + if len(branch_keys) != len(set(branch_keys)): + raise RuleCompileError("RULE_BRANCH_KEY_DUPLICATE_GLOBAL", repr(branch_keys)) + if len(predicate_ids) != len(set(predicate_ids)): + raise RuleCompileError("PREDICATE_ID_DUPLICATE_GLOBAL", repr(predicate_ids)) + + output_rows: list[dict[str, Any]] = [] + for document in compiled_documents: + case_type_id = document["case_type_id"] + catalog_row = case_by_id[case_type_id] + if document["document_status"] == "APPROVED": + if ( + catalog_row.get("claim_capable_disposition") != "CLAIM_CAPABLE" + or catalog_row.get("legal_classification_status") != "APPROVED" + or catalog_row.get("legal_content_status") != "APPROVED" + ): + raise RuleCompileError( + "APPROVED_RULE_REQUIRES_APPROVED_CLAIM_CAPABLE_CASE_TYPE", case_type_id + ) + receipt_pairs = { + ( + branch["legal_review_receipt_ref"], + branch["legal_review_receipt_sha256"], + ) + for branch in document["rule_branches"] + } + if len(receipt_pairs) != 1: + raise RuleCompileError("RULE_ROW_SINGLE_REVIEW_RECEIPT_REQUIRED", case_type_id) + receipt_ref, receipt_sha256 = next(iter(receipt_pairs)) + output_rows.append( + { + "case_type_id": case_type_id, + "claim_capable_disposition": "CLAIM_CAPABLE", + "legal_classification_status": "APPROVED", + "legal_content_status": "APPROVED", + "rule_branches": document["rule_branches"], + "non_claim_disposition": None, + "companion_case_type_ids": sorted( + set(catalog_row.get("companion_case_type_ids", [])) + ), + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": receipt_sha256, + } + ) + else: + output_rows.append( + { + "case_type_id": case_type_id, + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": catalog_row.get( + "legal_classification_status", "PENDING_LEGAL_CLASSIFICATION" + ), + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": None, + "companion_case_type_ids": [], + "legal_review_receipt_ref": None, + "legal_review_receipt_sha256": None, + } + ) + + approved_count = sum(row["legal_content_status"] == "APPROVED" for row in output_rows) + registry_status = ( + "APPROVED" + if approved_count == EXPECTED_DOCUMENT_COUNT + else "PARTIALLY_APPROVED" + if approved_count + else "PENDING_LEGAL_CONTENT" + ) + return { + "schema_version": "stage2_case_type_rule_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_RULE_REGISTRY_V1", + "case_type_registry_ref": "manifest/case_type_registry.yml", + "case_type_registry_sha256": sha256_bytes(case_registry_raw), + "rows": output_rows, + "runtime_branch_selector": "EXACT_TARGET_SIGNATURE_TO_CASE_TYPE_ID_TO_SUB_RULE_ID", + "fuzzy_fallback_allowed": False, + "nearest_rule_fallback_allowed": False, + "registry_status": registry_status, + } + + +def compile_pending_rule_registry( + case_registry_path: Path, + *, + logical_case_registry_path: str = "manifest/case_type_registry.yml", +) -> dict[str, Any]: + """Emit the schema-valid, non-executable 137-row rule registry scaffold. + + This mode exists so the release can bind the exact catalog and all expected + case-type slots before any legal Markdown is approved. It deliberately + emits no rule branch and cannot be upgraded to ``APPROVED`` without the + normal Markdown compiler and lawyer-receipt checks above. + """ + + raw = case_registry_path.read_bytes() + case_registry = _strict_json(raw, source=case_registry_path.as_posix()) + if ( + case_registry.get("schema_version") != "stage2_case_type_registry.v1" + or case_registry.get("registry_id") != "STAGE2_CASE_TYPE_137_V1" + ): + raise RuleCompileError("CASE_TYPE_REGISTRY_IDENTITY_MISMATCH", repr(case_registry)[:200]) + case_rows = case_registry.get("rows") + if not isinstance(case_rows, list) or len(case_rows) != EXPECTED_DOCUMENT_COUNT: + raise RuleCompileError("CASE_TYPE_REGISTRY_EXACT_137_REQUIRED", repr(case_rows)[:200]) + case_type_ids = [row.get("case_type_id") for row in case_rows if isinstance(row, dict)] + expected_ids = [f"CT-{ordinal:03d}" for ordinal in range(1, 138)] + if case_type_ids != expected_ids: + raise RuleCompileError("CASE_TYPE_ID_SET_MISMATCH", repr(case_type_ids)) + rows = [ + { + "case_type_id": case_type_id, + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": None, + "companion_case_type_ids": [], + "legal_review_receipt_ref": None, + "legal_review_receipt_sha256": None, + } + for case_type_id in case_type_ids + ] + return { + "schema_version": "stage2_case_type_rule_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_RULE_REGISTRY_V1", + "case_type_registry_ref": logical_case_registry_path, + "case_type_registry_sha256": sha256_bytes(raw), + "rows": rows, + "runtime_branch_selector": "EXACT_TARGET_SIGNATURE_TO_CASE_TYPE_ID_TO_SUB_RULE_ID", + "fuzzy_fallback_allowed": False, + "nearest_rule_fallback_allowed": False, + "registry_status": "PENDING_LEGAL_CONTENT", + } + + +def _atomic_write(path: Path, payload: bytes) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + temporary_name: str | None = None + try: + with tempfile.NamedTemporaryFile( + mode="wb", + dir=path.parent, + prefix=f".{path.name}.", + suffix=".tmp", + delete=False, + ) as handle: + temporary_name = handle.name + handle.write(payload) + handle.flush() + os.fsync(handle.fileno()) + os.replace(temporary_name, path) + temporary_name = None + finally: + if temporary_name is not None: + try: + Path(temporary_name).unlink() + except FileNotFoundError: + pass + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Compile 137 lawyer-reviewed relief-rule Markdown documents" + ) + parser.add_argument("rule_root", type=Path) + parser.add_argument("output", type=Path) + parser.add_argument("--deployment-root", type=Path, default=DEPLOYMENT_ROOT) + parser.add_argument("--logical-rule-root", default="rules/relief") + parser.add_argument( + "--pending-rule-registry-from", + type=Path, + help="emit a non-executable 137-row rule registry from this canonical case registry", + ) + parser.add_argument("--check", action="store_true") + return parser + + +def main(argv: Iterable[str] | None = None) -> int: + args = _parser().parse_args(list(argv) if argv is not None else None) + try: + if args.pending_rule_registry_from is not None: + compiled_document = compile_pending_rule_registry(args.pending_rule_registry_from) + else: + compiled_document = compile_projection( + args.rule_root, + deployment_root=args.deployment_root, + logical_rule_root=args.logical_rule_root, + ) + payload = canonical_json_bytes(compiled_document) + if args.check: + if not args.output.is_file() or args.output.read_bytes() != payload: + raise RuleCompileError("RELIEF_RULE_PROJECTION_PARITY_DRIFT", args.output.as_posix()) + result = {"status": "PARITY_PASS", "sha256": sha256_bytes(payload)} + else: + _atomic_write(args.output, payload) + compiled = json.loads(payload) + result = { + "status": "COMPILED", + "document_count": EXPECTED_DOCUMENT_COUNT, + "approved_branch_count": sum( + len(row.get("rule_branches", [])) + for row in compiled.get("rows", []) + if isinstance(row, dict) + ), + "registry_status": compiled.get("registry_status"), + "output_sha256": sha256_bytes(payload), + } + except (RuleCompileError, OSError) as exc: + result = { + "status": "COMPILE_FAILED", + "reason_code": getattr(exc, "code", type(exc).__name__), + "detail": getattr(exc, "detail", str(exc)), + } + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + return 2 + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_relief_rule_projection.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_relief_rule_projection.txt new file mode 100644 index 00000000..5fdfd55c --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/offline_build/compile_relief_rule_projection.txt @@ -0,0 +1,1006 @@ +#!/usr/bin/env python3 +"""Compile 137 reviewed relief-rule Markdown files into executable branches. + +The compiler never derives legal rules from prose. A document contributes an +executable branch only when its strict YAML front matter declares the branch +``APPROVED`` and every predicate, foreign key, source hash, and Korean-lawyer +approval receipt is independently verifiable. Incomplete documents remain +explicitly pending; a false ``APPROVED`` declaration is a hard failure rather +than a silent downgrade. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path, PurePosixPath +import re +import tempfile +from typing import Any, Iterable, Mapping, Sequence + +try: + import yaml +except ImportError: # pragma: no cover - incomplete offline build host + yaml = None # type: ignore[assignment] + + +DEPLOYMENT_ROOT = Path(__file__).resolve().parents[1] +EXPECTED_DOCUMENT_COUNT = 137 +METADATA_SCHEMA = "stage2_relief_rule_markdown_metadata.v1" +RECEIPT_SCHEMA = "stage2_relief_rule_legal_review_receipt.v1" +SHA256_RE = re.compile(r"^[a-f0-9]{64}$") +CASE_TYPE_RE = re.compile(r"^CT-[0-9]{3}$") +IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$") +LEGAL_REVIEW_PATH_RE = re.compile(r"^legal_review/[A-Za-z0-9][A-Za-z0-9._-]{0,191}\.json$") +ALLOWED_PREDICATE_FIELDS = frozenset( + { + "claim_family", + "legal_effect", + "performance_kind", + "object_kind", + "party_role_pattern", + "source_obligation_kind", + "remedy_mode", + "registry_action", + "procedural_posture", + "special_statute_tags", + } +) +ALLOWED_PREDICATE_OPERATORS = frozenset({"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"}) +BRANCH_REQUIRED_KEYS = frozenset( + { + "sub_rule_id", + "predicate", + "structured_relief_rule_id", + "renderer_ids", + "required_slot_ids", + "corpus_scope_id", + "element_set_id", + "ce13_branch_id", + "party_set_rule_id", + "review_policy_id", + "authority_refs", + "review_status", + "legal_review_receipt_ref", + "legal_review_receipt_sha256", + } +) +METADATA_REQUIRED_KEYS = frozenset( + {"schema_version", "case_type_id", "document_status", "rule_branches"} +) + + +class RuleCompileError(ValueError): + """Controlled compiler failure with a stable reason code.""" + + def __init__(self, code: str, detail: str) -> None: + super().__init__(f"{code}: {detail}") + self.code = code + self.detail = detail + + +if yaml is not None: + + class UniqueKeySafeLoader(yaml.SafeLoader): + """SafeLoader that rejects duplicate keys at every mapping depth.""" + + def construct_mapping(self, node: Any, deep: bool = False) -> dict[Any, Any]: + if not isinstance(node, yaml.MappingNode): + raise RuleCompileError("YAML_MAPPING_REQUIRED", repr(node)[:200]) + self.flatten_mapping(node) + result: dict[Any, Any] = {} + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + try: + duplicate = key in result + except TypeError as exc: + raise RuleCompileError("YAML_UNHASHABLE_KEY", repr(key)[:200]) from exc + if duplicate: + mark = getattr(key_node, "start_mark", None) + line = mark.line + 1 if mark is not None else "?" + raise RuleCompileError("YAML_DUPLICATE_KEY", f"{key!r}@line={line}") + result[key] = self.construct_object(value_node, deep=deep) + return result + +else: # pragma: no cover + + class UniqueKeySafeLoader: # type: ignore[no-redef] + pass + + +def sha256_bytes(value: bytes) -> str: + return hashlib.sha256(value).hexdigest() + + +def canonical_json_bytes(value: Any) -> bytes: + return ( + json.dumps( + value, + ensure_ascii=False, + allow_nan=False, + indent=2, + separators=(",", ": "), + ) + + "\n" + ).encode("utf-8") + + +def _strict_yaml(text: str, *, source: str) -> dict[str, Any]: + if yaml is None: + raise RuleCompileError("PYYAML_REQUIRED", "install PyYAML on offline build host") + try: + loaded = yaml.load(text, Loader=UniqueKeySafeLoader) + except RuleCompileError: + raise + except yaml.YAMLError as exc: + raise RuleCompileError("YAML_INVALID", f"{source}: {exc}") from exc + if not isinstance(loaded, dict): + raise RuleCompileError("YAML_ROOT_OBJECT_REQUIRED", source) + return loaded + + +def _strict_json(raw: bytes, *, source: str) -> dict[str, Any]: + def reject_duplicate(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise RuleCompileError("JSON_DUPLICATE_KEY", f"{source}:{key}") + result[key] = value + return result + + try: + loaded = json.loads( + raw.decode("utf-8"), + object_pairs_hook=reject_duplicate, + parse_constant=lambda value: (_ for _ in ()).throw( + RuleCompileError("JSON_NONFINITE_NUMBER", f"{source}:{value}") + ), + ) + except RuleCompileError: + raise + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise RuleCompileError("JSON_INVALID", f"{source}: {exc}") from exc + if not isinstance(loaded, dict): + raise RuleCompileError("JSON_ROOT_OBJECT_REQUIRED", source) + return loaded + + +def _require_exact_keys(value: Mapping[str, Any], expected: set[str] | frozenset[str], code: str) -> None: + observed = set(value) + if observed != set(expected): + raise RuleCompileError( + code, + json.dumps( + {"missing": sorted(set(expected) - observed), "extra": sorted(observed - set(expected))}, + ensure_ascii=False, + sort_keys=True, + ), + ) + + +def _safe_relative_path(value: Any, code: str) -> str: + if not isinstance(value, str) or not value or "\\" in value or "\x00" in value: + raise RuleCompileError(code, repr(value)) + path = PurePosixPath(value) + if path.is_absolute() or any(part in {"", ".", ".."} for part in path.parts): + raise RuleCompileError(code, value) + return path.as_posix() + + +def _safe_legal_review_path(value: Any) -> str: + path = _safe_relative_path(value, "LEGAL_REVIEW_RECEIPT_REF_INVALID") + if LEGAL_REVIEW_PATH_RE.fullmatch(path) is None: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_PATH_FAMILY", path) + return path + + +def _require_identifier(value: Any, code: str) -> str: + if not isinstance(value, str) or not IDENTIFIER_RE.fullmatch(value): + raise RuleCompileError(code, repr(value)) + return value + + +def _require_sha256(value: Any, code: str) -> str: + if not isinstance(value, str) or not SHA256_RE.fullmatch(value): + raise RuleCompileError(code, repr(value)) + return value + + +def _front_matter(path: Path, raw: bytes) -> tuple[dict[str, Any], bytes]: + try: + text = raw.decode("utf-8") + except UnicodeDecodeError as exc: + raise RuleCompileError("MARKDOWN_UTF8_REQUIRED", f"{path}: {exc}") from exc + if text.startswith("\ufeff"): + raise RuleCompileError("MARKDOWN_UTF8_BOM_FORBIDDEN", path.as_posix()) + lines = text.splitlines(keepends=True) + if not lines or lines[0].strip() != "---": + raise RuleCompileError("RULE_METADATA_FRONT_MATTER_REQUIRED", path.as_posix()) + try: + closing = next(index for index, line in enumerate(lines[1:], 1) if line.strip() == "---") + except StopIteration as exc: + raise RuleCompileError("RULE_METADATA_FRONT_MATTER_UNCLOSED", path.as_posix()) from exc + metadata_text = "".join(lines[1:closing]) + body_bytes = "".join(lines[closing + 1 :]).encode("utf-8") + return _strict_yaml(metadata_text, source=path.as_posix()), body_bytes + + +def _review_scope_sha256(metadata: Mapping[str, Any], body_bytes: bytes) -> str: + """Bind legal metadata/body without creating a receipt-hash cycle. + + The receipt SHA fields are the only removed values. Receipt paths, + predicates, legal slots, foreign keys, statuses, and the exact Markdown body + digest remain in scope, so a receipt cannot be replayed after legal content + changes. + """ + + def strip_receipt_hash(value: Any) -> Any: + if isinstance(value, dict): + return { + key: strip_receipt_hash(child) + for key, child in value.items() + if key != "legal_review_receipt_sha256" + } + if isinstance(value, list): + return [strip_receipt_hash(child) for child in value] + return value + + scope = { + "metadata_without_receipt_sha256": strip_receipt_hash(dict(metadata)), + "markdown_body_sha256": sha256_bytes(body_bytes), + } + return sha256_bytes(canonical_json_bytes(scope)) + + +def _collect_values(value: Any, keys: frozenset[str]) -> set[str]: + found: set[str] = set() + if isinstance(value, dict): + for key, child in value.items(): + if key in keys and isinstance(child, str) and child: + found.add(child) + found.update(_collect_values(child, keys)) + elif isinstance(value, list): + for child in value: + found.update(_collect_values(child, keys)) + return found + + +def _record_is_approved(value: Mapping[str, Any]) -> bool: + """Return true only when every present admission marker is affirmative.""" + + markers: list[bool] = [] + for key in ( + "status", + "review_status", + "legal_review_status", + "legal_classification_status", + "legal_content_status", + "implementation_status", + "semantic_review_status", + ): + if key in value: + markers.append(value.get(key) == "APPROVED") + if "execution_eligible" in value: + markers.append(value.get("execution_eligible") is True) + return bool(markers) and all(markers) + + +def _collect_approved_values(value: Any, keys: frozenset[str], *, inherited: bool = False) -> set[str]: + found: set[str] = set() + if isinstance(value, dict): + admitted = inherited or _record_is_approved(value) + for key, child in value.items(): + if admitted and key in keys and isinstance(child, str) and child: + found.add(child) + found.update(_collect_approved_values(child, keys, inherited=admitted)) + elif isinstance(value, list): + for child in value: + found.update(_collect_approved_values(child, keys, inherited=inherited)) + return found + + +def _load_registry(path: Path) -> dict[str, Any]: + if not path.is_file() or path.is_symlink(): + raise RuleCompileError("FK_REGISTRY_UNAVAILABLE", path.as_posix()) + raw = path.read_bytes() + try: + return _strict_json(raw, source=path.as_posix()) + except RuleCompileError as json_error: + try: + return _strict_yaml(raw.decode("utf-8"), source=path.as_posix()) + except (UnicodeDecodeError, RuleCompileError) as yaml_error: + raise RuleCompileError( + "FK_REGISTRY_PARSE_FAILED", f"{path}: json={json_error}; yaml={yaml_error}" + ) from yaml_error + + +def _logical_asset_path(path: Path, deployment_root: Path) -> str: + try: + return path.resolve(strict=True).relative_to(deployment_root.resolve(strict=True)).as_posix() + except ValueError as exc: + raise RuleCompileError("FK_ASSET_OUTSIDE_DEPLOYMENT_ROOT", path.as_posix()) from exc + + +def _add_index_values( + index: dict[str, list[dict[str, str]]], + ids: Sequence[str] | set[str], + path: Path, + deployment_root: Path, +) -> None: + raw = path.read_bytes() + ref = { + "path": _logical_asset_path(path, deployment_root), + "sha256": sha256_bytes(raw), + } + for value in ids: + refs = index.setdefault(value, []) + if ref not in refs: + refs.append(ref) + + +def build_foreign_key_indexes(deployment_root: Path) -> dict[str, dict[str, list[dict[str, str]]]]: + """Build exact ID->physical-asset/hash indexes from canonical registries.""" + + indexes: dict[str, dict[str, list[dict[str, str]]]] = { + "case_type": {}, + "renderer": {}, + "corpus_scope": {}, + "element_set": {}, + "ce_13_branch": {}, + "party_set_rule": {}, + "review_policy": {}, + "authority": {}, + } + + case_path = deployment_root / "manifest/case_type_registry.yml" + case_doc = _load_registry(case_path) + if ( + case_doc.get("schema_version") != "stage2_case_type_registry.v1" + or case_doc.get("registry_id") != "STAGE2_CASE_TYPE_137_V1" + ): + raise RuleCompileError("CASE_TYPE_REGISTRY_IDENTITY_MISMATCH", repr(case_doc)[:200]) + case_rows = case_doc.get("rows") + if not isinstance(case_rows, list) or len(case_rows) != EXPECTED_DOCUMENT_COUNT: + raise RuleCompileError("CASE_TYPE_REGISTRY_EXACT_137_REQUIRED", repr(case_rows)[:200]) + admitted_case_type_ids = { + str(row.get("case_type_id")) + for row in case_rows + if isinstance(row, dict) + and CASE_TYPE_RE.fullmatch(str(row.get("case_type_id", ""))) + and row.get("row_kind") in {"CLAIM", "GENERIC"} + and row.get("claim_capable_disposition") == "CLAIM_CAPABLE" + and row.get("legal_classification_status") == "APPROVED" + and row.get("legal_content_status") == "APPROVED" + and isinstance(row.get("legal_review_receipt_ref"), str) + and isinstance(row.get("legal_review_receipt_sha256"), str) + } + _add_index_values(indexes["case_type"], admitted_case_type_ids, case_path, deployment_root) + + renderer_paths = sorted((deployment_root / "renderers").glob("R*.yml")) + if not renderer_paths: + raise RuleCompileError("RENDERER_REGISTRY_UNAVAILABLE", "renderers/R*.yml") + for path in renderer_paths: + document = _load_registry(path) + renderer_id = document.get("renderer_id") + if isinstance(renderer_id, str) and renderer_id and _record_is_approved(document): + _add_index_values(indexes["renderer"], {renderer_id}, path, deployment_root) + + scope_path = deployment_root / "registry/corpus/requirement_fact_scope.yml" + scope_document = _load_registry(scope_path) + if ( + scope_document.get("schema_version") + != "stage2_requirement_fact_scope_registry.v1" + or scope_document.get("registry_id") != "STAGE2_REQUIREMENT_FACT_SCOPE_V1" + or scope_document.get("registry_status") + not in {"PENDING_LEGAL_CONTENT", "PARTIALLY_APPROVED", "APPROVED"} + ): + raise RuleCompileError( + "REQUIREMENT_FACT_SCOPE_REGISTRY_IDENTITY_MISMATCH", + repr(scope_document)[:200], + ) + scope_rows = scope_document.get("rows") + if not isinstance(scope_rows, list): + raise RuleCompileError("REQUIREMENT_FACT_SCOPE_ROWS_REQUIRED", repr(scope_rows)[:200]) + for row in scope_rows: + if not isinstance(row, dict) or row.get("approval_status") != "APPROVED": + continue + if ( + not isinstance(row.get("legal_review_receipt_ref"), str) + or not isinstance(row.get("legal_review_receipt_sha256"), str) + ): + raise RuleCompileError( + "APPROVED_REQUIREMENT_FACT_SCOPE_RECEIPT_REQUIRED", + repr(row.get("corpus_scope_id")), + ) + corpus_scope_id = row.get("corpus_scope_id") + element_set_id = row.get("element_set_id") + if isinstance(corpus_scope_id, str) and corpus_scope_id: + _add_index_values( + indexes["corpus_scope"], {corpus_scope_id}, scope_path, deployment_root + ) + if isinstance(element_set_id, str) and element_set_id: + _add_index_values( + indexes["element_set"], {element_set_id}, scope_path, deployment_root + ) + + specs = ( + ( + "registry/calculations/CE-13_court_value_cost.yml", + (("ce_13_branch", frozenset({"ce_13_branch_id", "court_value_rule_id", "branch_id", "formula_id"})),), + ), + ( + "registry/party/party_set_rules.yml", + (("party_set_rule", frozenset({"party_set_rule_id"})),), + ), + ( + "registry/review/review_policy_registry.yml", + (("review_policy", frozenset({"review_policy_id", "policy_id"})),), + ), + ( + "registry/authority/authority_registry.yml", + (("authority", frozenset({"authority_id", "authority_ref_id", "proposition_id"})),), + ), + ) + for relative_path, targets in specs: + path = deployment_root / relative_path + document = _load_registry(path) + for index_name, keys in targets: + _add_index_values( + indexes[index_name], + _collect_approved_values(document, keys), + path, + deployment_root, + ) + return indexes + + +def _exact_fk( + indexes: Mapping[str, Mapping[str, list[dict[str, str]]]], + index_name: str, + value: str, +) -> dict[str, str]: + matches = indexes.get(index_name, {}).get(value, []) + if len(matches) != 1: + raise RuleCompileError( + "RULE_BRANCH_FK_EXACT_ONE_REQUIRED", + f"kind={index_name} id={value!r} matches={len(matches)}", + ) + return {"asset_id": value, **matches[0]} + + +def _predicate( + value: Any, + *, + branch_receipt_ref: str, + branch_receipt_sha256: str, +) -> tuple[str, list[dict[str, Any]], dict[str, Any]]: + if not isinstance(value, dict): + raise RuleCompileError("RULE_BRANCH_PREDICATE_OBJECT_REQUIRED", repr(value)) + required = { + "predicate_id", + "clauses", + "priority", + "legal_review_status", + "legal_review_receipt_ref", + "legal_review_receipt_sha256", + } + _require_exact_keys(value, required, "RULE_BRANCH_PREDICATE_KEYS") + predicate_id = _require_identifier(value.get("predicate_id"), "PREDICATE_ID_INVALID") + clauses = value.get("clauses") + if not isinstance(clauses, list) or not clauses: + raise RuleCompileError("RULE_BRANCH_PREDICATE_CLAUSES_REQUIRED", predicate_id) + normalized: list[dict[str, Any]] = [] + for ordinal, clause in enumerate(clauses, 1): + if not isinstance(clause, dict): + raise RuleCompileError("PREDICATE_CLAUSE_OBJECT_REQUIRED", f"{predicate_id}:{ordinal}") + _require_exact_keys(clause, {"field", "operator", "value"}, "PREDICATE_CLAUSE_KEYS") + field = clause.get("field") + operator = clause.get("operator") + operand = clause.get("value") + if field not in ALLOWED_PREDICATE_FIELDS or operator not in ALLOWED_PREDICATE_OPERATORS: + raise RuleCompileError("PREDICATE_CLAUSE_CLOSED_VOCABULARY", repr(clause)) + if operator == "EXISTS" and not isinstance(operand, bool): + raise RuleCompileError("PREDICATE_EXISTS_BOOLEAN_REQUIRED", repr(clause)) + if operator in {"IN", "ALL_OF", "NONE_OF"} and ( + not isinstance(operand, list) + or not operand + or not all(isinstance(item, str) and item for item in operand) + or len(operand) != len(set(operand)) + ): + raise RuleCompileError("PREDICATE_SET_VALUE_INVALID", repr(clause)) + if operator == "EQ" and not isinstance(operand, (str, bool)): + raise RuleCompileError("PREDICATE_EQ_VALUE_INVALID", repr(clause)) + normalized.append({"field": field, "operator": operator, "value": operand}) + priority = value.get("priority") + if not isinstance(priority, int) or isinstance(priority, bool) or priority < 0: + raise RuleCompileError("PREDICATE_PRIORITY_INVALID", repr(priority)) + if value.get("legal_review_status") != "APPROVED": + raise RuleCompileError("PREDICATE_LAWYER_APPROVAL_REQUIRED", predicate_id) + if value.get("legal_review_receipt_ref") != branch_receipt_ref or value.get( + "legal_review_receipt_sha256" + ) != branch_receipt_sha256: + raise RuleCompileError("PREDICATE_RECEIPT_MUST_EQUAL_BRANCH_RECEIPT", predicate_id) + closed = { + "predicate_id": predicate_id, + "clauses": normalized, + "priority": priority, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": branch_receipt_ref, + "legal_review_receipt_sha256": branch_receipt_sha256, + } + return predicate_id, normalized, closed + + +def _verify_lawyer_receipt( + deployment_root: Path, + *, + receipt_ref: str, + receipt_sha256: str, + rule_branch_key: str, + review_scope_sha256: str, +) -> None: + receipt_path = deployment_root / receipt_ref + try: + receipt_path.resolve(strict=True).relative_to(deployment_root.resolve(strict=True)) + except (FileNotFoundError, ValueError) as exc: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_UNAVAILABLE", receipt_ref) from exc + if receipt_path.is_symlink() or not receipt_path.is_file(): + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_UNAVAILABLE", receipt_ref) + raw = receipt_path.read_bytes() + if sha256_bytes(raw) != receipt_sha256: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_HASH_MISMATCH", receipt_ref) + receipt = _strict_json(raw, source=receipt_ref) + required = { + "schema_version", + "status", + "reviewer_role", + "signed_by", + "signature_ref", + "signature_sha256", + "review_scope_sha256", + "approved_rule_branch_keys", + } + _require_exact_keys(receipt, required, "LEGAL_REVIEW_RECEIPT_KEYS") + if receipt.get("schema_version") != RECEIPT_SCHEMA or receipt.get("status") != "APPROVED": + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_NOT_APPROVED", receipt_ref) + if receipt.get("reviewer_role") not in {"KOREAN_LAWYER", "KOREAN_ATTORNEY"}: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_ROLE_INVALID", receipt_ref) + if not isinstance(receipt.get("signed_by"), str) or not receipt["signed_by"]: + raise RuleCompileError("LEGAL_REVIEW_RECEIPT_SIGNER_REQUIRED", receipt_ref) + signature_ref = _safe_relative_path( + receipt.get("signature_ref"), "LEGAL_REVIEW_SIGNATURE_REF_INVALID" + ) + signature_sha256 = _require_sha256( + receipt.get("signature_sha256"), "LEGAL_REVIEW_SIGNATURE_SHA256_INVALID" + ) + signature_path = deployment_root / signature_ref + try: + signature_path.resolve(strict=True).relative_to(deployment_root.resolve(strict=True)) + except (FileNotFoundError, ValueError) as exc: + raise RuleCompileError("LEGAL_REVIEW_SIGNATURE_UNAVAILABLE", signature_ref) from exc + if signature_path.is_symlink() or not signature_path.is_file(): + raise RuleCompileError("LEGAL_REVIEW_SIGNATURE_UNAVAILABLE", signature_ref) + if sha256_bytes(signature_path.read_bytes()) != signature_sha256: + raise RuleCompileError("LEGAL_REVIEW_SIGNATURE_HASH_MISMATCH", signature_ref) + if receipt.get("review_scope_sha256") != review_scope_sha256: + raise RuleCompileError("LEGAL_REVIEW_SCOPE_HASH_MISMATCH", receipt_ref) + branch_keys = receipt.get("approved_rule_branch_keys") + if ( + not isinstance(branch_keys, list) + or not all(isinstance(item, str) and item for item in branch_keys) + or len(branch_keys) != len(set(branch_keys)) + or rule_branch_key not in branch_keys + ): + raise RuleCompileError("LEGAL_REVIEW_BRANCH_SCOPE_MISMATCH", rule_branch_key) + + +def compile_document( + path: Path, + *, + deployment_root: Path = DEPLOYMENT_ROOT, + foreign_key_indexes: Mapping[str, Mapping[str, list[dict[str, str]]]] | None = None, + logical_rule_root: str = "rules/relief", +) -> dict[str, Any]: + raw = path.read_bytes() + markdown_sha256 = sha256_bytes(raw) + metadata, body_bytes = _front_matter(path, raw) + _require_exact_keys(metadata, METADATA_REQUIRED_KEYS, "RULE_METADATA_KEYS") + if metadata.get("schema_version") != METADATA_SCHEMA: + raise RuleCompileError("RULE_METADATA_SCHEMA_MISMATCH", repr(metadata.get("schema_version"))) + case_type_id = metadata.get("case_type_id") + if not isinstance(case_type_id, str) or not CASE_TYPE_RE.fullmatch(case_type_id): + raise RuleCompileError("CASE_TYPE_ID_INVALID", repr(case_type_id)) + if path.name != f"{case_type_id}.md": + raise RuleCompileError("RULE_DOCUMENT_FILENAME_ID_MISMATCH", path.as_posix()) + status = metadata.get("document_status") + if status not in {"PENDING_LEGAL_CONTENT", "APPROVED"}: + raise RuleCompileError("RULE_DOCUMENT_STATUS_INVALID", repr(status)) + raw_branches = metadata.get("rule_branches") + if not isinstance(raw_branches, list): + raise RuleCompileError("RULE_BRANCHES_ARRAY_REQUIRED", case_type_id) + if status == "PENDING_LEGAL_CONTENT": + if raw_branches: + raise RuleCompileError("PENDING_DOCUMENT_BRANCHES_MUST_BE_EMPTY", case_type_id) + return { + "case_type_id": case_type_id, + "document_status": status, + "markdown_source_path": f"{logical_rule_root}/{path.name}", + "markdown_source_sha256": markdown_sha256, + "rule_branches": [], + } + if not raw_branches: + raise RuleCompileError("APPROVED_DOCUMENT_BRANCH_REQUIRED", case_type_id) + + indexes = foreign_key_indexes or build_foreign_key_indexes(deployment_root) + case_type_ref = _exact_fk(indexes, "case_type", case_type_id) + review_scope_sha256 = _review_scope_sha256(metadata, body_bytes) + compiled: list[dict[str, Any]] = [] + seen_sub_rules: set[str] = set() + seen_predicates: set[str] = set() + for ordinal, value in enumerate(raw_branches, 1): + if not isinstance(value, dict): + raise RuleCompileError("RULE_BRANCH_OBJECT_REQUIRED", f"{case_type_id}:{ordinal}") + _require_exact_keys(value, BRANCH_REQUIRED_KEYS, "RULE_BRANCH_KEYS") + sub_rule_id = _require_identifier(value.get("sub_rule_id"), "SUB_RULE_ID_INVALID") + if sub_rule_id in seen_sub_rules: + raise RuleCompileError("SUB_RULE_ID_DUPLICATE_IN_DOCUMENT", sub_rule_id) + seen_sub_rules.add(sub_rule_id) + rule_branch_key = f"{case_type_id}::{sub_rule_id}" + if value.get("review_status") != "APPROVED": + raise RuleCompileError("RULE_BRANCH_LAWYER_APPROVAL_REQUIRED", rule_branch_key) + receipt_ref = _safe_legal_review_path(value.get("legal_review_receipt_ref")) + receipt_sha256 = _require_sha256( + value.get("legal_review_receipt_sha256"), "LEGAL_REVIEW_RECEIPT_SHA256_INVALID" + ) + _verify_lawyer_receipt( + deployment_root, + receipt_ref=receipt_ref, + receipt_sha256=receipt_sha256, + rule_branch_key=rule_branch_key, + review_scope_sha256=review_scope_sha256, + ) + predicate_id, clauses, closed_predicate = _predicate( + value.get("predicate"), + branch_receipt_ref=receipt_ref, + branch_receipt_sha256=receipt_sha256, + ) + if predicate_id in seen_predicates: + raise RuleCompileError("PREDICATE_ID_DUPLICATE_IN_DOCUMENT", predicate_id) + seen_predicates.add(predicate_id) + + structured_relief_rule_id = _require_identifier( + value.get("structured_relief_rule_id"), "STRUCTURED_RELIEF_RULE_ID_INVALID" + ) + slots = value.get("required_slot_ids") + if not isinstance(slots, list) or not slots or not all( + isinstance(item, str) and item for item in slots + ) or len(slots) != len(set(slots)): + raise RuleCompileError("REQUIRED_SLOT_IDS_INVALID", rule_branch_key) + renderer_ids = value.get("renderer_ids") + if not isinstance(renderer_ids, list) or not renderer_ids or not all( + isinstance(item, str) and item for item in renderer_ids + ) or len(renderer_ids) != len(set(renderer_ids)): + raise RuleCompileError("RENDERER_IDS_INVALID", rule_branch_key) + authority_ids = value.get("authority_refs") + if not isinstance(authority_ids, list) or not authority_ids or not all( + isinstance(item, str) and item for item in authority_ids + ) or len(authority_ids) != len(set(authority_ids)): + raise RuleCompileError("AUTHORITY_REFS_INVALID", rule_branch_key) + + corpus_scope_id = _require_identifier(value.get("corpus_scope_id"), "CORPUS_SCOPE_ID_INVALID") + element_set_id = _require_identifier(value.get("element_set_id"), "ELEMENT_SET_ID_INVALID") + ce13_branch_id = _require_identifier(value.get("ce13_branch_id"), "CE13_BRANCH_ID_INVALID") + party_set_rule_id = _require_identifier(value.get("party_set_rule_id"), "PARTY_SET_RULE_ID_INVALID") + review_policy_id = _require_identifier(value.get("review_policy_id"), "REVIEW_POLICY_ID_INVALID") + foreign_key_refs = { + "case_type": case_type_ref, + "renderers": [_exact_fk(indexes, "renderer", item) for item in renderer_ids], + "corpus_scope": _exact_fk(indexes, "corpus_scope", corpus_scope_id), + "element_set": _exact_fk(indexes, "element_set", element_set_id), + "ce_13_branch": _exact_fk(indexes, "ce_13_branch", ce13_branch_id), + "party_set_rule": _exact_fk(indexes, "party_set_rule", party_set_rule_id), + "review_policy": _exact_fk(indexes, "review_policy", review_policy_id), + "authorities": [_exact_fk(indexes, "authority", item) for item in authority_ids], + } + compiled.append( + { + "sub_rule_id": sub_rule_id, + "rule_branch_key": rule_branch_key, + "sub_rule_predicates": [closed_predicate], + "structured_relief_rule_id": structured_relief_rule_id, + "markdown_source_path": f"{logical_rule_root}/{path.name}", + "markdown_source_sha256": markdown_sha256, + "renderer_ids": renderer_ids, + "required_slot_ids": slots, + "corpus_scope_id": corpus_scope_id, + "element_set_id": element_set_id, + "ce13_branch_id": ce13_branch_id, + "party_set_rule_id": party_set_rule_id, + "review_policy_id": review_policy_id, + "legal_content_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": receipt_sha256, + } + ) + return { + "case_type_id": case_type_id, + "document_status": status, + "markdown_source_path": f"{logical_rule_root}/{path.name}", + "markdown_source_sha256": markdown_sha256, + "rule_branches": compiled, + } + + +def compile_projection( + rule_root: Path, + *, + deployment_root: Path = DEPLOYMENT_ROOT, + logical_rule_root: str = "rules/relief", +) -> dict[str, Any]: + """Compile reviewed Markdown directly into the canonical rule registry.""" + + documents = sorted(rule_root.glob("CT-[0-9][0-9][0-9].md")) + if len(documents) != EXPECTED_DOCUMENT_COUNT: + raise RuleCompileError("RELIEF_RULE_DOCUMENT_EXACT_137_REQUIRED", str(len(documents))) + expected_names = [f"CT-{ordinal:03d}.md" for ordinal in range(1, 138)] + if [path.name for path in documents] != expected_names: + raise RuleCompileError("RELIEF_RULE_DOCUMENT_ID_SET_MISMATCH", repr([p.name for p in documents])) + + case_registry_path = deployment_root / "manifest/case_type_registry.yml" + case_registry_raw = case_registry_path.read_bytes() + case_registry = _strict_json(case_registry_raw, source=case_registry_path.as_posix()) + if ( + case_registry.get("schema_version") != "stage2_case_type_registry.v1" + or case_registry.get("registry_id") != "STAGE2_CASE_TYPE_137_V1" + ): + raise RuleCompileError("CASE_TYPE_REGISTRY_IDENTITY_MISMATCH", repr(case_registry)[:200]) + case_rows = case_registry.get("rows") + if not isinstance(case_rows, list) or len(case_rows) != EXPECTED_DOCUMENT_COUNT: + raise RuleCompileError("CASE_TYPE_REGISTRY_EXACT_137_REQUIRED", repr(case_rows)[:200]) + case_by_id = { + row.get("case_type_id"): row for row in case_rows if isinstance(row, dict) + } + expected_ids = [f"CT-{ordinal:03d}" for ordinal in range(1, 138)] + if list(case_by_id) != expected_ids: + raise RuleCompileError("CASE_TYPE_ID_SET_MISMATCH", repr(list(case_by_id))) + + indexes = build_foreign_key_indexes(deployment_root) + compiled_documents = [ + compile_document( + path, + deployment_root=deployment_root, + foreign_key_indexes=indexes, + logical_rule_root=logical_rule_root, + ) + for path in documents + ] + branches = [branch for document in compiled_documents for branch in document["rule_branches"]] + branch_keys = [branch["rule_branch_key"] for branch in branches] + predicate_ids = [ + predicate["predicate_id"] + for branch in branches + for predicate in branch["sub_rule_predicates"] + ] + if len(branch_keys) != len(set(branch_keys)): + raise RuleCompileError("RULE_BRANCH_KEY_DUPLICATE_GLOBAL", repr(branch_keys)) + if len(predicate_ids) != len(set(predicate_ids)): + raise RuleCompileError("PREDICATE_ID_DUPLICATE_GLOBAL", repr(predicate_ids)) + + output_rows: list[dict[str, Any]] = [] + for document in compiled_documents: + case_type_id = document["case_type_id"] + catalog_row = case_by_id[case_type_id] + if document["document_status"] == "APPROVED": + if ( + catalog_row.get("claim_capable_disposition") != "CLAIM_CAPABLE" + or catalog_row.get("legal_classification_status") != "APPROVED" + or catalog_row.get("legal_content_status") != "APPROVED" + ): + raise RuleCompileError( + "APPROVED_RULE_REQUIRES_APPROVED_CLAIM_CAPABLE_CASE_TYPE", case_type_id + ) + receipt_pairs = { + ( + branch["legal_review_receipt_ref"], + branch["legal_review_receipt_sha256"], + ) + for branch in document["rule_branches"] + } + if len(receipt_pairs) != 1: + raise RuleCompileError("RULE_ROW_SINGLE_REVIEW_RECEIPT_REQUIRED", case_type_id) + receipt_ref, receipt_sha256 = next(iter(receipt_pairs)) + output_rows.append( + { + "case_type_id": case_type_id, + "claim_capable_disposition": "CLAIM_CAPABLE", + "legal_classification_status": "APPROVED", + "legal_content_status": "APPROVED", + "rule_branches": document["rule_branches"], + "non_claim_disposition": None, + "companion_case_type_ids": sorted( + set(catalog_row.get("companion_case_type_ids", [])) + ), + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": receipt_sha256, + } + ) + else: + output_rows.append( + { + "case_type_id": case_type_id, + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": catalog_row.get( + "legal_classification_status", "PENDING_LEGAL_CLASSIFICATION" + ), + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": None, + "companion_case_type_ids": [], + "legal_review_receipt_ref": None, + "legal_review_receipt_sha256": None, + } + ) + + approved_count = sum(row["legal_content_status"] == "APPROVED" for row in output_rows) + registry_status = ( + "APPROVED" + if approved_count == EXPECTED_DOCUMENT_COUNT + else "PARTIALLY_APPROVED" + if approved_count + else "PENDING_LEGAL_CONTENT" + ) + return { + "schema_version": "stage2_case_type_rule_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_RULE_REGISTRY_V1", + "case_type_registry_ref": "manifest/case_type_registry.yml", + "case_type_registry_sha256": sha256_bytes(case_registry_raw), + "rows": output_rows, + "runtime_branch_selector": "EXACT_TARGET_SIGNATURE_TO_CASE_TYPE_ID_TO_SUB_RULE_ID", + "fuzzy_fallback_allowed": False, + "nearest_rule_fallback_allowed": False, + "registry_status": registry_status, + } + + +def compile_pending_rule_registry( + case_registry_path: Path, + *, + logical_case_registry_path: str = "manifest/case_type_registry.yml", +) -> dict[str, Any]: + """Emit the schema-valid, non-executable 137-row rule registry scaffold. + + This mode exists so the release can bind the exact catalog and all expected + case-type slots before any legal Markdown is approved. It deliberately + emits no rule branch and cannot be upgraded to ``APPROVED`` without the + normal Markdown compiler and lawyer-receipt checks above. + """ + + raw = case_registry_path.read_bytes() + case_registry = _strict_json(raw, source=case_registry_path.as_posix()) + if ( + case_registry.get("schema_version") != "stage2_case_type_registry.v1" + or case_registry.get("registry_id") != "STAGE2_CASE_TYPE_137_V1" + ): + raise RuleCompileError("CASE_TYPE_REGISTRY_IDENTITY_MISMATCH", repr(case_registry)[:200]) + case_rows = case_registry.get("rows") + if not isinstance(case_rows, list) or len(case_rows) != EXPECTED_DOCUMENT_COUNT: + raise RuleCompileError("CASE_TYPE_REGISTRY_EXACT_137_REQUIRED", repr(case_rows)[:200]) + case_type_ids = [row.get("case_type_id") for row in case_rows if isinstance(row, dict)] + expected_ids = [f"CT-{ordinal:03d}" for ordinal in range(1, 138)] + if case_type_ids != expected_ids: + raise RuleCompileError("CASE_TYPE_ID_SET_MISMATCH", repr(case_type_ids)) + rows = [ + { + "case_type_id": case_type_id, + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + "legal_content_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + "non_claim_disposition": None, + "companion_case_type_ids": [], + "legal_review_receipt_ref": None, + "legal_review_receipt_sha256": None, + } + for case_type_id in case_type_ids + ] + return { + "schema_version": "stage2_case_type_rule_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_RULE_REGISTRY_V1", + "case_type_registry_ref": logical_case_registry_path, + "case_type_registry_sha256": sha256_bytes(raw), + "rows": rows, + "runtime_branch_selector": "EXACT_TARGET_SIGNATURE_TO_CASE_TYPE_ID_TO_SUB_RULE_ID", + "fuzzy_fallback_allowed": False, + "nearest_rule_fallback_allowed": False, + "registry_status": "PENDING_LEGAL_CONTENT", + } + + +def _atomic_write(path: Path, payload: bytes) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + temporary_name: str | None = None + try: + with tempfile.NamedTemporaryFile( + mode="wb", + dir=path.parent, + prefix=f".{path.name}.", + suffix=".tmp", + delete=False, + ) as handle: + temporary_name = handle.name + handle.write(payload) + handle.flush() + os.fsync(handle.fileno()) + os.replace(temporary_name, path) + temporary_name = None + finally: + if temporary_name is not None: + try: + Path(temporary_name).unlink() + except FileNotFoundError: + pass + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Compile 137 lawyer-reviewed relief-rule Markdown documents" + ) + parser.add_argument("rule_root", type=Path) + parser.add_argument("output", type=Path) + parser.add_argument("--deployment-root", type=Path, default=DEPLOYMENT_ROOT) + parser.add_argument("--logical-rule-root", default="rules/relief") + parser.add_argument( + "--pending-rule-registry-from", + type=Path, + help="emit a non-executable 137-row rule registry from this canonical case registry", + ) + parser.add_argument("--check", action="store_true") + return parser + + +def main(argv: Iterable[str] | None = None) -> int: + args = _parser().parse_args(list(argv) if argv is not None else None) + try: + if args.pending_rule_registry_from is not None: + compiled_document = compile_pending_rule_registry(args.pending_rule_registry_from) + else: + compiled_document = compile_projection( + args.rule_root, + deployment_root=args.deployment_root, + logical_rule_root=args.logical_rule_root, + ) + payload = canonical_json_bytes(compiled_document) + if args.check: + if not args.output.is_file() or args.output.read_bytes() != payload: + raise RuleCompileError("RELIEF_RULE_PROJECTION_PARITY_DRIFT", args.output.as_posix()) + result = {"status": "PARITY_PASS", "sha256": sha256_bytes(payload)} + else: + _atomic_write(args.output, payload) + compiled = json.loads(payload) + result = { + "status": "COMPILED", + "document_count": EXPECTED_DOCUMENT_COUNT, + "approved_branch_count": sum( + len(row.get("rule_branches", [])) + for row in compiled.get("rows", []) + if isinstance(row, dict) + ), + "registry_status": compiled.get("registry_status"), + "output_sha256": sha256_bytes(payload), + } + except (RuleCompileError, OSError) as exc: + result = { + "status": "COMPILE_FAILED", + "reason_code": getattr(exc, "code", type(exc).__name__), + "detail": getattr(exc, "detail", str(exc)), + } + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + return 2 + print(json.dumps(result, ensure_ascii=False, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/binding/binding_signature_projection.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/binding/binding_signature_projection.yml new file mode 100644 index 00000000..95826c7d --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/binding/binding_signature_projection.yml @@ -0,0 +1,30 @@ +{ + "schema_version": "stage2_binding_signature_projection_registry.v1", + "registry_id": "S2_20_BINDING_SIGNATURE_PROJECTION_V1", + "source_schema_ref": "schemas/domain_verdict.schema.json#/$defs/normalized_claim_signature", + "source_field_allowlist": [ + "legal_basis_family", + "right_holder_refs", + "obligor_refs", + "performance_kind", + "object_refs", + "legal_effect", + "source_occurrence_refs" + ], + "target_field_order": [ + "claim_family", + "legal_effect", + "performance_kind", + "object_kind", + "party_role_pattern", + "source_obligation_kind", + "remedy_mode", + "registry_action", + "procedural_posture", + "special_statute_tags" + ], + "projection_rules": [], + "free_text_fallback_allowed": false, + "fuzzy_matching_allowed": false, + "registry_status": "PENDING_LEGAL_REVIEW" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-01_interest_delay.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-01_interest_delay.yml new file mode 100644 index 00000000..d47b74a6 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-01_interest_delay.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-01", + "descriptor_id": "CE-01-INTEREST-DELAY", + "description": "obligation-and-defendant event rate timeline", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-02_allocation_setoff_balance.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-02_allocation_setoff_balance.yml new file mode 100644 index 00000000..363522c0 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-02_allocation_setoff_balance.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-02", + "descriptor_id": "CE-02-ALLOCATION-SETOFF-BALANCE", + "description": "allocation, setoff, and remaining balance", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-03_limitation_deadline.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-03_limitation_deadline.yml new file mode 100644 index 00000000..28fe2016 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-03_limitation_deadline.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-03", + "descriptor_id": "CE-03-LIMITATION-DEADLINE", + "description": "limitation, exclusion, filing deadline, and urgency", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-04_valuation.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-04_valuation.yml new file mode 100644 index 00000000..d3902c54 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-04_valuation.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-04", + "descriptor_id": "CE-04-VALUATION", + "description": "use gain and object valuation", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-05_personal_injury.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-05_personal_injury.yml new file mode 100644 index 00000000..b8c530ec --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-05_personal_injury.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-05", + "descriptor_id": "CE-05-PERSONAL-INJURY", + "description": "lost earnings, treatment cost, and comparative fault", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-06_construction_defect.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-06_construction_defect.yml new file mode 100644 index 00000000..f7fd08ae --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-06_construction_defect.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-06", + "descriptor_id": "CE-06-CONSTRUCTION-DEFECT", + "description": "progress payment, extra work, defect repair, and reduction", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-07_lease_use_gain.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-07_lease_use_gain.yml new file mode 100644 index 00000000..2526d108 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-07_lease_use_gain.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-07", + "descriptor_id": "CE-07-LEASE-USE-GAIN", + "description": "lease deductions and use gain", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-08_inheritance_reserved_share.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-08_inheritance_reserved_share.yml new file mode 100644 index 00000000..3613a0bf --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-08_inheritance_reserved_share.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-08", + "descriptor_id": "CE-08-INHERITANCE-RESERVED-SHARE", + "description": "reserved-share value and temporal remedy branch", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-09_wage_severance.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-09_wage_severance.yml new file mode 100644 index 00000000..83659621 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-09_wage_severance.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-09", + "descriptor_id": "CE-09-WAGE-SEVERANCE", + "description": "wage, allowance, and severance", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-10_actio_insolvency_value.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-10_actio_insolvency_value.yml new file mode 100644 index 00000000..9c89f8b8 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-10_actio_insolvency_value.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-10", + "descriptor_id": "CE-10-ACTIO-INSOLVENCY-VALUE", + "description": "preserved claim, common security, and beneficiary/transferee caps", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-11_distribution_share_division.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-11_distribution_share_division.yml new file mode 100644 index 00000000..dea74455 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-11_distribution_share_division.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-11", + "descriptor_id": "CE-11-DISTRIBUTION-SHARE-DIVISION", + "description": "distribution, share, and division amount", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-12_insurance.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-12_insurance.yml new file mode 100644 index 00000000..b38023cf --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-12_insurance.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-12", + "descriptor_id": "CE-12-INSURANCE", + "description": "insured value, duplicate insurance, and deduction", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-13_court_value_cost.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-13_court_value_cost.yml new file mode 100644 index 00000000..6c6eaa97 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-13_court_value_cost.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-13", + "descriptor_id": "CE-13-COURT-VALUE-COST", + "description": "amount in controversy, filing fee, service cost, joinder, and incidental claim", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R1_ip_damage.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R1_ip_damage.yml new file mode 100644 index 00000000..9d249bc6 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R1_ip_damage.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-R1", + "descriptor_id": "CE-R1-IP-DAMAGE", + "description": "intellectual-property damage", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R2_org_liquidation.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R2_org_liquidation.yml new file mode 100644 index 00000000..4f632b15 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R2_org_liquidation.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-R2", + "descriptor_id": "CE-R2-ORG-LIQUIDATION", + "description": "organization and partnership liquidation/distribution", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R3_transport_maritime.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R3_transport_maritime.yml new file mode 100644 index 00000000..4ce87e03 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R3_transport_maritime.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-R3", + "descriptor_id": "CE-R3-TRANSPORT-MARITIME", + "description": "transport and maritime damage/liability limit", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R4_financial_instrument.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R4_financial_instrument.yml new file mode 100644 index 00000000..bed908d2 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/CE-R4_financial_instrument.yml @@ -0,0 +1,54 @@ +{ + "schema_version": "stage2.s2_20.calculation_descriptor.v1", + "calculator_id": "CE-R4", + "descriptor_id": "CE-R4-FINANCIAL-INSTRUMENT", + "description": "financial instrument, account, and settlement", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "formula_rows": [], + "operand_contract": { + "required_operands": [], + "optional_operands": [], + "unit_contracts": [], + "missing_operand_policy": "ISSUE_AND_NO_RESULT" + }, + "law_value_contract": { + "law_value_refs": [], + "literal_output_affecting_legal_numbers_forbidden": true, + "effective_interval_required": true, + "authority_release_binding_required": true + }, + "numeric_contract": { + "decimal_only": true, + "binary_float_forbidden": true, + "rounding_rule_required_per_formula": true, + "explicit_units_required": true + }, + "receipt_contract": { + "required_fields": [ + "calculation_receipt_id", + "calculator_id", + "formula_id", + "operand_refs", + "units", + "law_value_id", + "effective_interval", + "rounding_rule", + "period_rows", + "missing_operands", + "result_sha256" + ] + }, + "failure_codes": [ + "CALCULATION_DESCRIPTOR_NOT_APPROVED", + "CALCULATION_FORMULA_NOT_AVAILABLE", + "CALCULATION_OPERAND_MISSING", + "CALCULATION_LAW_VALUE_UNRESOLVED", + "CALCULATION_TEMPORAL_GAP", + "CALCULATION_UNIT_MISMATCH" + ], + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/calculation_activation_registry.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/calculation_activation_registry.yml new file mode 100644 index 00000000..f6ab2893 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/calculations/calculation_activation_registry.yml @@ -0,0 +1,168 @@ +{ + "schema_version": "stage2.s2_20.calculation_activation_registry.v1", + "registry_id": "S2-20-CALCULATION-ACTIVATION", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "state_enum": [ + "ACTIVATED", + "NOT_APPLICABLE", + "ACTIVATION_GAP", + "ACTIVATION_CONFLICT" + ], + "allowed_predicate_inputs": [ + "normalized_claim_signature", + "case_type_id", + "rule_branch_key", + "requested_metric" + ], + "allowed_predicate_operators": [ + "EQ", + "IN", + "ALL_OF", + "NONE_OF", + "EXISTS" + ], + "activation_rows": [], + "activation_row_contract": { + "required_fields": [ + "calculator_id", + "requested_metrics", + "equals" + ] + }, + "calculator_descriptors": [ + { + "calculator_id": "CE-01", + "descriptor_path": "registry/calculations/CE-01_interest_delay.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-02", + "descriptor_path": "registry/calculations/CE-02_allocation_setoff_balance.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-03", + "descriptor_path": "registry/calculations/CE-03_limitation_deadline.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-04", + "descriptor_path": "registry/calculations/CE-04_valuation.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-05", + "descriptor_path": "registry/calculations/CE-05_personal_injury.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-06", + "descriptor_path": "registry/calculations/CE-06_construction_defect.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-07", + "descriptor_path": "registry/calculations/CE-07_lease_use_gain.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-08", + "descriptor_path": "registry/calculations/CE-08_inheritance_reserved_share.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-09", + "descriptor_path": "registry/calculations/CE-09_wage_severance.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-10", + "descriptor_path": "registry/calculations/CE-10_actio_insolvency_value.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-11", + "descriptor_path": "registry/calculations/CE-11_distribution_share_division.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-12", + "descriptor_path": "registry/calculations/CE-12_insurance.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-13", + "descriptor_path": "registry/calculations/CE-13_court_value_cost.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-R1", + "descriptor_path": "registry/calculations/CE-R1_ip_damage.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-R2", + "descriptor_path": "registry/calculations/CE-R2_org_liquidation.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-R3", + "descriptor_path": "registry/calculations/CE-R3_transport_maritime.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + }, + { + "calculator_id": "CE-R4", + "descriptor_path": "registry/calculations/CE-R4_financial_instrument.yml", + "activation_status": "PENDING_LEGAL_CONTENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false + } + ], + "closure": { + "exactly_one_state_per_calculator_required": true, + "activated_requires_nonempty_approved_predicate": true, + "activated_requires_release_selected_descriptor_hash": true, + "activated_requires_receipt": true, + "zero_match_issue_code": "CALCULATION_ACTIVATION_GAP", + "multi_match_issue_code": "CALCULATION_ACTIVATION_CONFLICT", + "model_arithmetic_forbidden": true, + "missing_operand_default_zero_forbidden": true + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/corpus/requirement_fact_scope.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/corpus/requirement_fact_scope.yml new file mode 100644 index 00000000..07ce7d9a --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/corpus/requirement_fact_scope.yml @@ -0,0 +1,7 @@ +{ + "schema_version": "stage2_requirement_fact_scope_registry.v1", + "registry_id": "STAGE2_REQUIREMENT_FACT_SCOPE_V1", + "rows": [], + "registry_status": "PENDING_LEGAL_CONTENT", + "fuzzy_fallback_allowed": false +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/case_type_relief_rules.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/case_type_relief_rules.yml new file mode 100644 index 00000000..90067ab3 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/case_type_relief_rules.yml @@ -0,0 +1,43 @@ +{ + "schema_version": "stage2.s2_20.case_type_relief_rules.v1", + "registry_id": "S2-20-CASE-TYPE-RELIEF-RULES", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "source_catalog_registry_path": "manifest/case_type_registry.yml", + "source_rule_registry_path": "manifest/case_type_rule_registry.yml", + "source_catalog_expected_count": 137, + "branch_key_format": "case_type_id::sub_rule_id", + "branch_contract": { + "required_fields": [ + "case_type_id", + "sub_rule_id", + "predicate_id", + "structured_relief_slots", + "allowed_renderer_ids", + "markdown_source_path", + "markdown_source_sha256", + "corpus_scope_id", + "element_set_id", + "ce_13_branch_id", + "party_set_rule_id", + "review_policy_id", + "authority_refs", + "review_status" + ] + }, + "rule_branches": [], + "closure": { + "directory_scan_forbidden": true, + "filename_match_forbidden": true, + "nearest_rule_fallback_forbidden": true, + "generic_wording_fallback_forbidden": true, + "exactly_one_approved_branch_required_for_clean_draft": true, + "zero_match_issue_code": "RELIEF_RULE_BRANCH_ZERO_MATCH", + "multi_match_issue_code": "RELIEF_RULE_BRANCH_MULTI_MATCH", + "source_hash_mismatch_issue_code": "RELIEF_RULE_SOURCE_HASH_MISMATCH" + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/counter_performance_rules.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/counter_performance_rules.yml new file mode 100644 index 00000000..aac637dc --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/counter_performance_rules.yml @@ -0,0 +1,29 @@ +{ + "schema_version": "stage2.s2_20.drafting_rule_registry.v1", + "registry_id": "S2-20-COUNTER-PERFORMANCE-RULES", + "purpose": "simultaneous or reciprocal performance branches", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "rule_rows": [], + "row_contract": { + "required_fields": [ + "rule_id", + "predicate_id", + "effect", + "required_slots", + "renderer_constraints", + "authority_refs", + "review_status" + ] + }, + "closure": { + "exactly_one_approved_rule_required_when_activated": true, + "zero_match_issue_code": "S2-20-COUNTER-PERFORMANCE-RULES-ZERO-MATCH", + "multi_match_issue_code": "S2-20-COUNTER-PERFORMANCE-RULES-MULTI-MATCH", + "free_text_fallback_forbidden": true + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/forbidden_relief_rules.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/forbidden_relief_rules.yml new file mode 100644 index 00000000..91c5e43f --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/forbidden_relief_rules.yml @@ -0,0 +1,29 @@ +{ + "schema_version": "stage2.s2_20.drafting_rule_registry.v1", + "registry_id": "S2-20-FORBIDDEN-RELIEF-RULES", + "purpose": "prohibited and permitted corrective relief branches", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "rule_rows": [], + "row_contract": { + "required_fields": [ + "rule_id", + "predicate_id", + "effect", + "required_slots", + "renderer_constraints", + "authority_refs", + "review_status" + ] + }, + "closure": { + "exactly_one_approved_rule_required_when_activated": true, + "zero_match_issue_code": "S2-20-FORBIDDEN-RELIEF-RULES-ZERO-MATCH", + "multi_match_issue_code": "S2-20-FORBIDDEN-RELIEF-RULES-MULTI-MATCH", + "free_text_fallback_forbidden": true + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/procedural_declaration_rules.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/procedural_declaration_rules.yml new file mode 100644 index 00000000..d9268a22 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/drafting/procedural_declaration_rules.yml @@ -0,0 +1,29 @@ +{ + "schema_version": "stage2.s2_20.drafting_rule_registry.v1", + "registry_id": "S2-20-PROCEDURAL-DECLARATION-RULES", + "purpose": "service-based procedural declaration branches", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "rule_rows": [], + "row_contract": { + "required_fields": [ + "rule_id", + "predicate_id", + "effect", + "required_slots", + "renderer_constraints", + "authority_refs", + "review_status" + ] + }, + "closure": { + "exactly_one_approved_rule_required_when_activated": true, + "zero_match_issue_code": "S2-20-PROCEDURAL-DECLARATION-RULES-ZERO-MATCH", + "multi_match_issue_code": "S2-20-PROCEDURAL-DECLARATION-RULES-MULTI-MATCH", + "free_text_fallback_forbidden": true + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/party/party_set_rules.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/party/party_set_rules.yml new file mode 100644 index 00000000..5e84abf4 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/party/party_set_rules.yml @@ -0,0 +1,8 @@ +{ + "schema_version": "stage2_party_set_rules.v1", + "registry_id": "S2_20_PARTY_SET_RULES_V1", + "rules": [], + "exactly_one_match_required": true, + "fallback_allowed": false, + "registry_status": "PENDING_LEGAL_CONTENT" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/planning/option_disposition_policy.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/planning/option_disposition_policy.yml new file mode 100644 index 00000000..9092e7d0 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/planning/option_disposition_policy.yml @@ -0,0 +1,16 @@ +{ + "schema_version": "stage2_option_disposition_policy.v1", + "policy_id": "S2_20_OPTION_DISPOSITION_POLICY_V1", + "precedence_order": [ + "LEGAL_EXCLUSION", + "CLIENT_NO_SUE", + "PRECONDITION", + "COMPATIBILITY", + "LEGAL_DECISION" + ], + "rules": [], + "exhaustive_cross_product_required": true, + "exactly_one_match_required": true, + "hidden_default_allowed": false, + "policy_status": "PENDING_LEGAL_REVIEW" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/regression/finding_fixture_map.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/regression/finding_fixture_map.yml new file mode 100644 index 00000000..81eaa9c6 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/regression/finding_fixture_map.yml @@ -0,0 +1,32 @@ +{ + "schema_version": "stage2_finding_fixture_map.v1", + "registry_id": "S2-FINDING-FIXTURE-MAP-PENDING", + "status": "PENDING_SOURCE_FINDING_RECONCILIATION", + "execution_eligible": false, + "runtime_legal_source": false, + "required_fixture_case_ids": [ + "S20-F01-SINGLE-OPTION-GROUP", + "S20-F02-MIXED-PORTFOLIO", + "S20-F03-CLIENT-NO-SUE", + "S20-F04-CASE-RULE-EXACTNESS", + "S20-F05-ROW-KIND-BRANCH", + "S20-F06-PARTY-TITLE", + "S20-F07-CE01-INTEREST", + "S20-F08-CE03-LIMITATION", + "S20-F09-CE08-RESERVED-SHARE", + "S20-F10-ACTIO-ROUTE-CAP", + "S20-F11-CE13-COURT-VALUE", + "S20-F12-RETRIEVAL-BOUNDARY", + "S20-F13-CORPUS-INTEGRITY", + "S20-F14-EXHIBIT-LABEL", + "S20-F15-OPTION-SILENT-LOSS", + "S20-F16-PUBLISH-BARRIER", + "S20-F17-LEGACY-PATH" + ], + "mapping_rows": [], + "mapped_fixture_case_count": 0, + "required_fixture_case_count": 17, + "source_finding_families": ["discrepancy_report_1..4", "improvement_report_1..4", "improvement_merged", "eval_stage_2_optimal_update_strategy_v.3"], + "unresolved_reason_codes": ["SOURCE_FINDINGS_NOT_EXTRACTED","INVARIANT_MAPPING_NOT_REVIEWED","FIXTURE_CROSSWALK_NOT_SIGNED"], + "guards": {"runtime_routing_use_forbidden":true,"legal_proposition_source_use_forbidden":true,"unmapped_finding_silent_drop_forbidden":true,"pending_map_is_not_coverage_proof":true} +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/review/review_policy_registry.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/review/review_policy_registry.yml new file mode 100644 index 00000000..a1476a6c --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/review/review_policy_registry.yml @@ -0,0 +1,37 @@ +{ + "schema_version": "stage2.s2_20.review_policy_registry.v1", + "registry_id": "S2-20-REVIEW-POLICY", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "base_status_enum": [ + "CLEAN_DRAFT", + "REVIEW_DRAFT", + "WORKNOTE_ONLY" + ], + "trigger_contract": { + "sources": [ + "case_type_rule", + "calculation", + "authority", + "corpus", + "party", + "runtime_issue" + ], + "exactly_one_policy_result_required": true + }, + "policy_rows": [], + "default_unapproved_result": { + "drafting_permission": "WORKNOTE_ONLY", + "issue_code": "REVIEW_POLICY_NOT_APPROVED", + "ready_eligible": false + }, + "closure": { + "ready_requires_all_required_receipts": true, + "ready_requires_no_unresolved_blocking_issue": true, + "missing_policy_cannot_upgrade_permission": true + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/temporal/inheritance_reserved_share.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/temporal/inheritance_reserved_share.yml new file mode 100644 index 00000000..de805865 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/registry/temporal/inheritance_reserved_share.yml @@ -0,0 +1,31 @@ +{ + "schema_version": "stage2.s2_20.inheritance_reserved_share_temporal.v1", + "registry_id": "S2-20-INHERITANCE-RESERVED-SHARE-TEMPORAL", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "temporal_basis_field": "inheritance_opening_date", + "branch_rows": [], + "row_contract": { + "required_fields": [ + "temporal_rule_id", + "effective_from", + "effective_to", + "remedy_mode", + "renderer_id", + "interest_branch_id", + "authority_refs", + "review_status" + ], + "interval_overlap_forbidden": true, + "uncovered_interval_issue_code": "INHERITANCE_RESERVED_SHARE_TEMPORAL_GAP" + }, + "closure": { + "missing_date_issue_code": "INHERITANCE_OPENING_DATE_MISSING", + "guessed_effective_date_forbidden": true, + "fallback_to_current_law_forbidden": true + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/canary_rollback.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/canary_rollback.py new file mode 100644 index 00000000..99a67366 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/canary_rollback.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Produce a non-destructive rollback decision receipt for an admitted canary.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("admission", type=Path) + parser.add_argument("baseline", type=Path) + parser.add_argument("output", type=Path) + args = parser.parse_args() + admission_raw = args.admission.read_bytes() + baseline_raw = args.baseline.read_bytes() + admission = json.loads(admission_raw) + baseline = json.loads(baseline_raw) + rollback = admission.get("canary_status") != "PASS" + payload = { + "schema_version": "stage2.canary_rollback_receipt.v1", + "decision": "ROLLBACK_REQUIRED" if rollback else "KEEP_CANARY", + "admission_sha256": hashlib.sha256(admission_raw).hexdigest(), + "baseline_sha256": hashlib.sha256(baseline_raw).hexdigest(), + "rollback_target_release_sha256": baseline.get("release_sha256") if rollback else None, + "mutation_performed": False, + } + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(payload, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/canary_rollback.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/canary_rollback.txt new file mode 100644 index 00000000..99a67366 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/canary_rollback.txt @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Produce a non-destructive rollback decision receipt for an admitted canary.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("admission", type=Path) + parser.add_argument("baseline", type=Path) + parser.add_argument("output", type=Path) + args = parser.parse_args() + admission_raw = args.admission.read_bytes() + baseline_raw = args.baseline.read_bytes() + admission = json.loads(admission_raw) + baseline = json.loads(baseline_raw) + rollback = admission.get("canary_status") != "PASS" + payload = { + "schema_version": "stage2.canary_rollback_receipt.v1", + "decision": "ROLLBACK_REQUIRED" if rollback else "KEEP_CANARY", + "admission_sha256": hashlib.sha256(admission_raw).hexdigest(), + "baseline_sha256": hashlib.sha256(baseline_raw).hexdigest(), + "rollback_target_release_sha256": baseline.get("release_sha256") if rollback else None, + "mutation_performed": False, + } + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(payload, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/release_validator.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/release_validator.py new file mode 100644 index 00000000..c09ee20b --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/release_validator.py @@ -0,0 +1,581 @@ +#!/usr/bin/env python3 +"""Read-only validator for the canonical Stage-2 raw release closure. + +The report separates contract errors from honest pending legal/live evidence. +An offline PASS therefore never means that 137-case legal content, Weaviate, +Code Executor, host CAS or production admission has been approved. +""" + +from __future__ import annotations + +import argparse +import copy +import hashlib +import json +from pathlib import Path, PurePosixPath +from typing import Any + + +MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1" +PARENT_RELEASE_SCHEMA = "stage2_release.v2" +SHA256_LENGTH = 64 +EXPECTED_CASE_TYPE_IDS = tuple(f"CT-{index:03d}" for index in range(1, 138)) +EXPECTED_CATALOG_ROW_IDS = tuple(f"CAT-{index:03d}" for index in range(1, 138)) +EXPECTED_MANIFEST_DIGEST_CONTRACT = { + "algorithm_id": "STAGE2-MODULE-MANIFEST-DIGEST-V1", + "array_order": "PRESERVE_DECLARED_ORDER", + "canonicalization_algorithm_id": "STAGE2-CANONICAL-JSON-V1", + "digest_algorithm": "sha256", + "digest_scope": "ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_MANIFEST_DIGEST", + "encoding": "UTF-8", + "line_termination": "LF_ONE_TRAILING_NEWLINE", + "non_finite_numbers_allowed": False, + "object_key_order": "SORT_CODEPOINT", +} +EXPECTED_RELEASE_DIGEST_CONTRACT = { + "algorithm_id": "STAGE2-RELEASE-DIGEST-V1", + "array_order": "PRESERVE_DECLARED_ORDER", + "canonicalization_algorithm_id": "STAGE2-CANONICAL-JSON-V1", + "digest_algorithm": "sha256", + "digest_scope": "ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_RELEASE_DIGEST", + "encoding": "UTF-8", + "line_termination": "LF_ONE_TRAILING_NEWLINE", + "non_finite_numbers_allowed": False, + "object_key_order": "SORT_CODEPOINT", +} +FORBIDDEN_PARENT_MEMBERS = frozenset( + { + "manifest/module_manifest.json", + "manifest/stage2_release.json", + "deployment/stage2_code_executor_binding.yml", + "manifest/stage2_deterministic_admission_receipt.json", + "agent_scripts/Stage_2_S2_00.yml", + "runtime/s2_00_ingress.py", + "runtime/s2_00_ingress.txt", + "manifest/s2_00_inline_code_receipt.json", + "agent_scripts/Stage_2_S2_20.yml", + "runtime/s2_20_reduce.py", + "runtime/s2_20_reduce.txt", + "manifest/s2_20_inline_code_receipt.json", + "manifest/s2_10_release.json", + "manifest/s2_10_agent_receipt.json", + "manifest/s2_10_platform_adapter_receipt.json", + "manifest/s2_10_model_benchmark_receipt.json", + "manifest/s2_10_legal_review_receipt.json", + } +) + + +class ValidationInputError(ValueError): + """Raised when a validation input cannot be parsed deterministically.""" + + +def _reject_duplicate_pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise ValidationInputError(f"DUPLICATE_JSON_KEY:{key}") + result[key] = value + return result + + +def _load_json(path: Path) -> Any: + try: + return json.loads( + path.read_text(encoding="utf-8"), + object_pairs_hook=_reject_duplicate_pairs, + parse_constant=lambda token: (_ for _ in ()).throw( + ValidationInputError(f"NON_FINITE_JSON_NUMBER:{token}") + ), + ) + except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc: + raise ValidationInputError(f"JSON_READ_FAILED:{path.as_posix()}:{exc}") from exc + + +def _canonical_bytes(document: Any) -> bytes: + return ( + json.dumps( + document, + ensure_ascii=False, + sort_keys=True, + separators=(",", ":"), + allow_nan=False, + ) + + "\n" + ).encode("utf-8") + + +def _sha256(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest() + + +def _document_digest(document: dict[str, Any], digest_field: str) -> str: + payload = copy.deepcopy(document) + payload.pop(digest_field, None) + return _sha256(_canonical_bytes(payload)) + + +def _finding(code: str, path: str, detail: str) -> dict[str, str]: + return {"code": code, "detail": detail, "path": path} + + +def _is_sha256(value: Any) -> bool: + return ( + isinstance(value, str) + and len(value) == SHA256_LENGTH + and all(character in "0123456789abcdef" for character in value) + ) + + +def _contains_contract(value: Any, expected: dict[str, Any]) -> bool: + return isinstance(value, dict) and all(value.get(key) == item for key, item in expected.items()) + + +def _relative_path(value: Any) -> str | None: + if not isinstance(value, str) or not value or "\\" in value: + return None + pure = PurePosixPath(value) + if pure.is_absolute() or any(part in {"", ".", ".."} for part in pure.parts): + return None + return pure.as_posix() + + +def _legacy_path(relative: str) -> bool: + return any(part in {"v.0", "v.1", "v.2", "v.3"} for part in PurePosixPath(relative).parts) + + +def _physical(root: Path, relative: str) -> Path | None: + candidate = root / relative + if candidate.is_symlink(): + return None + try: + resolved = candidate.resolve(strict=True) + root_real = root.resolve(strict=True) + except FileNotFoundError: + return None + if root_real not in resolved.parents or not resolved.is_file(): + return None + return resolved + + +def _validate_module_manifest( + root: Path, + manifest: dict[str, Any], +) -> tuple[list[dict[str, str]], list[dict[str, str]], dict[str, int]]: + errors: list[dict[str, str]] = [] + pending: list[dict[str, str]] = [] + if manifest.get("schema_version") != MODULE_MANIFEST_SCHEMA: + errors.append(_finding("MODULE_SCHEMA_VERSION", "$/schema_version", repr(manifest.get("schema_version")))) + declared_digest = manifest.get("manifest_digest") + observed_digest = _document_digest(manifest, "manifest_digest") + if declared_digest != observed_digest: + errors.append(_finding("MANIFEST_DIGEST_MISMATCH", "$/manifest_digest", f"declared={declared_digest};observed={observed_digest}")) + if not _contains_contract(manifest.get("manifest_digest_contract"), EXPECTED_MANIFEST_DIGEST_CONTRACT): + errors.append(_finding("MANIFEST_DIGEST_CONTRACT_INVALID", "$/manifest_digest_contract", repr(manifest.get("manifest_digest_contract")))) + + rows = manifest.get("modules") + if not isinstance(rows, list): + return errors + [_finding("MODULES_REQUIRED", "$/modules", "canonical modules array missing")], pending, {"module_count": 0, "mirror_count": 0} + seen_ids: set[str] = set() + seen_paths: set[str] = set() + row_by_path: dict[str, dict[str, Any]] = {} + for index, row in enumerate(rows): + location = f"$/modules/{index}" + if not isinstance(row, dict): + errors.append(_finding("MODULE_ROW_INVALID", location, "object required")) + continue + module_id = row.get("module_id") + if not isinstance(module_id, str) or not module_id or module_id in seen_ids: + errors.append(_finding("MODULE_ID_INVALID_OR_DUPLICATE", f"{location}/module_id", repr(module_id))) + else: + seen_ids.add(module_id) + relative = _relative_path(row.get("path")) + if relative is None or relative in seen_paths: + errors.append(_finding("MODULE_PATH_INVALID_OR_DUPLICATE", f"{location}/path", repr(row.get("path")))) + continue + seen_paths.add(relative) + row_by_path[relative] = row + if relative in FORBIDDEN_PARENT_MEMBERS: + errors.append(_finding("NON_CYCLIC_PARENT_VIOLATION", f"{location}/path", relative)) + if _legacy_path(relative): + errors.append(_finding("LEGACY_STAGE2_DEPENDENCY", f"{location}/path", relative)) + physical = _physical(root, relative) + if physical is None: + errors.append(_finding("MODULE_MISSING_OR_SYMLINK", f"{location}/path", relative)) + continue + raw = physical.read_bytes() + observed = _sha256(raw) + if row.get("sha256") != observed: + errors.append(_finding("MODULE_HASH_MISMATCH", f"{location}/sha256", f"path={relative};observed={observed}")) + if row.get("size_bytes") != len(raw): + errors.append(_finding("MODULE_SIZE_MISMATCH", f"{location}/size_bytes", f"path={relative};observed={len(raw)}")) + + if relative.endswith(".py"): + mirror = relative[:-3] + ".txt" + mirror_physical = _physical(root, mirror) + if mirror_physical is None: + errors.append(_finding("PYTHON_DOCUMENTATION_MIRROR_MISSING", location, mirror)) + continue + mirror_raw = mirror_physical.read_bytes() + if raw != mirror_raw: + errors.append(_finding("PYTHON_DOCUMENTATION_MIRROR_MISMATCH", location, relative)) + expected_fields = { + "mirror_path": mirror, + "mirror_sha256": _sha256(mirror_raw), + "mirror_size_bytes": len(mirror_raw), + "mirror_byte_parity": True, + } + for key, expected in expected_fields.items(): + if row.get(key) != expected: + errors.append(_finding("MODULE_MIRROR_BINDING_MISMATCH", f"{location}/{key}", f"expected={expected!r};observed={row.get(key)!r}")) + + mirrors = manifest.get("documentation_mirrors") + if not isinstance(mirrors, list): + errors.append(_finding("DOCUMENTATION_MIRRORS_REQUIRED", "$/documentation_mirrors", "array required")) + mirrors = [] + seen_sources: set[str] = set() + for index, mirror in enumerate(mirrors): + location = f"$/documentation_mirrors/{index}" + if not isinstance(mirror, dict): + errors.append(_finding("DOCUMENTATION_MIRROR_ROW_INVALID", location, "object required")) + continue + source = _relative_path(mirror.get("source_path")) + target = _relative_path(mirror.get("mirror_path")) + if source is None or target is None or source in seen_sources: + errors.append(_finding("DOCUMENTATION_MIRROR_PATH_INVALID", location, repr(mirror))) + continue + seen_sources.add(source) + if source in FORBIDDEN_PARENT_MEMBERS or target in FORBIDDEN_PARENT_MEMBERS: + errors.append(_finding("NON_CYCLIC_MIRROR_VIOLATION", location, f"{source}->{target}")) + source_physical = _physical(root, source) + target_physical = _physical(root, target) + if source_physical is None or target_physical is None: + errors.append(_finding("DOCUMENTATION_MIRROR_MISSING", location, f"{source}->{target}")) + continue + source_raw = source_physical.read_bytes() + target_raw = target_physical.read_bytes() + expected = { + "source_sha256": _sha256(source_raw), + "source_size_bytes": len(source_raw), + "mirror_sha256": _sha256(target_raw), + "mirror_size_bytes": len(target_raw), + "byte_identical": True, + "runtime_import_allowed": False, + } + if source_raw != target_raw: + errors.append(_finding("DOCUMENTATION_MIRROR_BYTES_DIFFER", location, source)) + for key, expected_value in expected.items(): + if mirror.get(key) != expected_value: + errors.append(_finding("DOCUMENTATION_MIRROR_METADATA_MISMATCH", f"{location}/{key}", f"expected={expected_value!r};observed={mirror.get(key)!r}")) + row = row_by_path.get(source) + if row is None or row.get("mirror_path") != target: + errors.append(_finding("DOCUMENTATION_MIRROR_MODULE_ORPHAN", location, source)) + + py_sources = {path for path in row_by_path if path.endswith(".py")} + if seen_sources != py_sources: + errors.append(_finding("DOCUMENTATION_MIRROR_COVERAGE_MISMATCH", "$/documentation_mirrors", f"expected={sorted(py_sources)};observed={sorted(seen_sources)}")) + + dependency_graph: dict[str, tuple[str, ...]] = {} + for index, row in enumerate(rows): + if not isinstance(row, dict) or not isinstance(row.get("module_id"), str): + continue + dependencies = row.get("dependency_module_ids", []) + location = f"$/modules/{index}/dependency_module_ids" + if ( + not isinstance(dependencies, list) + or not all(isinstance(item, str) and item for item in dependencies) + or len(dependencies) != len(set(dependencies)) + ): + errors.append(_finding("MODULE_DEPENDENCY_LIST_INVALID", location, repr(dependencies))) + continue + missing = sorted(set(dependencies) - seen_ids) + if missing: + errors.append(_finding("MODULE_DEPENDENCY_MISSING", location, repr(missing))) + if row["module_id"] in dependencies: + errors.append(_finding("MODULE_SELF_DEPENDENCY", location, row["module_id"])) + dependency_graph[row["module_id"]] = tuple(dependencies) + + visiting: set[str] = set() + visited: set[str] = set() + + def visit(module_id: str, chain: tuple[str, ...]) -> None: + if module_id in visiting: + errors.append(_finding("MODULE_DEPENDENCY_CYCLE", "$/modules", "->".join(chain + (module_id,)))) + return + if module_id in visited: + return + visiting.add(module_id) + for dependency in dependency_graph.get(module_id, ()): + if dependency in dependency_graph: + visit(dependency, chain + (module_id,)) + visiting.remove(module_id) + visited.add(module_id) + + for module_id in sorted(dependency_graph): + visit(module_id, ()) + summary = manifest.get("closure_summary") + if not isinstance(summary, dict): + errors.append(_finding("CLOSURE_SUMMARY_REQUIRED", "$/closure_summary", "object required")) + else: + for key, expected in ( + ("module_count", len(rows)), + ("documentation_mirror_count", len(mirrors)), + ): + if summary.get(key) != expected: + errors.append(_finding("CLOSURE_SUMMARY_COUNT_MISMATCH", f"$/closure_summary/{key}", f"expected={expected};observed={summary.get(key)}")) + for key in ( + "executable_agent_hash_included", + "executor_binding_hash_included", + "inline_runtime_code_hash_included", + "self_hash_included", + "stage2_release_hash_included", + ): + if key in summary and summary.get(key) is not False: + errors.append(_finding("NON_CYCLIC_SUMMARY_FLAG_INVALID", f"$/closure_summary/{key}", repr(summary.get(key)))) + return errors, pending, {"module_count": len(rows), "mirror_count": len(mirrors)} + + +def _walk_parent_refs( + root: Path, + value: Any, + errors: list[dict[str, str]], + location: str = "$", +) -> None: + if isinstance(value, list): + for index, item in enumerate(value): + _walk_parent_refs(root, item, errors, f"{location}/{index}") + return + if not isinstance(value, dict): + return + relative = _relative_path(value.get("path")) + if relative is not None and "<" not in relative: + if relative == "manifest/module_manifest.json": + pass + elif relative in FORBIDDEN_PARENT_MEMBERS: + if "sha256" in value or value.get("binding_status") == "BOUND": + errors.append(_finding("NON_CYCLIC_PARENT_HASH_REFERENCE", location, relative)) + elif "sha256" in value and _is_sha256(value.get("sha256")): + physical = _physical(root, relative) + if physical is not None: + observed = _sha256(physical.read_bytes()) + if value["sha256"] != observed: + errors.append(_finding("PARENT_ASSET_HASH_MISMATCH", f"{location}/sha256", f"path={relative};observed={observed}")) + for key, item in value.items(): + _walk_parent_refs(root, item, errors, f"{location}/{key}") + + +def _validate_parent_release( + root: Path, + release: dict[str, Any], + manifest_raw: bytes, +) -> tuple[list[dict[str, str]], list[dict[str, str]]]: + errors: list[dict[str, str]] = [] + pending: list[dict[str, str]] = [] + if release.get("schema_version") != PARENT_RELEASE_SCHEMA: + errors.append(_finding("PARENT_RELEASE_SCHEMA_VERSION", "$/schema_version", repr(release.get("schema_version")))) + if not _contains_contract(release.get("release_digest_contract"), EXPECTED_RELEASE_DIGEST_CONTRACT): + errors.append(_finding("PARENT_RELEASE_DIGEST_CONTRACT_INVALID", "$/release_digest_contract", repr(release.get("release_digest_contract")))) + declared = release.get("release_digest") + observed = _document_digest(release, "release_digest") + if declared != observed: + errors.append(_finding("PARENT_RELEASE_DIGEST_MISMATCH", "$/release_digest", f"declared={declared};observed={observed}")) + module_ref = release.get("module_manifest_ref") + if not isinstance(module_ref, dict) or module_ref.get("path") != "manifest/module_manifest.json": + errors.append(_finding("PARENT_MODULE_REF_INVALID", "$/module_manifest_ref", repr(module_ref))) + else: + expected_hash = _sha256(manifest_raw) + if module_ref.get("sha256") != expected_hash: + errors.append(_finding("PARENT_MODULE_REF_HASH_MISMATCH", "$/module_manifest_ref/sha256", f"expected={expected_hash};observed={module_ref.get('sha256')}")) + executor_ref = release.get("executor_binding_ref") + if ( + not isinstance(executor_ref, dict) + or executor_ref.get("path") != "deployment/stage2_code_executor_binding.yml" + or executor_ref.get("schema_id") != "stage2_code_executor_binding.v3" + or "sha256" in executor_ref + or executor_ref.get("binding_status") == "BOUND" + ): + errors.append(_finding("PARENT_EXECUTOR_TRUST_ROOT_REF_INVALID", "$/executor_binding_ref", repr(executor_ref))) + _walk_parent_refs(root, release, errors) + + status = release.get("release_status") + authorization = release.get("authorization_status") + signature = release.get("signature") + if status == "ADMITTED": + if authorization not in {"CANARY_ADMITTED", "PRODUCTION_ADMITTED"}: + errors.append(_finding("ADMITTED_RELEASE_AUTHORIZATION_INVALID", "$/authorization_status", repr(authorization))) + if signature == "PENDING_SEQUENTIAL_BIND" or not isinstance(signature, str) or len(signature) < 32: + errors.append(_finding("ADMITTED_RELEASE_SIGNATURE_INVALID", "$/signature", repr(signature))) + else: + pending.append(_finding("PARENT_RELEASE_NOT_ADMITTED", "$/release_status", str(status))) + return errors, pending + + +def _nested_pending(value: Any) -> bool: + if isinstance(value, str): + return "PENDING" in value or value in {"UNVERIFIED", "NOT_IMPLEMENTED", "DEV_UNAVAILABLE"} + if isinstance(value, list): + return any(_nested_pending(item) for item in value) + if isinstance(value, dict): + return any(_nested_pending(item) for item in value.values()) + return False + + +def _contains_ready_claim(value: Any) -> bool: + if isinstance(value, str): + return value in {"FULL_137_PRODUCTION_READY", "PRODUCTION_READY", "ADMITTED"} + if isinstance(value, list): + return any(_contains_ready_claim(item) for item in value) + if isinstance(value, dict): + return any(_contains_ready_claim(item) for item in value.values()) + return False + + +def _validate_137_coverage( + root: Path, + case_registry: dict[str, Any], + rule_registry: dict[str, Any], + coverage: dict[str, Any], +) -> tuple[list[dict[str, str]], list[dict[str, str]], dict[str, int]]: + errors: list[dict[str, str]] = [] + pending: list[dict[str, str]] = [] + case_rows = case_registry.get("rows") + rule_rows = rule_registry.get("rows") + coverage_rows = coverage.get("coverage_rows") + if not isinstance(case_rows, list) or len(case_rows) != 137: + errors.append(_finding("CASE_TYPE_REGISTRY_EXACT_137_REQUIRED", "case_type_registry:/rows", f"observed={len(case_rows) if isinstance(case_rows, list) else 'invalid'}")) + case_rows = [] + if not isinstance(rule_rows, list) or len(rule_rows) != 137: + errors.append(_finding("CASE_TYPE_RULE_REGISTRY_EXACT_137_REQUIRED", "case_type_rule_registry:/rows", f"observed={len(rule_rows) if isinstance(rule_rows, list) else 'invalid'}")) + rule_rows = [] + if not isinstance(coverage_rows, list) or len(coverage_rows) != 137: + errors.append(_finding("CASE_TYPE_COVERAGE_EXACT_137_REQUIRED", "case_type_coverage:/coverage_rows", f"observed={len(coverage_rows) if isinstance(coverage_rows, list) else 'invalid'}")) + coverage_rows = [] + + observed_case_ids = tuple(row.get("case_type_id") for row in case_rows if isinstance(row, dict)) + observed_catalog_ids = tuple(row.get("catalog_row_id") for row in case_rows if isinstance(row, dict)) + declared_catalog_ids = tuple(case_registry.get("catalog_row_ids", ())) + observed_rule_ids = tuple(row.get("case_type_id") for row in rule_rows if isinstance(row, dict)) + observed_coverage_ids = tuple(row.get("case_type_id") for row in coverage_rows if isinstance(row, dict)) + for code, path, observed, expected in ( + ("CASE_TYPE_ID_ORDER_MISMATCH", "case_type_registry:/rows", observed_case_ids, EXPECTED_CASE_TYPE_IDS), + ("CATALOG_ROW_ID_ORDER_MISMATCH", "case_type_registry:/rows", observed_catalog_ids, EXPECTED_CATALOG_ROW_IDS), + ("DECLARED_CATALOG_ROW_ID_ORDER_MISMATCH", "case_type_registry:/catalog_row_ids", declared_catalog_ids, EXPECTED_CATALOG_ROW_IDS), + ("RULE_CASE_TYPE_ID_ORDER_MISMATCH", "case_type_rule_registry:/rows", observed_rule_ids, EXPECTED_CASE_TYPE_IDS), + ("COVERAGE_CASE_TYPE_ID_ORDER_MISMATCH", "case_type_coverage:/coverage_rows", observed_coverage_ids, EXPECTED_CASE_TYPE_IDS), + ): + if observed != expected: + errors.append(_finding(code, path, "exact CT/CAT ordinal sequence required")) + + case_path = _physical(root, "manifest/case_type_registry.yml") + if case_path is None: + errors.append(_finding("CASE_TYPE_REGISTRY_PHYSICAL_FILE_REQUIRED", "case_type_registry:$", "manifest/case_type_registry.yml")) + case_raw = _canonical_bytes(case_registry) + else: + case_raw = case_path.read_bytes() + case_ref = rule_registry.get("case_type_registry_ref") + case_hash = rule_registry.get("case_type_registry_sha256") + if case_ref != "manifest/case_type_registry.yml" or case_hash != _sha256(case_raw): + errors.append(_finding("RULE_REGISTRY_CASE_TYPE_REF_MISMATCH", "case_type_rule_registry:/case_type_registry_sha256", f"expected={_sha256(case_raw)};observed={case_hash}")) + + coverage_dependency = coverage.get("registry_dependency") + if isinstance(coverage_dependency, dict): + declared = coverage_dependency.get("sha256") + if declared is not None and declared != _sha256(case_raw): + errors.append(_finding("COVERAGE_REGISTRY_HASH_MISMATCH", "case_type_coverage:/registry_dependency/sha256", f"expected={_sha256(case_raw)};observed={declared}")) + else: + errors.append(_finding("COVERAGE_REGISTRY_DEPENDENCY_REQUIRED", "case_type_coverage:/registry_dependency", "object required")) + + rule_doc_count = 0 + for case_type_id in EXPECTED_CASE_TYPE_IDS: + if _physical(root, f"rules/relief/{case_type_id}.md") is not None: + rule_doc_count += 1 + pending_legal = ( + rule_doc_count != 137 + or _nested_pending(case_registry) + or _nested_pending(rule_registry) + or _nested_pending(coverage) + ) + if pending_legal: + if coverage.get("release_eligible") is True or _contains_ready_claim(coverage): + errors.append(_finding("DISHONEST_FULL_137_READY_CLAIM", "case_type_coverage:$", f"rule_document_count={rule_doc_count};pending_contract=true")) + pending.append(_finding("FULL_137_LEGAL_COVERAGE_PENDING", "case_type_coverage:$", f"rule_document_count={rule_doc_count};legal registry/sign-off not fully admitted")) + return errors, pending, { + "case_type_count": len(case_rows), + "case_type_rule_row_count": len(rule_rows), + "coverage_row_count": len(coverage_rows), + "relief_rule_document_count": rule_doc_count, + } + + +def validate_package( + root: Path, + manifest_path: Path, + release_path: Path, + case_registry_path: Path, + rule_registry_path: Path, + coverage_path: Path, +) -> dict[str, Any]: + manifest = _load_json(manifest_path) + release = _load_json(release_path) + case_registry = _load_json(case_registry_path) + rule_registry = _load_json(rule_registry_path) + coverage = _load_json(coverage_path) + if not all(isinstance(row, dict) for row in (manifest, release, case_registry, rule_registry, coverage)): + raise ValidationInputError("ALL_RELEASE_INPUTS_MUST_BE_OBJECTS") + errors, pending, counts = _validate_module_manifest(root, manifest) + parent_errors, parent_pending = _validate_parent_release(root, release, manifest_path.read_bytes()) + coverage_errors, coverage_pending, coverage_counts = _validate_137_coverage(root, case_registry, rule_registry, coverage) + errors.extend(parent_errors) + errors.extend(coverage_errors) + pending.extend(parent_pending) + pending.extend(coverage_pending) + if ( + any(row["code"] == "FULL_137_LEGAL_COVERAGE_PENDING" for row in coverage_pending) + and ( + release.get("release_class") == "PRODUCTION_RELEASE" + or (isinstance(release.get("bundle"), dict) and release["bundle"].get("mode") == "PRODUCTION") + ) + ): + errors.append(_finding("PRODUCTION_RELEASE_WITH_PENDING_137_COVERAGE", "stage2_release:$", "full-137 legal/corpus coverage is not admitted")) + counts.update(coverage_counts) + errors = sorted(errors, key=lambda row: (row["code"], row["path"], row["detail"])) + pending = sorted(pending, key=lambda row: (row["code"], row["path"], row["detail"])) + return { + "counts": dict(sorted(counts.items())), + "errors": errors, + "pending": pending, + "schema_version": "stage2_release_validation.v1", + "status": "PASS" if not errors else "FAIL", + } + + +def validate(root: Path, manifest: dict[str, object]) -> list[str]: + """Compatibility wrapper for callers that only validate module rows.""" + + errors, _, _ = _validate_module_manifest(root, manifest) + return [f"{row['code']}:{row['path']}:{row['detail']}" for row in errors] + + +def main() -> int: + parser = argparse.ArgumentParser(description="Validate canonical Stage-2 raw release closure") + parser.add_argument("root", type=Path) + parser.add_argument("manifest", type=Path, nargs="?") + parser.add_argument("--release", type=Path) + parser.add_argument("--case-registry", type=Path) + parser.add_argument("--rule-registry", type=Path) + parser.add_argument("--coverage", type=Path) + args = parser.parse_args() + report = validate_package( + args.root, + args.manifest or args.root / "manifest/module_manifest.json", + args.release or args.root / "manifest/stage2_release.json", + args.case_registry or args.root / "manifest/case_type_registry.yml", + args.rule_registry or args.root / "manifest/case_type_rule_registry.yml", + args.coverage or args.root / "manifest/case_type_coverage.json", + ) + print(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":"))) + return 0 if report["status"] == "PASS" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/release_validator.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/release_validator.txt new file mode 100644 index 00000000..c09ee20b --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/release_ops/release_validator.txt @@ -0,0 +1,581 @@ +#!/usr/bin/env python3 +"""Read-only validator for the canonical Stage-2 raw release closure. + +The report separates contract errors from honest pending legal/live evidence. +An offline PASS therefore never means that 137-case legal content, Weaviate, +Code Executor, host CAS or production admission has been approved. +""" + +from __future__ import annotations + +import argparse +import copy +import hashlib +import json +from pathlib import Path, PurePosixPath +from typing import Any + + +MODULE_MANIFEST_SCHEMA = "stage2_module_manifest.v1" +PARENT_RELEASE_SCHEMA = "stage2_release.v2" +SHA256_LENGTH = 64 +EXPECTED_CASE_TYPE_IDS = tuple(f"CT-{index:03d}" for index in range(1, 138)) +EXPECTED_CATALOG_ROW_IDS = tuple(f"CAT-{index:03d}" for index in range(1, 138)) +EXPECTED_MANIFEST_DIGEST_CONTRACT = { + "algorithm_id": "STAGE2-MODULE-MANIFEST-DIGEST-V1", + "array_order": "PRESERVE_DECLARED_ORDER", + "canonicalization_algorithm_id": "STAGE2-CANONICAL-JSON-V1", + "digest_algorithm": "sha256", + "digest_scope": "ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_MANIFEST_DIGEST", + "encoding": "UTF-8", + "line_termination": "LF_ONE_TRAILING_NEWLINE", + "non_finite_numbers_allowed": False, + "object_key_order": "SORT_CODEPOINT", +} +EXPECTED_RELEASE_DIGEST_CONTRACT = { + "algorithm_id": "STAGE2-RELEASE-DIGEST-V1", + "array_order": "PRESERVE_DECLARED_ORDER", + "canonicalization_algorithm_id": "STAGE2-CANONICAL-JSON-V1", + "digest_algorithm": "sha256", + "digest_scope": "ENTIRE_DOCUMENT_AFTER_REMOVING_TOP_LEVEL_RELEASE_DIGEST", + "encoding": "UTF-8", + "line_termination": "LF_ONE_TRAILING_NEWLINE", + "non_finite_numbers_allowed": False, + "object_key_order": "SORT_CODEPOINT", +} +FORBIDDEN_PARENT_MEMBERS = frozenset( + { + "manifest/module_manifest.json", + "manifest/stage2_release.json", + "deployment/stage2_code_executor_binding.yml", + "manifest/stage2_deterministic_admission_receipt.json", + "agent_scripts/Stage_2_S2_00.yml", + "runtime/s2_00_ingress.py", + "runtime/s2_00_ingress.txt", + "manifest/s2_00_inline_code_receipt.json", + "agent_scripts/Stage_2_S2_20.yml", + "runtime/s2_20_reduce.py", + "runtime/s2_20_reduce.txt", + "manifest/s2_20_inline_code_receipt.json", + "manifest/s2_10_release.json", + "manifest/s2_10_agent_receipt.json", + "manifest/s2_10_platform_adapter_receipt.json", + "manifest/s2_10_model_benchmark_receipt.json", + "manifest/s2_10_legal_review_receipt.json", + } +) + + +class ValidationInputError(ValueError): + """Raised when a validation input cannot be parsed deterministically.""" + + +def _reject_duplicate_pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise ValidationInputError(f"DUPLICATE_JSON_KEY:{key}") + result[key] = value + return result + + +def _load_json(path: Path) -> Any: + try: + return json.loads( + path.read_text(encoding="utf-8"), + object_pairs_hook=_reject_duplicate_pairs, + parse_constant=lambda token: (_ for _ in ()).throw( + ValidationInputError(f"NON_FINITE_JSON_NUMBER:{token}") + ), + ) + except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc: + raise ValidationInputError(f"JSON_READ_FAILED:{path.as_posix()}:{exc}") from exc + + +def _canonical_bytes(document: Any) -> bytes: + return ( + json.dumps( + document, + ensure_ascii=False, + sort_keys=True, + separators=(",", ":"), + allow_nan=False, + ) + + "\n" + ).encode("utf-8") + + +def _sha256(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest() + + +def _document_digest(document: dict[str, Any], digest_field: str) -> str: + payload = copy.deepcopy(document) + payload.pop(digest_field, None) + return _sha256(_canonical_bytes(payload)) + + +def _finding(code: str, path: str, detail: str) -> dict[str, str]: + return {"code": code, "detail": detail, "path": path} + + +def _is_sha256(value: Any) -> bool: + return ( + isinstance(value, str) + and len(value) == SHA256_LENGTH + and all(character in "0123456789abcdef" for character in value) + ) + + +def _contains_contract(value: Any, expected: dict[str, Any]) -> bool: + return isinstance(value, dict) and all(value.get(key) == item for key, item in expected.items()) + + +def _relative_path(value: Any) -> str | None: + if not isinstance(value, str) or not value or "\\" in value: + return None + pure = PurePosixPath(value) + if pure.is_absolute() or any(part in {"", ".", ".."} for part in pure.parts): + return None + return pure.as_posix() + + +def _legacy_path(relative: str) -> bool: + return any(part in {"v.0", "v.1", "v.2", "v.3"} for part in PurePosixPath(relative).parts) + + +def _physical(root: Path, relative: str) -> Path | None: + candidate = root / relative + if candidate.is_symlink(): + return None + try: + resolved = candidate.resolve(strict=True) + root_real = root.resolve(strict=True) + except FileNotFoundError: + return None + if root_real not in resolved.parents or not resolved.is_file(): + return None + return resolved + + +def _validate_module_manifest( + root: Path, + manifest: dict[str, Any], +) -> tuple[list[dict[str, str]], list[dict[str, str]], dict[str, int]]: + errors: list[dict[str, str]] = [] + pending: list[dict[str, str]] = [] + if manifest.get("schema_version") != MODULE_MANIFEST_SCHEMA: + errors.append(_finding("MODULE_SCHEMA_VERSION", "$/schema_version", repr(manifest.get("schema_version")))) + declared_digest = manifest.get("manifest_digest") + observed_digest = _document_digest(manifest, "manifest_digest") + if declared_digest != observed_digest: + errors.append(_finding("MANIFEST_DIGEST_MISMATCH", "$/manifest_digest", f"declared={declared_digest};observed={observed_digest}")) + if not _contains_contract(manifest.get("manifest_digest_contract"), EXPECTED_MANIFEST_DIGEST_CONTRACT): + errors.append(_finding("MANIFEST_DIGEST_CONTRACT_INVALID", "$/manifest_digest_contract", repr(manifest.get("manifest_digest_contract")))) + + rows = manifest.get("modules") + if not isinstance(rows, list): + return errors + [_finding("MODULES_REQUIRED", "$/modules", "canonical modules array missing")], pending, {"module_count": 0, "mirror_count": 0} + seen_ids: set[str] = set() + seen_paths: set[str] = set() + row_by_path: dict[str, dict[str, Any]] = {} + for index, row in enumerate(rows): + location = f"$/modules/{index}" + if not isinstance(row, dict): + errors.append(_finding("MODULE_ROW_INVALID", location, "object required")) + continue + module_id = row.get("module_id") + if not isinstance(module_id, str) or not module_id or module_id in seen_ids: + errors.append(_finding("MODULE_ID_INVALID_OR_DUPLICATE", f"{location}/module_id", repr(module_id))) + else: + seen_ids.add(module_id) + relative = _relative_path(row.get("path")) + if relative is None or relative in seen_paths: + errors.append(_finding("MODULE_PATH_INVALID_OR_DUPLICATE", f"{location}/path", repr(row.get("path")))) + continue + seen_paths.add(relative) + row_by_path[relative] = row + if relative in FORBIDDEN_PARENT_MEMBERS: + errors.append(_finding("NON_CYCLIC_PARENT_VIOLATION", f"{location}/path", relative)) + if _legacy_path(relative): + errors.append(_finding("LEGACY_STAGE2_DEPENDENCY", f"{location}/path", relative)) + physical = _physical(root, relative) + if physical is None: + errors.append(_finding("MODULE_MISSING_OR_SYMLINK", f"{location}/path", relative)) + continue + raw = physical.read_bytes() + observed = _sha256(raw) + if row.get("sha256") != observed: + errors.append(_finding("MODULE_HASH_MISMATCH", f"{location}/sha256", f"path={relative};observed={observed}")) + if row.get("size_bytes") != len(raw): + errors.append(_finding("MODULE_SIZE_MISMATCH", f"{location}/size_bytes", f"path={relative};observed={len(raw)}")) + + if relative.endswith(".py"): + mirror = relative[:-3] + ".txt" + mirror_physical = _physical(root, mirror) + if mirror_physical is None: + errors.append(_finding("PYTHON_DOCUMENTATION_MIRROR_MISSING", location, mirror)) + continue + mirror_raw = mirror_physical.read_bytes() + if raw != mirror_raw: + errors.append(_finding("PYTHON_DOCUMENTATION_MIRROR_MISMATCH", location, relative)) + expected_fields = { + "mirror_path": mirror, + "mirror_sha256": _sha256(mirror_raw), + "mirror_size_bytes": len(mirror_raw), + "mirror_byte_parity": True, + } + for key, expected in expected_fields.items(): + if row.get(key) != expected: + errors.append(_finding("MODULE_MIRROR_BINDING_MISMATCH", f"{location}/{key}", f"expected={expected!r};observed={row.get(key)!r}")) + + mirrors = manifest.get("documentation_mirrors") + if not isinstance(mirrors, list): + errors.append(_finding("DOCUMENTATION_MIRRORS_REQUIRED", "$/documentation_mirrors", "array required")) + mirrors = [] + seen_sources: set[str] = set() + for index, mirror in enumerate(mirrors): + location = f"$/documentation_mirrors/{index}" + if not isinstance(mirror, dict): + errors.append(_finding("DOCUMENTATION_MIRROR_ROW_INVALID", location, "object required")) + continue + source = _relative_path(mirror.get("source_path")) + target = _relative_path(mirror.get("mirror_path")) + if source is None or target is None or source in seen_sources: + errors.append(_finding("DOCUMENTATION_MIRROR_PATH_INVALID", location, repr(mirror))) + continue + seen_sources.add(source) + if source in FORBIDDEN_PARENT_MEMBERS or target in FORBIDDEN_PARENT_MEMBERS: + errors.append(_finding("NON_CYCLIC_MIRROR_VIOLATION", location, f"{source}->{target}")) + source_physical = _physical(root, source) + target_physical = _physical(root, target) + if source_physical is None or target_physical is None: + errors.append(_finding("DOCUMENTATION_MIRROR_MISSING", location, f"{source}->{target}")) + continue + source_raw = source_physical.read_bytes() + target_raw = target_physical.read_bytes() + expected = { + "source_sha256": _sha256(source_raw), + "source_size_bytes": len(source_raw), + "mirror_sha256": _sha256(target_raw), + "mirror_size_bytes": len(target_raw), + "byte_identical": True, + "runtime_import_allowed": False, + } + if source_raw != target_raw: + errors.append(_finding("DOCUMENTATION_MIRROR_BYTES_DIFFER", location, source)) + for key, expected_value in expected.items(): + if mirror.get(key) != expected_value: + errors.append(_finding("DOCUMENTATION_MIRROR_METADATA_MISMATCH", f"{location}/{key}", f"expected={expected_value!r};observed={mirror.get(key)!r}")) + row = row_by_path.get(source) + if row is None or row.get("mirror_path") != target: + errors.append(_finding("DOCUMENTATION_MIRROR_MODULE_ORPHAN", location, source)) + + py_sources = {path for path in row_by_path if path.endswith(".py")} + if seen_sources != py_sources: + errors.append(_finding("DOCUMENTATION_MIRROR_COVERAGE_MISMATCH", "$/documentation_mirrors", f"expected={sorted(py_sources)};observed={sorted(seen_sources)}")) + + dependency_graph: dict[str, tuple[str, ...]] = {} + for index, row in enumerate(rows): + if not isinstance(row, dict) or not isinstance(row.get("module_id"), str): + continue + dependencies = row.get("dependency_module_ids", []) + location = f"$/modules/{index}/dependency_module_ids" + if ( + not isinstance(dependencies, list) + or not all(isinstance(item, str) and item for item in dependencies) + or len(dependencies) != len(set(dependencies)) + ): + errors.append(_finding("MODULE_DEPENDENCY_LIST_INVALID", location, repr(dependencies))) + continue + missing = sorted(set(dependencies) - seen_ids) + if missing: + errors.append(_finding("MODULE_DEPENDENCY_MISSING", location, repr(missing))) + if row["module_id"] in dependencies: + errors.append(_finding("MODULE_SELF_DEPENDENCY", location, row["module_id"])) + dependency_graph[row["module_id"]] = tuple(dependencies) + + visiting: set[str] = set() + visited: set[str] = set() + + def visit(module_id: str, chain: tuple[str, ...]) -> None: + if module_id in visiting: + errors.append(_finding("MODULE_DEPENDENCY_CYCLE", "$/modules", "->".join(chain + (module_id,)))) + return + if module_id in visited: + return + visiting.add(module_id) + for dependency in dependency_graph.get(module_id, ()): + if dependency in dependency_graph: + visit(dependency, chain + (module_id,)) + visiting.remove(module_id) + visited.add(module_id) + + for module_id in sorted(dependency_graph): + visit(module_id, ()) + summary = manifest.get("closure_summary") + if not isinstance(summary, dict): + errors.append(_finding("CLOSURE_SUMMARY_REQUIRED", "$/closure_summary", "object required")) + else: + for key, expected in ( + ("module_count", len(rows)), + ("documentation_mirror_count", len(mirrors)), + ): + if summary.get(key) != expected: + errors.append(_finding("CLOSURE_SUMMARY_COUNT_MISMATCH", f"$/closure_summary/{key}", f"expected={expected};observed={summary.get(key)}")) + for key in ( + "executable_agent_hash_included", + "executor_binding_hash_included", + "inline_runtime_code_hash_included", + "self_hash_included", + "stage2_release_hash_included", + ): + if key in summary and summary.get(key) is not False: + errors.append(_finding("NON_CYCLIC_SUMMARY_FLAG_INVALID", f"$/closure_summary/{key}", repr(summary.get(key)))) + return errors, pending, {"module_count": len(rows), "mirror_count": len(mirrors)} + + +def _walk_parent_refs( + root: Path, + value: Any, + errors: list[dict[str, str]], + location: str = "$", +) -> None: + if isinstance(value, list): + for index, item in enumerate(value): + _walk_parent_refs(root, item, errors, f"{location}/{index}") + return + if not isinstance(value, dict): + return + relative = _relative_path(value.get("path")) + if relative is not None and "<" not in relative: + if relative == "manifest/module_manifest.json": + pass + elif relative in FORBIDDEN_PARENT_MEMBERS: + if "sha256" in value or value.get("binding_status") == "BOUND": + errors.append(_finding("NON_CYCLIC_PARENT_HASH_REFERENCE", location, relative)) + elif "sha256" in value and _is_sha256(value.get("sha256")): + physical = _physical(root, relative) + if physical is not None: + observed = _sha256(physical.read_bytes()) + if value["sha256"] != observed: + errors.append(_finding("PARENT_ASSET_HASH_MISMATCH", f"{location}/sha256", f"path={relative};observed={observed}")) + for key, item in value.items(): + _walk_parent_refs(root, item, errors, f"{location}/{key}") + + +def _validate_parent_release( + root: Path, + release: dict[str, Any], + manifest_raw: bytes, +) -> tuple[list[dict[str, str]], list[dict[str, str]]]: + errors: list[dict[str, str]] = [] + pending: list[dict[str, str]] = [] + if release.get("schema_version") != PARENT_RELEASE_SCHEMA: + errors.append(_finding("PARENT_RELEASE_SCHEMA_VERSION", "$/schema_version", repr(release.get("schema_version")))) + if not _contains_contract(release.get("release_digest_contract"), EXPECTED_RELEASE_DIGEST_CONTRACT): + errors.append(_finding("PARENT_RELEASE_DIGEST_CONTRACT_INVALID", "$/release_digest_contract", repr(release.get("release_digest_contract")))) + declared = release.get("release_digest") + observed = _document_digest(release, "release_digest") + if declared != observed: + errors.append(_finding("PARENT_RELEASE_DIGEST_MISMATCH", "$/release_digest", f"declared={declared};observed={observed}")) + module_ref = release.get("module_manifest_ref") + if not isinstance(module_ref, dict) or module_ref.get("path") != "manifest/module_manifest.json": + errors.append(_finding("PARENT_MODULE_REF_INVALID", "$/module_manifest_ref", repr(module_ref))) + else: + expected_hash = _sha256(manifest_raw) + if module_ref.get("sha256") != expected_hash: + errors.append(_finding("PARENT_MODULE_REF_HASH_MISMATCH", "$/module_manifest_ref/sha256", f"expected={expected_hash};observed={module_ref.get('sha256')}")) + executor_ref = release.get("executor_binding_ref") + if ( + not isinstance(executor_ref, dict) + or executor_ref.get("path") != "deployment/stage2_code_executor_binding.yml" + or executor_ref.get("schema_id") != "stage2_code_executor_binding.v3" + or "sha256" in executor_ref + or executor_ref.get("binding_status") == "BOUND" + ): + errors.append(_finding("PARENT_EXECUTOR_TRUST_ROOT_REF_INVALID", "$/executor_binding_ref", repr(executor_ref))) + _walk_parent_refs(root, release, errors) + + status = release.get("release_status") + authorization = release.get("authorization_status") + signature = release.get("signature") + if status == "ADMITTED": + if authorization not in {"CANARY_ADMITTED", "PRODUCTION_ADMITTED"}: + errors.append(_finding("ADMITTED_RELEASE_AUTHORIZATION_INVALID", "$/authorization_status", repr(authorization))) + if signature == "PENDING_SEQUENTIAL_BIND" or not isinstance(signature, str) or len(signature) < 32: + errors.append(_finding("ADMITTED_RELEASE_SIGNATURE_INVALID", "$/signature", repr(signature))) + else: + pending.append(_finding("PARENT_RELEASE_NOT_ADMITTED", "$/release_status", str(status))) + return errors, pending + + +def _nested_pending(value: Any) -> bool: + if isinstance(value, str): + return "PENDING" in value or value in {"UNVERIFIED", "NOT_IMPLEMENTED", "DEV_UNAVAILABLE"} + if isinstance(value, list): + return any(_nested_pending(item) for item in value) + if isinstance(value, dict): + return any(_nested_pending(item) for item in value.values()) + return False + + +def _contains_ready_claim(value: Any) -> bool: + if isinstance(value, str): + return value in {"FULL_137_PRODUCTION_READY", "PRODUCTION_READY", "ADMITTED"} + if isinstance(value, list): + return any(_contains_ready_claim(item) for item in value) + if isinstance(value, dict): + return any(_contains_ready_claim(item) for item in value.values()) + return False + + +def _validate_137_coverage( + root: Path, + case_registry: dict[str, Any], + rule_registry: dict[str, Any], + coverage: dict[str, Any], +) -> tuple[list[dict[str, str]], list[dict[str, str]], dict[str, int]]: + errors: list[dict[str, str]] = [] + pending: list[dict[str, str]] = [] + case_rows = case_registry.get("rows") + rule_rows = rule_registry.get("rows") + coverage_rows = coverage.get("coverage_rows") + if not isinstance(case_rows, list) or len(case_rows) != 137: + errors.append(_finding("CASE_TYPE_REGISTRY_EXACT_137_REQUIRED", "case_type_registry:/rows", f"observed={len(case_rows) if isinstance(case_rows, list) else 'invalid'}")) + case_rows = [] + if not isinstance(rule_rows, list) or len(rule_rows) != 137: + errors.append(_finding("CASE_TYPE_RULE_REGISTRY_EXACT_137_REQUIRED", "case_type_rule_registry:/rows", f"observed={len(rule_rows) if isinstance(rule_rows, list) else 'invalid'}")) + rule_rows = [] + if not isinstance(coverage_rows, list) or len(coverage_rows) != 137: + errors.append(_finding("CASE_TYPE_COVERAGE_EXACT_137_REQUIRED", "case_type_coverage:/coverage_rows", f"observed={len(coverage_rows) if isinstance(coverage_rows, list) else 'invalid'}")) + coverage_rows = [] + + observed_case_ids = tuple(row.get("case_type_id") for row in case_rows if isinstance(row, dict)) + observed_catalog_ids = tuple(row.get("catalog_row_id") for row in case_rows if isinstance(row, dict)) + declared_catalog_ids = tuple(case_registry.get("catalog_row_ids", ())) + observed_rule_ids = tuple(row.get("case_type_id") for row in rule_rows if isinstance(row, dict)) + observed_coverage_ids = tuple(row.get("case_type_id") for row in coverage_rows if isinstance(row, dict)) + for code, path, observed, expected in ( + ("CASE_TYPE_ID_ORDER_MISMATCH", "case_type_registry:/rows", observed_case_ids, EXPECTED_CASE_TYPE_IDS), + ("CATALOG_ROW_ID_ORDER_MISMATCH", "case_type_registry:/rows", observed_catalog_ids, EXPECTED_CATALOG_ROW_IDS), + ("DECLARED_CATALOG_ROW_ID_ORDER_MISMATCH", "case_type_registry:/catalog_row_ids", declared_catalog_ids, EXPECTED_CATALOG_ROW_IDS), + ("RULE_CASE_TYPE_ID_ORDER_MISMATCH", "case_type_rule_registry:/rows", observed_rule_ids, EXPECTED_CASE_TYPE_IDS), + ("COVERAGE_CASE_TYPE_ID_ORDER_MISMATCH", "case_type_coverage:/coverage_rows", observed_coverage_ids, EXPECTED_CASE_TYPE_IDS), + ): + if observed != expected: + errors.append(_finding(code, path, "exact CT/CAT ordinal sequence required")) + + case_path = _physical(root, "manifest/case_type_registry.yml") + if case_path is None: + errors.append(_finding("CASE_TYPE_REGISTRY_PHYSICAL_FILE_REQUIRED", "case_type_registry:$", "manifest/case_type_registry.yml")) + case_raw = _canonical_bytes(case_registry) + else: + case_raw = case_path.read_bytes() + case_ref = rule_registry.get("case_type_registry_ref") + case_hash = rule_registry.get("case_type_registry_sha256") + if case_ref != "manifest/case_type_registry.yml" or case_hash != _sha256(case_raw): + errors.append(_finding("RULE_REGISTRY_CASE_TYPE_REF_MISMATCH", "case_type_rule_registry:/case_type_registry_sha256", f"expected={_sha256(case_raw)};observed={case_hash}")) + + coverage_dependency = coverage.get("registry_dependency") + if isinstance(coverage_dependency, dict): + declared = coverage_dependency.get("sha256") + if declared is not None and declared != _sha256(case_raw): + errors.append(_finding("COVERAGE_REGISTRY_HASH_MISMATCH", "case_type_coverage:/registry_dependency/sha256", f"expected={_sha256(case_raw)};observed={declared}")) + else: + errors.append(_finding("COVERAGE_REGISTRY_DEPENDENCY_REQUIRED", "case_type_coverage:/registry_dependency", "object required")) + + rule_doc_count = 0 + for case_type_id in EXPECTED_CASE_TYPE_IDS: + if _physical(root, f"rules/relief/{case_type_id}.md") is not None: + rule_doc_count += 1 + pending_legal = ( + rule_doc_count != 137 + or _nested_pending(case_registry) + or _nested_pending(rule_registry) + or _nested_pending(coverage) + ) + if pending_legal: + if coverage.get("release_eligible") is True or _contains_ready_claim(coverage): + errors.append(_finding("DISHONEST_FULL_137_READY_CLAIM", "case_type_coverage:$", f"rule_document_count={rule_doc_count};pending_contract=true")) + pending.append(_finding("FULL_137_LEGAL_COVERAGE_PENDING", "case_type_coverage:$", f"rule_document_count={rule_doc_count};legal registry/sign-off not fully admitted")) + return errors, pending, { + "case_type_count": len(case_rows), + "case_type_rule_row_count": len(rule_rows), + "coverage_row_count": len(coverage_rows), + "relief_rule_document_count": rule_doc_count, + } + + +def validate_package( + root: Path, + manifest_path: Path, + release_path: Path, + case_registry_path: Path, + rule_registry_path: Path, + coverage_path: Path, +) -> dict[str, Any]: + manifest = _load_json(manifest_path) + release = _load_json(release_path) + case_registry = _load_json(case_registry_path) + rule_registry = _load_json(rule_registry_path) + coverage = _load_json(coverage_path) + if not all(isinstance(row, dict) for row in (manifest, release, case_registry, rule_registry, coverage)): + raise ValidationInputError("ALL_RELEASE_INPUTS_MUST_BE_OBJECTS") + errors, pending, counts = _validate_module_manifest(root, manifest) + parent_errors, parent_pending = _validate_parent_release(root, release, manifest_path.read_bytes()) + coverage_errors, coverage_pending, coverage_counts = _validate_137_coverage(root, case_registry, rule_registry, coverage) + errors.extend(parent_errors) + errors.extend(coverage_errors) + pending.extend(parent_pending) + pending.extend(coverage_pending) + if ( + any(row["code"] == "FULL_137_LEGAL_COVERAGE_PENDING" for row in coverage_pending) + and ( + release.get("release_class") == "PRODUCTION_RELEASE" + or (isinstance(release.get("bundle"), dict) and release["bundle"].get("mode") == "PRODUCTION") + ) + ): + errors.append(_finding("PRODUCTION_RELEASE_WITH_PENDING_137_COVERAGE", "stage2_release:$", "full-137 legal/corpus coverage is not admitted")) + counts.update(coverage_counts) + errors = sorted(errors, key=lambda row: (row["code"], row["path"], row["detail"])) + pending = sorted(pending, key=lambda row: (row["code"], row["path"], row["detail"])) + return { + "counts": dict(sorted(counts.items())), + "errors": errors, + "pending": pending, + "schema_version": "stage2_release_validation.v1", + "status": "PASS" if not errors else "FAIL", + } + + +def validate(root: Path, manifest: dict[str, object]) -> list[str]: + """Compatibility wrapper for callers that only validate module rows.""" + + errors, _, _ = _validate_module_manifest(root, manifest) + return [f"{row['code']}:{row['path']}:{row['detail']}" for row in errors] + + +def main() -> int: + parser = argparse.ArgumentParser(description="Validate canonical Stage-2 raw release closure") + parser.add_argument("root", type=Path) + parser.add_argument("manifest", type=Path, nargs="?") + parser.add_argument("--release", type=Path) + parser.add_argument("--case-registry", type=Path) + parser.add_argument("--rule-registry", type=Path) + parser.add_argument("--coverage", type=Path) + args = parser.parse_args() + report = validate_package( + args.root, + args.manifest or args.root / "manifest/module_manifest.json", + args.release or args.root / "manifest/stage2_release.json", + args.case_registry or args.root / "manifest/case_type_registry.yml", + args.rule_registry or args.root / "manifest/case_type_rule_registry.yml", + args.coverage or args.root / "manifest/case_type_coverage.json", + ) + print(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":"))) + return 0 if report["status"] == "PASS" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R01_money_payment.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R01_money_payment.yml new file mode 100644 index 00000000..15bdbdfb --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R01_money_payment.yml @@ -0,0 +1,31 @@ +{ + "schema_version": "stage2.s2_20.renderer_descriptor.v1", + "renderer_id": "R01", + "renderer_kind": "MONEY_PAYMENT", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "stage2_20_role": "SELECT_AND_SEAL_ONLY", + "stage2_40_role": "CLOSED_RENDER", + "typed_slot_contract": { + "structural_slot_names": [ + "amount", + "currency", + "obligor_ref", + "payment_event_ref" + ], + "legal_branch_may_add_required_slots_only_after_review": true, + "missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING" + }, + "branch_rows": [], + "closure": { + "exactly_one_approved_branch_required": true, + "free_text_template_fallback_forbidden": true, + "renderer_may_not_change_claim_or_facts": true, + "zero_match_issue_code": "RENDERER_BRANCH_ZERO_MATCH", + "multi_match_issue_code": "RENDERER_BRANCH_MULTI_MATCH" + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R02_delivery_possession.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R02_delivery_possession.yml new file mode 100644 index 00000000..0df92c57 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R02_delivery_possession.yml @@ -0,0 +1,30 @@ +{ + "schema_version": "stage2.s2_20.renderer_descriptor.v1", + "renderer_id": "R02", + "renderer_kind": "DELIVERY_POSSESSION", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "stage2_20_role": "SELECT_AND_SEAL_ONLY", + "stage2_40_role": "CLOSED_RENDER", + "typed_slot_contract": { + "structural_slot_names": [ + "object_ref", + "obligor_ref", + "performance_mode" + ], + "legal_branch_may_add_required_slots_only_after_review": true, + "missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING" + }, + "branch_rows": [], + "closure": { + "exactly_one_approved_branch_required": true, + "free_text_template_fallback_forbidden": true, + "renderer_may_not_change_claim_or_facts": true, + "zero_match_issue_code": "RENDERER_BRANCH_ZERO_MATCH", + "multi_match_issue_code": "RENDERER_BRANCH_MULTI_MATCH" + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R03_declaration_registry.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R03_declaration_registry.yml new file mode 100644 index 00000000..652633d4 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R03_declaration_registry.yml @@ -0,0 +1,30 @@ +{ + "schema_version": "stage2.s2_20.renderer_descriptor.v1", + "renderer_id": "R03", + "renderer_kind": "DECLARATION_REGISTRY", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "stage2_20_role": "SELECT_AND_SEAL_ONLY", + "stage2_40_role": "CLOSED_RENDER", + "typed_slot_contract": { + "structural_slot_names": [ + "registry_object_ref", + "registry_action", + "obligor_ref" + ], + "legal_branch_may_add_required_slots_only_after_review": true, + "missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING" + }, + "branch_rows": [], + "closure": { + "exactly_one_approved_branch_required": true, + "free_text_template_fallback_forbidden": true, + "renderer_may_not_change_claim_or_facts": true, + "zero_match_issue_code": "RENDERER_BRANCH_ZERO_MATCH", + "multi_match_issue_code": "RENDERER_BRANCH_MULTI_MATCH" + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R04_special_nonmoney_performance.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R04_special_nonmoney_performance.yml new file mode 100644 index 00000000..29510d65 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R04_special_nonmoney_performance.yml @@ -0,0 +1,30 @@ +{ + "schema_version": "stage2.s2_20.renderer_descriptor.v1", + "renderer_id": "R04", + "renderer_kind": "SPECIAL_NONMONEY_PERFORMANCE", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "stage2_20_role": "SELECT_AND_SEAL_ONLY", + "stage2_40_role": "CLOSED_RENDER", + "typed_slot_contract": { + "structural_slot_names": [ + "performance_object_ref", + "performance_mode", + "obligor_ref" + ], + "legal_branch_may_add_required_slots_only_after_review": true, + "missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING" + }, + "branch_rows": [], + "closure": { + "exactly_one_approved_branch_required": true, + "free_text_template_fallback_forbidden": true, + "renderer_may_not_change_claim_or_facts": true, + "zero_match_issue_code": "RENDERER_BRANCH_ZERO_MATCH", + "multi_match_issue_code": "RENDERER_BRANCH_MULTI_MATCH" + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R05_declaratory.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R05_declaratory.yml new file mode 100644 index 00000000..3155c5a0 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R05_declaratory.yml @@ -0,0 +1,30 @@ +{ + "schema_version": "stage2.s2_20.renderer_descriptor.v1", + "renderer_id": "R05", + "renderer_kind": "DECLARATORY", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "stage2_20_role": "SELECT_AND_SEAL_ONLY", + "stage2_40_role": "CLOSED_RENDER", + "typed_slot_contract": { + "structural_slot_names": [ + "legal_relation_ref", + "declaration_scope", + "opposing_party_ref" + ], + "legal_branch_may_add_required_slots_only_after_review": true, + "missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING" + }, + "branch_rows": [], + "closure": { + "exactly_one_approved_branch_required": true, + "free_text_template_fallback_forbidden": true, + "renderer_may_not_change_claim_or_facts": true, + "zero_match_issue_code": "RENDERER_BRANCH_ZERO_MATCH", + "multi_match_issue_code": "RENDERER_BRANCH_MULTI_MATCH" + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R06_constitutive_judgment_challenge.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R06_constitutive_judgment_challenge.yml new file mode 100644 index 00000000..8535c997 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/renderers/R06_constitutive_judgment_challenge.yml @@ -0,0 +1,30 @@ +{ + "schema_version": "stage2.s2_20.renderer_descriptor.v1", + "renderer_id": "R06", + "renderer_kind": "CONSTITUTIVE_JUDGMENT_CHALLENGE", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "stage2_20_role": "SELECT_AND_SEAL_ONLY", + "stage2_40_role": "CLOSED_RENDER", + "typed_slot_contract": { + "structural_slot_names": [ + "legal_effect_ref", + "constitutive_method", + "opposing_party_ref" + ], + "legal_branch_may_add_required_slots_only_after_review": true, + "missing_required_slot_issue_code": "RENDERER_REQUIRED_SLOT_MISSING" + }, + "branch_rows": [], + "closure": { + "exactly_one_approved_branch_required": true, + "free_text_template_fallback_forbidden": true, + "renderer_may_not_change_claim_or_facts": true, + "zero_match_issue_code": "RENDERER_BRANCH_ZERO_MATCH", + "multi_match_issue_code": "RENDERER_BRANCH_MULTI_MATCH" + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/routing/actio_pauliana_mortgage_route.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/routing/actio_pauliana_mortgage_route.yml new file mode 100644 index 00000000..0855eb1c --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/routing/actio_pauliana_mortgage_route.yml @@ -0,0 +1,23 @@ +{ + "schema_version": "stage2.s2_20.actio_pauliana_mortgage_route.v1", + "registry_id": "S2-20-ACTIO-MORTGAGE-ROUTES", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "route_rows": [], + "required_distinctions": [ + "ENCUMBERED_TRANSFER", + "FRAUDULENT_MORTGAGE_CREATION", + "MORTGAGE_CANCELLATION", + "DISTRIBUTION_OR_DIVIDEND_EFFECT" + ], + "closure": { + "distinction_must_be_authority_backed": true, + "zero_match_issue_code": "ACTIO_MORTGAGE_ROUTE_ZERO_MATCH", + "multi_match_issue_code": "ACTIO_MORTGAGE_ROUTE_MULTI_MATCH", + "value_compensation_guess_forbidden": true + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/routing/actio_pauliana_route_registry.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/routing/actio_pauliana_route_registry.yml new file mode 100644 index 00000000..e54e4ec7 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/routing/actio_pauliana_route_registry.yml @@ -0,0 +1,32 @@ +{ + "schema_version": "stage2.s2_20.actio_pauliana_route_registry.v1", + "registry_id": "S2-20-ACTIO-PAULIANA-ROUTES", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "required_approved_route_count": 6, + "route_rows": [], + "route_contract": { + "required_fields": [ + "route_id", + "predicate_id", + "required_operands", + "defendant_role", + "restoration_recipient_role", + "renderer_id", + "calculation_branch_id", + "authority_refs", + "review_status" + ] + }, + "closure": { + "exactly_one_approved_route_required": true, + "zero_match_issue_code": "ACTIO_ROUTE_ZERO_MATCH", + "multi_match_issue_code": "ACTIO_ROUTE_MULTI_MATCH", + "generic_actio_fallback_forbidden": true + }, + "migration_sources_are_runtime_inputs": false, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/authority/authority_preflight.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/authority/authority_preflight.py new file mode 100644 index 00000000..3a63d373 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/authority/authority_preflight.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""Offline authority/law-value release integrity oracle.""" + +from __future__ import annotations + +from datetime import date +import hashlib +from pathlib import Path +from typing import Any, Mapping, Sequence + + +class AuthorityPreflightError(ValueError): + pass + + +def verify_authority_rows( + root: Path, + authority_rows: Sequence[Mapping[str, Any]], + as_of: date, +) -> dict[str, Any]: + verified: list[str] = [] + pending: list[dict[str, str]] = [] + for row in sorted(authority_rows, key=lambda item: str(item.get("authority_id"))): + authority_id = row.get("authority_id") + path = row.get("capture_path") + expected_hash = row.get("capture_sha256") + if not all(isinstance(value, str) and value for value in (authority_id, path, expected_hash)): + raise AuthorityPreflightError("AUTHORITY_ROW_INCOMPLETE") + target = (root / str(path)).resolve() + if root.resolve() not in target.parents or not target.is_file(): + pending.append({"authority_id": str(authority_id), "reason": "CAPTURE_UNAVAILABLE"}) + continue + if hashlib.sha256(target.read_bytes()).hexdigest() != expected_hash: + pending.append({"authority_id": str(authority_id), "reason": "CAPTURE_HASH_MISMATCH"}) + continue + effective_from = date.fromisoformat(str(row["effective_from"])) + effective_to_raw = row.get("effective_to") + effective_to = date.fromisoformat(str(effective_to_raw)) if effective_to_raw else None + if as_of < effective_from or (effective_to is not None and as_of > effective_to): + pending.append({"authority_id": str(authority_id), "reason": "OUTSIDE_EFFECTIVE_INTERVAL"}) + continue + if row.get("approval_status") != "APPROVED": + pending.append({"authority_id": str(authority_id), "reason": "LEGAL_APPROVAL_PENDING"}) + continue + verified.append(str(authority_id)) + return { + "status": "PASS" if not pending else "PENDING", + "verified_authority_ids": verified, + "pending_rows": pending, + } + diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/authority/authority_preflight.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/authority/authority_preflight.txt new file mode 100644 index 00000000..3a63d373 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/authority/authority_preflight.txt @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""Offline authority/law-value release integrity oracle.""" + +from __future__ import annotations + +from datetime import date +import hashlib +from pathlib import Path +from typing import Any, Mapping, Sequence + + +class AuthorityPreflightError(ValueError): + pass + + +def verify_authority_rows( + root: Path, + authority_rows: Sequence[Mapping[str, Any]], + as_of: date, +) -> dict[str, Any]: + verified: list[str] = [] + pending: list[dict[str, str]] = [] + for row in sorted(authority_rows, key=lambda item: str(item.get("authority_id"))): + authority_id = row.get("authority_id") + path = row.get("capture_path") + expected_hash = row.get("capture_sha256") + if not all(isinstance(value, str) and value for value in (authority_id, path, expected_hash)): + raise AuthorityPreflightError("AUTHORITY_ROW_INCOMPLETE") + target = (root / str(path)).resolve() + if root.resolve() not in target.parents or not target.is_file(): + pending.append({"authority_id": str(authority_id), "reason": "CAPTURE_UNAVAILABLE"}) + continue + if hashlib.sha256(target.read_bytes()).hexdigest() != expected_hash: + pending.append({"authority_id": str(authority_id), "reason": "CAPTURE_HASH_MISMATCH"}) + continue + effective_from = date.fromisoformat(str(row["effective_from"])) + effective_to_raw = row.get("effective_to") + effective_to = date.fromisoformat(str(effective_to_raw)) if effective_to_raw else None + if as_of < effective_from or (effective_to is not None and as_of > effective_to): + pending.append({"authority_id": str(authority_id), "reason": "OUTSIDE_EFFECTIVE_INTERVAL"}) + continue + if row.get("approval_status") != "APPROVED": + pending.append({"authority_id": str(authority_id), "reason": "LEGAL_APPROVAL_PENDING"}) + continue + verified.append(str(authority_id)) + return { + "status": "PASS" if not pending else "PENDING", + "verified_authority_ids": verified, + "pending_rows": pending, + } + diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/binding_signature_projection.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/binding_signature_projection.py new file mode 100644 index 00000000..f2a198ae --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/binding_signature_projection.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +"""Deterministic S2_10 -> C25 signature projection pure core. + +The module is an offline oracle. Runtime delivery copies these functions into +the single S2_20 YAML ``run_code`` body; a case run must not import this file. +""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Mapping, Sequence + + +SOURCE_FIELDS = frozenset( + { + "vocabulary_version", + "legal_basis_family", + "right_holder_refs", + "obligor_refs", + "performance_kind", + "object_refs", + "legal_effect", + "source_occurrence_refs", + } +) +TARGET_FIELDS = ( + "claim_family", + "legal_effect", + "performance_kind", + "object_kind", + "party_role_pattern", + "source_obligation_kind", + "remedy_mode", + "registry_action", + "procedural_posture", + "special_statute_tags", +) +ALLOWED_SOURCE_ROOTS = frozenset({"signature", "c20", "c21", "remedy"}) + + +class ProjectionError(ValueError): + """Closed projection contract violation.""" + + +def canonical_json_bytes(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) + + "\n" + ).encode("utf-8") + + +def _dig(root: Mapping[str, Any], path: str) -> Any: + parts = path.split(".") + if not parts or parts[0] not in ALLOWED_SOURCE_ROOTS: + raise ProjectionError(f"SOURCE_PATH_NOT_ALLOWED:{path}") + current: Any = root + for part in parts: + if not isinstance(current, Mapping) or part not in current: + return None + current = current[part] + return current + + +def _stable_values(value: Any) -> list[str]: + if value is None: + return [] + raw = value if isinstance(value, list) else [value] + if not all(isinstance(item, str) and item for item in raw): + raise ProjectionError("NON_TOKEN_SOURCE_VALUE") + return sorted(set(raw)) + + +def project_signature( + source_signature: Mapping[str, Any], + c20_projection: Mapping[str, Any], + c21_projection: Mapping[str, Any], + remedy_context: Mapping[str, Any], + projection_registry: Mapping[str, Any], +) -> dict[str, Any]: + """Apply release-bound mappings; never infer from labels or free text.""" + + unknown_source = sorted(set(source_signature) - SOURCE_FIELDS) + if unknown_source: + raise ProjectionError(f"UNEXPECTED_S2_10_SIGNATURE_FIELDS:{unknown_source}") + missing_source = sorted(SOURCE_FIELDS - set(source_signature)) + if missing_source: + raise ProjectionError(f"MISSING_S2_10_SIGNATURE_FIELDS:{missing_source}") + if source_signature.get("vocabulary_version") != "stage2.claim_signature_vocab.v1": + raise ProjectionError("UNSUPPORTED_SOURCE_VOCABULARY") + + rules = projection_registry.get("projection_rules") + if not isinstance(rules, list): + raise ProjectionError("PROJECTION_RULES_REQUIRED") + if projection_registry.get("registry_status") != "APPROVED": + rules = [] + by_target: dict[str, Mapping[str, Any]] = {} + for rule in rules: + if not isinstance(rule, Mapping): + raise ProjectionError("PROJECTION_ROW_NOT_OBJECT") + target = rule.get("target_field") + if target not in TARGET_FIELDS or target in by_target: + raise ProjectionError(f"TARGET_FIELD_INVALID_OR_DUPLICATE:{target}") + by_target[str(target)] = rule + if rules and set(by_target) != set(TARGET_FIELDS): + raise ProjectionError("TARGET_FIELD_COVERAGE_MISMATCH") + + source_values: Mapping[str, Any] = { + **source_signature, + "c20_remedy_projection": remedy_context.get("remedy_mode"), + "c20_dependency_projection": c20_projection.get("dependency_kind"), + "c21_party_projection": c21_projection.get("party_role_pattern"), + "c21_object_projection": c21_projection.get("object_kind"), + } + target: dict[str, Any] = {} + provenance: list[dict[str, Any]] = [] + for field in TARGET_FIELDS: + rule = by_target.get(field) + if rule is None: + value = [] if field == "special_statute_tags" else "UNKNOWN" + paths: list[str] = [] + distinct: list[str] = [] + else: + paths = rule.get("source_fields") + allowed = rule.get("allowed_output_values") + operator = rule.get("derivation_operator") + if not isinstance(paths, list) or not paths or not isinstance(allowed, list): + raise ProjectionError(f"INVALID_PROJECTION_RULE:{field}") + candidates = [token for path in paths for token in _stable_values(source_values.get(path))] + distinct = sorted({token for token in candidates if token in allowed}) + if operator in {"DIRECT_COPY", "CLOSED_LOOKUP"}: + value = distinct[0] if len(distinct) == 1 else "UNKNOWN" + elif operator in {"SET_INTERSECTION", "SET_UNION"} and field == "special_statute_tags": + value = distinct + else: + value = [] if field == "special_statute_tags" else "UNKNOWN" + target[field] = value + provenance.append( + { + "target_field": field, + "value": value, + "source_paths": sorted(paths), + "status": "DERIVED" if value not in ("UNKNOWN", []) else "UNKNOWN", + } + ) + + payload = { + "schema_version": "stage2.binding_target_signature.v1", + "source_vocabulary_version": source_signature["vocabulary_version"], + "target_vocabulary_version": projection_registry.get( + "target_vocabulary_version", "stage2.case_type_signature_vocab.v1" + ), + "signature": target, + "provenance": provenance, + } + payload["signature_sha256"] = hashlib.sha256(canonical_json_bytes(target)).hexdigest() + return payload diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/binding_signature_projection.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/binding_signature_projection.txt new file mode 100644 index 00000000..f2a198ae --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/binding_signature_projection.txt @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +"""Deterministic S2_10 -> C25 signature projection pure core. + +The module is an offline oracle. Runtime delivery copies these functions into +the single S2_20 YAML ``run_code`` body; a case run must not import this file. +""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Mapping, Sequence + + +SOURCE_FIELDS = frozenset( + { + "vocabulary_version", + "legal_basis_family", + "right_holder_refs", + "obligor_refs", + "performance_kind", + "object_refs", + "legal_effect", + "source_occurrence_refs", + } +) +TARGET_FIELDS = ( + "claim_family", + "legal_effect", + "performance_kind", + "object_kind", + "party_role_pattern", + "source_obligation_kind", + "remedy_mode", + "registry_action", + "procedural_posture", + "special_statute_tags", +) +ALLOWED_SOURCE_ROOTS = frozenset({"signature", "c20", "c21", "remedy"}) + + +class ProjectionError(ValueError): + """Closed projection contract violation.""" + + +def canonical_json_bytes(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) + + "\n" + ).encode("utf-8") + + +def _dig(root: Mapping[str, Any], path: str) -> Any: + parts = path.split(".") + if not parts or parts[0] not in ALLOWED_SOURCE_ROOTS: + raise ProjectionError(f"SOURCE_PATH_NOT_ALLOWED:{path}") + current: Any = root + for part in parts: + if not isinstance(current, Mapping) or part not in current: + return None + current = current[part] + return current + + +def _stable_values(value: Any) -> list[str]: + if value is None: + return [] + raw = value if isinstance(value, list) else [value] + if not all(isinstance(item, str) and item for item in raw): + raise ProjectionError("NON_TOKEN_SOURCE_VALUE") + return sorted(set(raw)) + + +def project_signature( + source_signature: Mapping[str, Any], + c20_projection: Mapping[str, Any], + c21_projection: Mapping[str, Any], + remedy_context: Mapping[str, Any], + projection_registry: Mapping[str, Any], +) -> dict[str, Any]: + """Apply release-bound mappings; never infer from labels or free text.""" + + unknown_source = sorted(set(source_signature) - SOURCE_FIELDS) + if unknown_source: + raise ProjectionError(f"UNEXPECTED_S2_10_SIGNATURE_FIELDS:{unknown_source}") + missing_source = sorted(SOURCE_FIELDS - set(source_signature)) + if missing_source: + raise ProjectionError(f"MISSING_S2_10_SIGNATURE_FIELDS:{missing_source}") + if source_signature.get("vocabulary_version") != "stage2.claim_signature_vocab.v1": + raise ProjectionError("UNSUPPORTED_SOURCE_VOCABULARY") + + rules = projection_registry.get("projection_rules") + if not isinstance(rules, list): + raise ProjectionError("PROJECTION_RULES_REQUIRED") + if projection_registry.get("registry_status") != "APPROVED": + rules = [] + by_target: dict[str, Mapping[str, Any]] = {} + for rule in rules: + if not isinstance(rule, Mapping): + raise ProjectionError("PROJECTION_ROW_NOT_OBJECT") + target = rule.get("target_field") + if target not in TARGET_FIELDS or target in by_target: + raise ProjectionError(f"TARGET_FIELD_INVALID_OR_DUPLICATE:{target}") + by_target[str(target)] = rule + if rules and set(by_target) != set(TARGET_FIELDS): + raise ProjectionError("TARGET_FIELD_COVERAGE_MISMATCH") + + source_values: Mapping[str, Any] = { + **source_signature, + "c20_remedy_projection": remedy_context.get("remedy_mode"), + "c20_dependency_projection": c20_projection.get("dependency_kind"), + "c21_party_projection": c21_projection.get("party_role_pattern"), + "c21_object_projection": c21_projection.get("object_kind"), + } + target: dict[str, Any] = {} + provenance: list[dict[str, Any]] = [] + for field in TARGET_FIELDS: + rule = by_target.get(field) + if rule is None: + value = [] if field == "special_statute_tags" else "UNKNOWN" + paths: list[str] = [] + distinct: list[str] = [] + else: + paths = rule.get("source_fields") + allowed = rule.get("allowed_output_values") + operator = rule.get("derivation_operator") + if not isinstance(paths, list) or not paths or not isinstance(allowed, list): + raise ProjectionError(f"INVALID_PROJECTION_RULE:{field}") + candidates = [token for path in paths for token in _stable_values(source_values.get(path))] + distinct = sorted({token for token in candidates if token in allowed}) + if operator in {"DIRECT_COPY", "CLOSED_LOOKUP"}: + value = distinct[0] if len(distinct) == 1 else "UNKNOWN" + elif operator in {"SET_INTERSECTION", "SET_UNION"} and field == "special_statute_tags": + value = distinct + else: + value = [] if field == "special_statute_tags" else "UNKNOWN" + target[field] = value + provenance.append( + { + "target_field": field, + "value": value, + "source_paths": sorted(paths), + "status": "DERIVED" if value not in ("UNKNOWN", []) else "UNKNOWN", + } + ) + + payload = { + "schema_version": "stage2.binding_target_signature.v1", + "source_vocabulary_version": source_signature["vocabulary_version"], + "target_vocabulary_version": projection_registry.get( + "target_vocabulary_version", "stage2.case_type_signature_vocab.v1" + ), + "signature": target, + "provenance": provenance, + } + payload["signature_sha256"] = hashlib.sha256(canonical_json_bytes(target)).hexdigest() + return payload diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c25_case_type_bind.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c25_case_type_bind.py new file mode 100644 index 00000000..79c066bc --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c25_case_type_bind.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""Closed-predicate C25 case-type binding pure core.""" + +from __future__ import annotations + +from typing import Any, Mapping, Sequence + + +ALLOWED_OPERATORS = frozenset({"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"}) +ELIGIBLE_ROW_KINDS = frozenset({"CLAIM", "GENERIC"}) + + +class BindingError(ValueError): + pass + + +def evaluate_predicate(signature: Mapping[str, Any], clauses: Sequence[Mapping[str, Any]]) -> bool: + for clause in clauses: + field = clause.get("field") + operator = clause.get("operator") + expected = clause.get("value") + if not isinstance(field, str) or operator not in ALLOWED_OPERATORS: + raise BindingError("INVALID_PREDICATE_CLAUSE") + actual = signature.get(field, "UNKNOWN") + if operator == "EQ": + passed = actual == expected + elif operator == "IN": + passed = isinstance(expected, list) and actual in expected + elif operator == "ALL_OF": + passed = isinstance(actual, list) and isinstance(expected, list) and set(expected) <= set(actual) + elif operator == "NONE_OF": + passed = isinstance(actual, list) and isinstance(expected, list) and set(expected).isdisjoint(actual) + else: + passed = (actual not in (None, "UNKNOWN", [])) is bool(expected) + if not passed: + return False + return True + + +def bind_case_type(signature: Mapping[str, Any], registry_rows: Sequence[Mapping[str, Any]]) -> dict[str, Any]: + matches: list[dict[str, str]] = [] + seen_ids: set[str] = set() + for row in registry_rows: + case_type_id = row.get("case_type_id") + if not isinstance(case_type_id, str): + raise BindingError("REGISTRY_ID_REQUIRED") + if case_type_id in seen_ids: + raise BindingError("DUPLICATE_CASE_TYPE_ID") + seen_ids.add(case_type_id) + row_kind = row.get("row_kind") + if row_kind not in ELIGIBLE_ROW_KINDS: + continue + if ( + row.get("claim_capable_disposition") != "CLAIM_CAPABLE" + or row.get("legal_classification_status") != "APPROVED" + or row.get("legal_content_status") != "APPROVED" + ): + continue + predicates = row.get("case_type_predicates") + if not isinstance(predicates, list) or len(predicates) != 1: + continue + predicate = predicates[0] + if ( + not isinstance(predicate, Mapping) + or predicate.get("legal_review_status") != "APPROVED" + or not isinstance(predicate.get("predicate_id"), str) + or not isinstance(predicate.get("clauses"), list) + ): + continue + predicate_id = str(predicate["predicate_id"]) + if evaluate_predicate(signature, predicate["clauses"]): + matches.append({"case_type_id": case_type_id, "matched_case_type_predicate_id": predicate_id}) + matches.sort(key=lambda item: (item["case_type_id"], item["matched_case_type_predicate_id"])) + if len(matches) == 1: + return {"binding_status": "BOUND", **matches[0], "binding_issue_codes": []} + return { + "binding_status": "UNRESOLVED" if not matches else "CONFLICT", + "matched_case_type_id": None, + "matched_case_type_predicate_id": None, + "candidate_matches": matches, + "binding_issue_codes": [ + "CASE_TYPE_BINDING_ZERO_MATCH" if not matches else "CASE_TYPE_BINDING_MULTI_MATCH" + ], + } diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c25_case_type_bind.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c25_case_type_bind.txt new file mode 100644 index 00000000..79c066bc --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c25_case_type_bind.txt @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""Closed-predicate C25 case-type binding pure core.""" + +from __future__ import annotations + +from typing import Any, Mapping, Sequence + + +ALLOWED_OPERATORS = frozenset({"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"}) +ELIGIBLE_ROW_KINDS = frozenset({"CLAIM", "GENERIC"}) + + +class BindingError(ValueError): + pass + + +def evaluate_predicate(signature: Mapping[str, Any], clauses: Sequence[Mapping[str, Any]]) -> bool: + for clause in clauses: + field = clause.get("field") + operator = clause.get("operator") + expected = clause.get("value") + if not isinstance(field, str) or operator not in ALLOWED_OPERATORS: + raise BindingError("INVALID_PREDICATE_CLAUSE") + actual = signature.get(field, "UNKNOWN") + if operator == "EQ": + passed = actual == expected + elif operator == "IN": + passed = isinstance(expected, list) and actual in expected + elif operator == "ALL_OF": + passed = isinstance(actual, list) and isinstance(expected, list) and set(expected) <= set(actual) + elif operator == "NONE_OF": + passed = isinstance(actual, list) and isinstance(expected, list) and set(expected).isdisjoint(actual) + else: + passed = (actual not in (None, "UNKNOWN", [])) is bool(expected) + if not passed: + return False + return True + + +def bind_case_type(signature: Mapping[str, Any], registry_rows: Sequence[Mapping[str, Any]]) -> dict[str, Any]: + matches: list[dict[str, str]] = [] + seen_ids: set[str] = set() + for row in registry_rows: + case_type_id = row.get("case_type_id") + if not isinstance(case_type_id, str): + raise BindingError("REGISTRY_ID_REQUIRED") + if case_type_id in seen_ids: + raise BindingError("DUPLICATE_CASE_TYPE_ID") + seen_ids.add(case_type_id) + row_kind = row.get("row_kind") + if row_kind not in ELIGIBLE_ROW_KINDS: + continue + if ( + row.get("claim_capable_disposition") != "CLAIM_CAPABLE" + or row.get("legal_classification_status") != "APPROVED" + or row.get("legal_content_status") != "APPROVED" + ): + continue + predicates = row.get("case_type_predicates") + if not isinstance(predicates, list) or len(predicates) != 1: + continue + predicate = predicates[0] + if ( + not isinstance(predicate, Mapping) + or predicate.get("legal_review_status") != "APPROVED" + or not isinstance(predicate.get("predicate_id"), str) + or not isinstance(predicate.get("clauses"), list) + ): + continue + predicate_id = str(predicate["predicate_id"]) + if evaluate_predicate(signature, predicate["clauses"]): + matches.append({"case_type_id": case_type_id, "matched_case_type_predicate_id": predicate_id}) + matches.sort(key=lambda item: (item["case_type_id"], item["matched_case_type_predicate_id"])) + if len(matches) == 1: + return {"binding_status": "BOUND", **matches[0], "binding_issue_codes": []} + return { + "binding_status": "UNRESOLVED" if not matches else "CONFLICT", + "matched_case_type_id": None, + "matched_case_type_predicate_id": None, + "candidate_matches": matches, + "binding_issue_codes": [ + "CASE_TYPE_BINDING_ZERO_MATCH" if not matches else "CASE_TYPE_BINDING_MULTI_MATCH" + ], + } diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c26_rule_branch_bind.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c26_rule_branch_bind.py new file mode 100644 index 00000000..4e4e6dd9 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c26_rule_branch_bind.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""C26 exact sub-rule/renderer/party/corpus binding pure core.""" + +from __future__ import annotations + +from typing import Any, Mapping, Sequence + + +class RuleBindingError(ValueError): + pass + + +def _evaluate(signature: Mapping[str, Any], clauses: Sequence[Mapping[str, Any]]) -> bool: + allowed = {"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"} + for clause in clauses: + field = clause.get("field") + op = clause.get("operator") + expected = clause.get("value") + if not isinstance(field, str) or op not in allowed: + raise RuleBindingError("INVALID_SUB_RULE_PREDICATE") + actual = signature.get(field, "UNKNOWN") + if op == "EQ": + ok = actual == expected + elif op == "IN": + ok = isinstance(expected, list) and actual in expected + elif op == "ALL_OF": + ok = isinstance(actual, list) and isinstance(expected, list) and set(expected) <= set(actual) + elif op == "NONE_OF": + ok = isinstance(actual, list) and isinstance(expected, list) and set(expected).isdisjoint(actual) + else: + ok = (actual not in (None, "UNKNOWN", [])) is bool(expected) + if not ok: + return False + return True + + +def bind_rule_branch( + signature: Mapping[str, Any], + case_binding: Mapping[str, Any], + rule_rows: Sequence[Mapping[str, Any]], + asset_hashes: Mapping[str, str], +) -> dict[str, Any]: + if case_binding.get("binding_status") != "BOUND": + return { + "binding_status": "UNRESOLVED", + "binding_issue_codes": ["QUERY_NOT_RUN_UNRESOLVED_BINDING"], + } + case_type_id = case_binding.get("case_type_id") or case_binding.get("matched_case_type_id") + candidates: list[Mapping[str, Any]] = [] + for row in rule_rows: + if row.get("case_type_id") != case_type_id: + continue + if ( + row.get("claim_capable_disposition") != "CLAIM_CAPABLE" + or row.get("legal_classification_status") != "APPROVED" + or row.get("legal_content_status") != "APPROVED" + ): + continue + branches = row.get("rule_branches") + if not isinstance(branches, list): + raise RuleBindingError("RULE_BRANCHES_ARRAY_REQUIRED") + for branch in branches: + if not isinstance(branch, Mapping) or branch.get("legal_content_status") != "APPROVED": + continue + predicates = branch.get("sub_rule_predicates") + if not isinstance(predicates, list) or len(predicates) != 1: + continue + predicate = predicates[0] + if ( + isinstance(predicate, Mapping) + and predicate.get("legal_review_status") == "APPROVED" + and isinstance(predicate.get("clauses"), list) + and _evaluate(signature, predicate["clauses"]) + ): + candidates.append(branch) + candidates.sort(key=lambda row: str(row.get("sub_rule_id"))) + if len(candidates) != 1: + return { + "binding_status": "UNRESOLVED" if not candidates else "CONFLICT", + "binding_issue_codes": [ + "SUB_RULE_BINDING_ZERO_MATCH" if not candidates else "SUB_RULE_BINDING_MULTI_MATCH" + ], + "candidate_sub_rule_ids": [row.get("sub_rule_id") for row in candidates], + } + row = candidates[0] + required_fields = ( + "sub_rule_id", "rule_branch_key", "markdown_source_path", + "markdown_source_sha256", "renderer_ids", "corpus_scope_id", + "element_set_id", "party_set_rule_id", "ce13_branch_id", + ) + missing = [name for name in required_fields if row.get(name) in (None, "", [])] + markdown_path = row.get("markdown_source_path") + markdown_hash = row.get("markdown_source_sha256") + hash_mismatch = ( + isinstance(markdown_path, str) + and markdown_path in asset_hashes + and asset_hashes[markdown_path] != markdown_hash + ) + if missing or hash_mismatch: + return { + "binding_status": "CONFLICT", + "binding_issue_codes": sorted( + ({"RULE_BRANCH_MISSING"} if missing else set()) + | ({"RULE_HASH_MISMATCH"} if hash_mismatch else set()) + ), + "missing_refs": sorted(missing), + "hash_mismatches": ["markdown_source_sha256"] if hash_mismatch else [], + } + sub_rule_id = row.get("sub_rule_id") + predicates = row.get("sub_rule_predicates", []) + return { + "binding_status": "BOUND", + "case_type_id": case_type_id, + "sub_rule_id": sub_rule_id, + "rule_branch_key": row.get("rule_branch_key"), + "matched_sub_rule_predicate_id": predicates[0].get("predicate_id"), + "renderer_ids": list(row.get("renderer_ids", [])), + "party_set_rule_id": row.get("party_set_rule_id"), + "corpus_scope_id": row.get("corpus_scope_id"), + "element_set_id": row.get("element_set_id"), + "ce13_branch_id": row.get("ce13_branch_id"), + "binding_issue_codes": [], + } diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c26_rule_branch_bind.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c26_rule_branch_bind.txt new file mode 100644 index 00000000..4e4e6dd9 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c26_rule_branch_bind.txt @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""C26 exact sub-rule/renderer/party/corpus binding pure core.""" + +from __future__ import annotations + +from typing import Any, Mapping, Sequence + + +class RuleBindingError(ValueError): + pass + + +def _evaluate(signature: Mapping[str, Any], clauses: Sequence[Mapping[str, Any]]) -> bool: + allowed = {"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"} + for clause in clauses: + field = clause.get("field") + op = clause.get("operator") + expected = clause.get("value") + if not isinstance(field, str) or op not in allowed: + raise RuleBindingError("INVALID_SUB_RULE_PREDICATE") + actual = signature.get(field, "UNKNOWN") + if op == "EQ": + ok = actual == expected + elif op == "IN": + ok = isinstance(expected, list) and actual in expected + elif op == "ALL_OF": + ok = isinstance(actual, list) and isinstance(expected, list) and set(expected) <= set(actual) + elif op == "NONE_OF": + ok = isinstance(actual, list) and isinstance(expected, list) and set(expected).isdisjoint(actual) + else: + ok = (actual not in (None, "UNKNOWN", [])) is bool(expected) + if not ok: + return False + return True + + +def bind_rule_branch( + signature: Mapping[str, Any], + case_binding: Mapping[str, Any], + rule_rows: Sequence[Mapping[str, Any]], + asset_hashes: Mapping[str, str], +) -> dict[str, Any]: + if case_binding.get("binding_status") != "BOUND": + return { + "binding_status": "UNRESOLVED", + "binding_issue_codes": ["QUERY_NOT_RUN_UNRESOLVED_BINDING"], + } + case_type_id = case_binding.get("case_type_id") or case_binding.get("matched_case_type_id") + candidates: list[Mapping[str, Any]] = [] + for row in rule_rows: + if row.get("case_type_id") != case_type_id: + continue + if ( + row.get("claim_capable_disposition") != "CLAIM_CAPABLE" + or row.get("legal_classification_status") != "APPROVED" + or row.get("legal_content_status") != "APPROVED" + ): + continue + branches = row.get("rule_branches") + if not isinstance(branches, list): + raise RuleBindingError("RULE_BRANCHES_ARRAY_REQUIRED") + for branch in branches: + if not isinstance(branch, Mapping) or branch.get("legal_content_status") != "APPROVED": + continue + predicates = branch.get("sub_rule_predicates") + if not isinstance(predicates, list) or len(predicates) != 1: + continue + predicate = predicates[0] + if ( + isinstance(predicate, Mapping) + and predicate.get("legal_review_status") == "APPROVED" + and isinstance(predicate.get("clauses"), list) + and _evaluate(signature, predicate["clauses"]) + ): + candidates.append(branch) + candidates.sort(key=lambda row: str(row.get("sub_rule_id"))) + if len(candidates) != 1: + return { + "binding_status": "UNRESOLVED" if not candidates else "CONFLICT", + "binding_issue_codes": [ + "SUB_RULE_BINDING_ZERO_MATCH" if not candidates else "SUB_RULE_BINDING_MULTI_MATCH" + ], + "candidate_sub_rule_ids": [row.get("sub_rule_id") for row in candidates], + } + row = candidates[0] + required_fields = ( + "sub_rule_id", "rule_branch_key", "markdown_source_path", + "markdown_source_sha256", "renderer_ids", "corpus_scope_id", + "element_set_id", "party_set_rule_id", "ce13_branch_id", + ) + missing = [name for name in required_fields if row.get(name) in (None, "", [])] + markdown_path = row.get("markdown_source_path") + markdown_hash = row.get("markdown_source_sha256") + hash_mismatch = ( + isinstance(markdown_path, str) + and markdown_path in asset_hashes + and asset_hashes[markdown_path] != markdown_hash + ) + if missing or hash_mismatch: + return { + "binding_status": "CONFLICT", + "binding_issue_codes": sorted( + ({"RULE_BRANCH_MISSING"} if missing else set()) + | ({"RULE_HASH_MISMATCH"} if hash_mismatch else set()) + ), + "missing_refs": sorted(missing), + "hash_mismatches": ["markdown_source_sha256"] if hash_mismatch else [], + } + sub_rule_id = row.get("sub_rule_id") + predicates = row.get("sub_rule_predicates", []) + return { + "binding_status": "BOUND", + "case_type_id": case_type_id, + "sub_rule_id": sub_rule_id, + "rule_branch_key": row.get("rule_branch_key"), + "matched_sub_rule_predicate_id": predicates[0].get("predicate_id"), + "renderer_ids": list(row.get("renderer_ids", [])), + "party_set_rule_id": row.get("party_set_rule_id"), + "corpus_scope_id": row.get("corpus_scope_id"), + "element_set_id": row.get("element_set_id"), + "ce13_branch_id": row.get("ce13_branch_id"), + "binding_issue_codes": [], + } diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c27_query_plan.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c27_query_plan.py new file mode 100644 index 00000000..d117558d --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c27_query_plan.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +"""Build schema-conformant, release-bound C27 requirement-fact query plans.""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Mapping, Sequence + + +ROLES = ("element", "defense", "rebuttal", "burden", "relief_consistency") +SHA256_KEYS = ( + "embedding_configuration_digest", + "reranker_configuration_digest", + "index_configuration_digest", +) + + +class QueryPlanError(ValueError): + """Controlled C27 contract failure.""" + + +def _digest(value: Any) -> str: + raw = ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + return hashlib.sha256(raw).hexdigest() + + +def _nonempty(value: Any, code: str) -> str: + if not isinstance(value, str) or not value: + raise QueryPlanError(code) + return value + + +def _sha256(value: Any, code: str) -> str: + text = _nonempty(value, code) + if len(text) != 64 or any(character not in "0123456789abcdef" for character in text): + raise QueryPlanError(code) + return text + + +def _bound(binding: Mapping[str, Any]) -> bool: + return ( + binding.get("case_type_binding_status") == "BOUND" + and binding.get("sub_rule_binding_status") == "BOUND" + and binding.get("legal_content_status") == "APPROVED" + ) + + +def build_query_plan( + atomic_claim_id: str, + binding: Mapping[str, Any], + scope: Mapping[str, Any], + role_element_ids: Mapping[str, Sequence[str]], + *, + run_binding_digest: str, + effective_on: str, +) -> dict[str, Any]: + """Return the exact ``query_plan`` contract from binding_retrieval.schema. + + An unresolved binding produces a valid empty plan. It never manufactures a + case type or rule ID merely to populate a query row. + """ + + atomic_claim_id = _nonempty(atomic_claim_id, "ATOMIC_CLAIM_ID_REQUIRED") + run_binding_digest = _sha256(run_binding_digest, "RUN_BINDING_DIGEST_INVALID") + if not _bound(binding): + core = { + "schema_version": "stage2_requirement_fact_query_plan.v1", + "run_binding_digest": run_binding_digest, + "atomic_claim_id": atomic_claim_id, + "rows": [], + } + return {**core, "query_plan_sha256": _digest(core)} + + case_type_id = _nonempty(binding.get("case_type_id"), "CASE_TYPE_ID_REQUIRED") + sub_rule_id = _nonempty(binding.get("sub_rule_id"), "SUB_RULE_ID_REQUIRED") + rule_branch_key = _nonempty(binding.get("rule_branch_key"), "RULE_BRANCH_KEY_REQUIRED") + corpus_scope_id = _nonempty(binding.get("corpus_scope_id"), "CORPUS_SCOPE_ID_REQUIRED") + element_set_id = _nonempty(binding.get("element_set_id"), "ELEMENT_SET_ID_REQUIRED") + if rule_branch_key != f"{case_type_id}::{sub_rule_id}": + raise QueryPlanError("RULE_BRANCH_KEY_MISMATCH") + if scope.get("corpus_scope_id") not in {None, corpus_scope_id}: + raise QueryPlanError("CORPUS_SCOPE_BINDING_MISMATCH") + if scope.get("element_set_id") not in {None, element_set_id}: + raise QueryPlanError("ELEMENT_SET_BINDING_MISMATCH") + + snapshot_id = _nonempty(scope.get("snapshot_id"), "SNAPSHOT_ID_REQUIRED") + effective_on = _nonempty(effective_on, "EFFECTIVE_ON_REQUIRED") + query_contract = scope.get("query_contract") + if not isinstance(query_contract, Mapping): + raise QueryPlanError("QUERY_CONTRACT_REQUIRED") + alpha = query_contract.get("alpha") + top_k = query_contract.get("top_k") + if not isinstance(alpha, (int, float)) or isinstance(alpha, bool) or not 0 <= alpha <= 1: + raise QueryPlanError("QUERY_ALPHA_INVALID") + if not isinstance(top_k, int) or isinstance(top_k, bool) or not 1 <= top_k <= 100: + raise QueryPlanError("QUERY_TOP_K_INVALID") + configuration_digests = { + key: _sha256(query_contract.get(key), f"{key.upper()}_INVALID") for key in SHA256_KEYS + } + allowed_elements = scope.get("corpus_element_ids", []) + if not isinstance(allowed_elements, list) or not all( + isinstance(item, str) and item for item in allowed_elements + ): + raise QueryPlanError("CORPUS_ELEMENT_IDS_INVALID") + allowed_set = set(allowed_elements) + + rows: list[dict[str, Any]] = [] + for role in ROLES: + raw_ids = role_element_ids.get(role, ()) + if not isinstance(raw_ids, Sequence) or isinstance(raw_ids, (str, bytes)): + raise QueryPlanError("ROLE_ELEMENT_IDS_INVALID") + element_ids = sorted(set(raw_ids)) + if not all(isinstance(item, str) and item for item in element_ids): + raise QueryPlanError("ROLE_ELEMENT_IDS_INVALID") + if allowed_set and not set(element_ids).issubset(allowed_set): + raise QueryPlanError("ROLE_ELEMENT_OUTSIDE_APPROVED_SCOPE") + query_text = " ".join([case_type_id, sub_rule_id, role, *element_ids]) + query_text_sha256 = _digest(query_text) + query_identity = { + "atomic_claim_id": atomic_claim_id, + "rule_branch_key": rule_branch_key, + "doctrine_role": role, + "query_sha256": query_text_sha256, + } + row = { + "query_id": f"Q-{_digest(query_identity)[:24]}", + "atomic_claim_id": atomic_claim_id, + "case_type_id": case_type_id, + "sub_rule_id": sub_rule_id, + "rule_branch_key": rule_branch_key, + "corpus_scope_id": corpus_scope_id, + "element_set_id": element_set_id, + "doctrine_role": role, + "query_status": "READY", + "query_text": query_text, + "query_sha256": query_text_sha256, + "filter": { + "case_type_id": case_type_id, + "sub_rule_id": sub_rule_id, + "rule_branch_key": rule_branch_key, + "corpus_scope_id": corpus_scope_id, + "element_set_id": element_set_id, + "doctrine_role": role, + "jurisdiction": "KR", + "snapshot_id": snapshot_id, + "approval_status": "APPROVED", + "effective_on": effective_on, + }, + "alpha": float(alpha), + "top_k": top_k, + **configuration_digests, + "expected_corpus_element_ids": element_ids, + "issue_codes": [], + } + rows.append(row) + + core = { + "schema_version": "stage2_requirement_fact_query_plan.v1", + "run_binding_digest": run_binding_digest, + "atomic_claim_id": atomic_claim_id, + "rows": rows, + } + return {**core, "query_plan_sha256": _digest(core)} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c27_query_plan.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c27_query_plan.txt new file mode 100644 index 00000000..d117558d --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c27_query_plan.txt @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +"""Build schema-conformant, release-bound C27 requirement-fact query plans.""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Mapping, Sequence + + +ROLES = ("element", "defense", "rebuttal", "burden", "relief_consistency") +SHA256_KEYS = ( + "embedding_configuration_digest", + "reranker_configuration_digest", + "index_configuration_digest", +) + + +class QueryPlanError(ValueError): + """Controlled C27 contract failure.""" + + +def _digest(value: Any) -> str: + raw = ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + return hashlib.sha256(raw).hexdigest() + + +def _nonempty(value: Any, code: str) -> str: + if not isinstance(value, str) or not value: + raise QueryPlanError(code) + return value + + +def _sha256(value: Any, code: str) -> str: + text = _nonempty(value, code) + if len(text) != 64 or any(character not in "0123456789abcdef" for character in text): + raise QueryPlanError(code) + return text + + +def _bound(binding: Mapping[str, Any]) -> bool: + return ( + binding.get("case_type_binding_status") == "BOUND" + and binding.get("sub_rule_binding_status") == "BOUND" + and binding.get("legal_content_status") == "APPROVED" + ) + + +def build_query_plan( + atomic_claim_id: str, + binding: Mapping[str, Any], + scope: Mapping[str, Any], + role_element_ids: Mapping[str, Sequence[str]], + *, + run_binding_digest: str, + effective_on: str, +) -> dict[str, Any]: + """Return the exact ``query_plan`` contract from binding_retrieval.schema. + + An unresolved binding produces a valid empty plan. It never manufactures a + case type or rule ID merely to populate a query row. + """ + + atomic_claim_id = _nonempty(atomic_claim_id, "ATOMIC_CLAIM_ID_REQUIRED") + run_binding_digest = _sha256(run_binding_digest, "RUN_BINDING_DIGEST_INVALID") + if not _bound(binding): + core = { + "schema_version": "stage2_requirement_fact_query_plan.v1", + "run_binding_digest": run_binding_digest, + "atomic_claim_id": atomic_claim_id, + "rows": [], + } + return {**core, "query_plan_sha256": _digest(core)} + + case_type_id = _nonempty(binding.get("case_type_id"), "CASE_TYPE_ID_REQUIRED") + sub_rule_id = _nonempty(binding.get("sub_rule_id"), "SUB_RULE_ID_REQUIRED") + rule_branch_key = _nonempty(binding.get("rule_branch_key"), "RULE_BRANCH_KEY_REQUIRED") + corpus_scope_id = _nonempty(binding.get("corpus_scope_id"), "CORPUS_SCOPE_ID_REQUIRED") + element_set_id = _nonempty(binding.get("element_set_id"), "ELEMENT_SET_ID_REQUIRED") + if rule_branch_key != f"{case_type_id}::{sub_rule_id}": + raise QueryPlanError("RULE_BRANCH_KEY_MISMATCH") + if scope.get("corpus_scope_id") not in {None, corpus_scope_id}: + raise QueryPlanError("CORPUS_SCOPE_BINDING_MISMATCH") + if scope.get("element_set_id") not in {None, element_set_id}: + raise QueryPlanError("ELEMENT_SET_BINDING_MISMATCH") + + snapshot_id = _nonempty(scope.get("snapshot_id"), "SNAPSHOT_ID_REQUIRED") + effective_on = _nonempty(effective_on, "EFFECTIVE_ON_REQUIRED") + query_contract = scope.get("query_contract") + if not isinstance(query_contract, Mapping): + raise QueryPlanError("QUERY_CONTRACT_REQUIRED") + alpha = query_contract.get("alpha") + top_k = query_contract.get("top_k") + if not isinstance(alpha, (int, float)) or isinstance(alpha, bool) or not 0 <= alpha <= 1: + raise QueryPlanError("QUERY_ALPHA_INVALID") + if not isinstance(top_k, int) or isinstance(top_k, bool) or not 1 <= top_k <= 100: + raise QueryPlanError("QUERY_TOP_K_INVALID") + configuration_digests = { + key: _sha256(query_contract.get(key), f"{key.upper()}_INVALID") for key in SHA256_KEYS + } + allowed_elements = scope.get("corpus_element_ids", []) + if not isinstance(allowed_elements, list) or not all( + isinstance(item, str) and item for item in allowed_elements + ): + raise QueryPlanError("CORPUS_ELEMENT_IDS_INVALID") + allowed_set = set(allowed_elements) + + rows: list[dict[str, Any]] = [] + for role in ROLES: + raw_ids = role_element_ids.get(role, ()) + if not isinstance(raw_ids, Sequence) or isinstance(raw_ids, (str, bytes)): + raise QueryPlanError("ROLE_ELEMENT_IDS_INVALID") + element_ids = sorted(set(raw_ids)) + if not all(isinstance(item, str) and item for item in element_ids): + raise QueryPlanError("ROLE_ELEMENT_IDS_INVALID") + if allowed_set and not set(element_ids).issubset(allowed_set): + raise QueryPlanError("ROLE_ELEMENT_OUTSIDE_APPROVED_SCOPE") + query_text = " ".join([case_type_id, sub_rule_id, role, *element_ids]) + query_text_sha256 = _digest(query_text) + query_identity = { + "atomic_claim_id": atomic_claim_id, + "rule_branch_key": rule_branch_key, + "doctrine_role": role, + "query_sha256": query_text_sha256, + } + row = { + "query_id": f"Q-{_digest(query_identity)[:24]}", + "atomic_claim_id": atomic_claim_id, + "case_type_id": case_type_id, + "sub_rule_id": sub_rule_id, + "rule_branch_key": rule_branch_key, + "corpus_scope_id": corpus_scope_id, + "element_set_id": element_set_id, + "doctrine_role": role, + "query_status": "READY", + "query_text": query_text, + "query_sha256": query_text_sha256, + "filter": { + "case_type_id": case_type_id, + "sub_rule_id": sub_rule_id, + "rule_branch_key": rule_branch_key, + "corpus_scope_id": corpus_scope_id, + "element_set_id": element_set_id, + "doctrine_role": role, + "jurisdiction": "KR", + "snapshot_id": snapshot_id, + "approval_status": "APPROVED", + "effective_on": effective_on, + }, + "alpha": float(alpha), + "top_k": top_k, + **configuration_digests, + "expected_corpus_element_ids": element_ids, + "issue_codes": [], + } + rows.append(row) + + core = { + "schema_version": "stage2_requirement_fact_query_plan.v1", + "run_binding_digest": run_binding_digest, + "atomic_claim_id": atomic_claim_id, + "rows": rows, + } + return {**core, "query_plan_sha256": _digest(core)} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c28_weaviate_retrieve.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c28_weaviate_retrieve.py new file mode 100644 index 00000000..4363ede8 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c28_weaviate_retrieve.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 +"""C28 exact-filter retrieval and canonical result/receipt normalization.""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Callable, Mapping, Sequence + + +class RetrievalError(RuntimeError): + """Controlled C28 transport or contract failure.""" + + +RESPONSE_KEYS = frozenset( + { + "schema_version", "request_id", "request_sha256", "response_status", + "raw_hit_count", "selected_hits", "dropped_hits", "deterministic_order_digest", + "retry_count", + } +) +HIT_KEYS = frozenset( + { + "chunk_id", "chunk_sha256", "source_document_id", "source_document_sha256", + "source_locator", "pre_rerank_rank", "hybrid_score", "post_rerank_rank", + "reranker_score", "metadata", + } +) +METADATA_KEYS = frozenset( + { + "schema_version", "document_id", "document_sha256", "chunk_id", "chunk_sha256", + "source_path", "source_locator", "case_type_ids", "sub_rule_ids", + "rule_branch_keys", "corpus_scope_ids", "element_set_ids", "corpus_element_ids", + "doctrine_roles", "jurisdiction", "effective_from", "effective_to", "authority_ids", + "proposition_ids", "approval_status", "legal_reviewer_receipt_ref", + "legal_reviewer_receipt_sha256", + } +) +DROP_KEYS = frozenset({"chunk_id", "chunk_sha256", "drop_reason_code"}) +DROP_REASONS = frozenset( + {"FILTER_MISMATCH", "UNAPPROVED_CHUNK", "HASH_MISMATCH", "SNAPSHOT_MISMATCH", "CROSSWALK_GAP", "DUPLICATE_CHUNK"} +) + + +def _canonical(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + + +def _digest(value: Any) -> str: + return hashlib.sha256(_canonical(value)).hexdigest() + + +def _nonempty(value: Any, code: str) -> str: + if not isinstance(value, str) or not value: + raise RetrievalError(code) + return value + + +def build_request(query_row: Mapping[str, Any], collection: Mapping[str, Any]) -> dict[str, Any]: + """Build the exact Weaviate ``hybrid_search_request`` contract.""" + + if query_row.get("query_status") != "READY": + raise RetrievalError("QUERY_ROW_NOT_READY") + filter_value = query_row.get("filter") + if not isinstance(filter_value, Mapping): + raise RetrievalError("QUERY_FILTER_REQUIRED") + request_seed = { + "query_id": query_row.get("query_id"), + "query_sha256": query_row.get("query_sha256"), + "snapshot_sha256": collection.get("snapshot_sha256"), + } + return { + "schema_version": "stage2_weaviate_hybrid_search_request.v1", + "request_id": f"REQ-{_digest(request_seed)[:24]}", + "query_plan_id": _nonempty(query_row.get("query_id"), "QUERY_ID_REQUIRED"), + "atomic_claim_id": _nonempty( + query_row.get("atomic_claim_id"), "ATOMIC_CLAIM_ID_REQUIRED" + ), + "collection_name": _nonempty( + collection.get("collection_name"), "COLLECTION_NAME_REQUIRED" + ), + "tenant": _nonempty(collection.get("tenant"), "TENANT_REQUIRED"), + "query_text": _nonempty(query_row.get("query_text"), "QUERY_TEXT_REQUIRED"), + "query_sha256": _nonempty(query_row.get("query_sha256"), "QUERY_SHA256_REQUIRED"), + "filter": dict(filter_value), + "alpha": query_row.get("alpha"), + "top_k": query_row.get("top_k"), + "embedding_configuration_digest": query_row.get("embedding_configuration_digest"), + "reranker_configuration_digest": query_row.get("reranker_configuration_digest"), + "index_configuration_digest": query_row.get("index_configuration_digest"), + "snapshot_sha256": _nonempty( + collection.get("snapshot_sha256"), "SNAPSHOT_SHA256_REQUIRED" + ), + } + + +def _drop(hit: Mapping[str, Any], reason: str) -> dict[str, Any]: + return { + "chunk_id": _nonempty(hit.get("chunk_id"), "HIT_CHUNK_ID_REQUIRED"), + "chunk_sha256": _nonempty(hit.get("chunk_sha256"), "HIT_CHUNK_SHA256_REQUIRED"), + "drop_reason_code": reason, + } + + +def _hit_filter_mismatch(hit: Mapping[str, Any], filter_value: Mapping[str, Any]) -> str | None: + if set(hit) != HIT_KEYS: + raise RetrievalError("RETRIEVAL_HIT_KEYS_INVALID") + metadata = hit.get("metadata") + if not isinstance(metadata, Mapping): + raise RetrievalError("RETRIEVAL_HIT_METADATA_INVALID") + if set(metadata) != METADATA_KEYS: + raise RetrievalError("RETRIEVAL_HIT_METADATA_KEYS_INVALID") + if metadata.get("approval_status") != "APPROVED": + return "UNAPPROVED_CHUNK" + scalar_to_set = { + "case_type_id": "case_type_ids", + "sub_rule_id": "sub_rule_ids", + "rule_branch_key": "rule_branch_keys", + "corpus_scope_id": "corpus_scope_ids", + "element_set_id": "element_set_ids", + "doctrine_role": "doctrine_roles", + } + for filter_key, metadata_key in scalar_to_set.items(): + values = metadata.get(metadata_key) + if not isinstance(values, list) or filter_value.get(filter_key) not in values: + return "FILTER_MISMATCH" + if metadata.get("jurisdiction") != "KR": + return "FILTER_MISMATCH" + if hit.get("chunk_sha256") != metadata.get("chunk_sha256"): + return "HASH_MISMATCH" + return None + + +def _receipt( + *, + query_plan: Mapping[str, Any], + query_row: Mapping[str, Any], + request: Mapping[str, Any], + response: Mapping[str, Any], + collection: Mapping[str, Any], + credential_ref: str, +) -> dict[str, Any]: + configuration = { + key: query_row.get(key) + for key in ( + "alpha", + "top_k", + "embedding_configuration_digest", + "reranker_configuration_digest", + "index_configuration_digest", + ) + } + core = { + "schema_version": "stage2_requirement_fact_retrieval_receipt.v1", + "run_binding_digest": query_plan["run_binding_digest"], + "atomic_claim_id": query_plan["atomic_claim_id"], + "query_plan_sha256": query_plan["query_plan_sha256"], + "request_sha256": _digest(request), + "response_sha256": _digest(response), + "transport": "MCP_OVER_HTTP_JSON_RPC", + "endpoint": "https://weaviate.eroomai.com/mcp", + "tool_name": "search_hybrid", + "collection_name": collection["collection_name"], + "tenant": collection["tenant"], + "snapshot_sha256": collection["snapshot_sha256"], + "filter_sha256": _digest(query_row["filter"]), + "configuration_digest": _digest(configuration), + "attempt_count": int(response.get("retry_count", 0)) + 1, + "replay_status": "NOT_REQUESTED", + "read_only_verified": True, + "credential_ref": _nonempty(credential_ref, "CREDENTIAL_REF_REQUIRED"), + "credential_material_persisted": False, + } + return {**core, "receipt_sha256": _digest(core)} + + +def retrieve_exact( + query_plan: Mapping[str, Any], + collection: Mapping[str, Any], + search_hybrid: Callable[[Mapping[str, Any]], Mapping[str, Any]], + *, + credential_ref: str, +) -> tuple[dict[str, Any], list[dict[str, Any]]]: + """Execute READY rows without fallback and return canonical result + receipts.""" + + rows = query_plan.get("rows") + if not isinstance(rows, Sequence) or isinstance(rows, (str, bytes)): + raise RetrievalError("QUERY_PLAN_ROWS_INVALID") + selected: list[dict[str, Any]] = [] + dropped: list[dict[str, Any]] = [] + receipts: list[dict[str, Any]] = [] + issue_codes: set[str] = set() + technical_incomplete = False + for query_row in rows: + if not isinstance(query_row, Mapping) or query_row.get("query_status") != "READY": + raise RetrievalError("QUERY_PLAN_READY_ROW_REQUIRED") + request = build_request(query_row, collection) + request_sha256 = _digest(request) + response = search_hybrid(request) + if not isinstance(response, Mapping) or response.get("request_sha256") != request_sha256: + raise RetrievalError("RETRIEVAL_REQUEST_ECHO_MISMATCH") + if set(response) != RESPONSE_KEYS: + raise RetrievalError("RETRIEVAL_RESPONSE_KEYS_INVALID") + if response.get("schema_version") != "stage2_weaviate_hybrid_search_response.v1": + raise RetrievalError("RETRIEVAL_RESPONSE_SCHEMA_MISMATCH") + if response.get("request_id") != request["request_id"]: + raise RetrievalError("RETRIEVAL_REQUEST_ID_ECHO_MISMATCH") + retry_count = response.get("retry_count") + if not isinstance(retry_count, int) or isinstance(retry_count, bool) or not 0 <= retry_count <= 2: + raise RetrievalError("RETRIEVAL_RETRY_COUNT_INVALID") + response_status = response.get("response_status") + if response_status not in {"COMPLETE", "NO_HIT", "TECHNICAL_INCOMPLETE"}: + raise RetrievalError("RETRIEVAL_RESPONSE_STATUS_INVALID") + if response_status == "TECHNICAL_INCOMPLETE": + technical_incomplete = True + issue_codes.add("RETRIEVAL_TECHNICAL_INCOMPLETE") + raw_hits = response.get("selected_hits") + raw_drops = response.get("dropped_hits") + if not isinstance(raw_hits, list) or not isinstance(raw_drops, list): + raise RetrievalError("RETRIEVAL_RESPONSE_ROWS_INVALID") + for hit in raw_hits: + if not isinstance(hit, Mapping): + raise RetrievalError("RETRIEVAL_HIT_INVALID") + reason = _hit_filter_mismatch(hit, query_row["filter"]) + if reason is None: + selected.append(dict(hit)) + else: + dropped.append(_drop(hit, reason)) + issue_codes.add(reason) + for row in raw_drops: + if ( + not isinstance(row, Mapping) + or set(row) != DROP_KEYS + or row.get("drop_reason_code") not in DROP_REASONS + ): + raise RetrievalError("DROPPED_HIT_INVALID") + dropped.append(dict(row)) + receipts.append( + _receipt( + query_plan=query_plan, + query_row=query_row, + request=request, + response=response, + collection=collection, + credential_ref=credential_ref, + ) + ) + + unique: dict[str, dict[str, Any]] = {} + for hit in sorted( + selected, + key=lambda value: ( + int(value.get("post_rerank_rank", 2**31 - 1)), + -float(value.get("reranker_score") or value.get("hybrid_score") or 0), + str(value.get("chunk_id", "")), + ), + ): + chunk_id = _nonempty(hit.get("chunk_id"), "HIT_CHUNK_ID_REQUIRED") + if chunk_id in unique: + dropped.append(_drop(hit, "DUPLICATE_CHUNK")) + issue_codes.add("DUPLICATE_CHUNK") + else: + unique[chunk_id] = hit + selected_hits = list(unique.values()) + result_status = ( + "NOT_RUN_UNRESOLVED_BINDING" + if not rows + else "TECHNICAL_INCOMPLETE" + if technical_incomplete + else "COMPLETE" + if selected_hits + else "NO_HIT" + ) + if not rows: + issue_codes.add("QUERY_NOT_RUN_UNRESOLVED_BINDING") + core = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": query_plan["run_binding_digest"], + "atomic_claim_id": query_plan["atomic_claim_id"], + "query_plan_sha256": query_plan["query_plan_sha256"], + "snapshot_id": _nonempty(collection.get("snapshot_id"), "SNAPSHOT_ID_REQUIRED"), + "snapshot_sha256": _nonempty( + collection.get("snapshot_sha256"), "SNAPSHOT_SHA256_REQUIRED" + ), + "result_status": result_status, + "selected_hits": selected_hits, + "dropped_hits": sorted( + dropped, + key=lambda value: ( + str(value.get("chunk_id", "")), + str(value.get("drop_reason_code", "")), + ), + ), + "issue_codes": sorted(issue_codes), + } + return {**core, "result_sha256": _digest(core)}, receipts diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c28_weaviate_retrieve.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c28_weaviate_retrieve.txt new file mode 100644 index 00000000..4363ede8 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c28_weaviate_retrieve.txt @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 +"""C28 exact-filter retrieval and canonical result/receipt normalization.""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Callable, Mapping, Sequence + + +class RetrievalError(RuntimeError): + """Controlled C28 transport or contract failure.""" + + +RESPONSE_KEYS = frozenset( + { + "schema_version", "request_id", "request_sha256", "response_status", + "raw_hit_count", "selected_hits", "dropped_hits", "deterministic_order_digest", + "retry_count", + } +) +HIT_KEYS = frozenset( + { + "chunk_id", "chunk_sha256", "source_document_id", "source_document_sha256", + "source_locator", "pre_rerank_rank", "hybrid_score", "post_rerank_rank", + "reranker_score", "metadata", + } +) +METADATA_KEYS = frozenset( + { + "schema_version", "document_id", "document_sha256", "chunk_id", "chunk_sha256", + "source_path", "source_locator", "case_type_ids", "sub_rule_ids", + "rule_branch_keys", "corpus_scope_ids", "element_set_ids", "corpus_element_ids", + "doctrine_roles", "jurisdiction", "effective_from", "effective_to", "authority_ids", + "proposition_ids", "approval_status", "legal_reviewer_receipt_ref", + "legal_reviewer_receipt_sha256", + } +) +DROP_KEYS = frozenset({"chunk_id", "chunk_sha256", "drop_reason_code"}) +DROP_REASONS = frozenset( + {"FILTER_MISMATCH", "UNAPPROVED_CHUNK", "HASH_MISMATCH", "SNAPSHOT_MISMATCH", "CROSSWALK_GAP", "DUPLICATE_CHUNK"} +) + + +def _canonical(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + + +def _digest(value: Any) -> str: + return hashlib.sha256(_canonical(value)).hexdigest() + + +def _nonempty(value: Any, code: str) -> str: + if not isinstance(value, str) or not value: + raise RetrievalError(code) + return value + + +def build_request(query_row: Mapping[str, Any], collection: Mapping[str, Any]) -> dict[str, Any]: + """Build the exact Weaviate ``hybrid_search_request`` contract.""" + + if query_row.get("query_status") != "READY": + raise RetrievalError("QUERY_ROW_NOT_READY") + filter_value = query_row.get("filter") + if not isinstance(filter_value, Mapping): + raise RetrievalError("QUERY_FILTER_REQUIRED") + request_seed = { + "query_id": query_row.get("query_id"), + "query_sha256": query_row.get("query_sha256"), + "snapshot_sha256": collection.get("snapshot_sha256"), + } + return { + "schema_version": "stage2_weaviate_hybrid_search_request.v1", + "request_id": f"REQ-{_digest(request_seed)[:24]}", + "query_plan_id": _nonempty(query_row.get("query_id"), "QUERY_ID_REQUIRED"), + "atomic_claim_id": _nonempty( + query_row.get("atomic_claim_id"), "ATOMIC_CLAIM_ID_REQUIRED" + ), + "collection_name": _nonempty( + collection.get("collection_name"), "COLLECTION_NAME_REQUIRED" + ), + "tenant": _nonempty(collection.get("tenant"), "TENANT_REQUIRED"), + "query_text": _nonempty(query_row.get("query_text"), "QUERY_TEXT_REQUIRED"), + "query_sha256": _nonempty(query_row.get("query_sha256"), "QUERY_SHA256_REQUIRED"), + "filter": dict(filter_value), + "alpha": query_row.get("alpha"), + "top_k": query_row.get("top_k"), + "embedding_configuration_digest": query_row.get("embedding_configuration_digest"), + "reranker_configuration_digest": query_row.get("reranker_configuration_digest"), + "index_configuration_digest": query_row.get("index_configuration_digest"), + "snapshot_sha256": _nonempty( + collection.get("snapshot_sha256"), "SNAPSHOT_SHA256_REQUIRED" + ), + } + + +def _drop(hit: Mapping[str, Any], reason: str) -> dict[str, Any]: + return { + "chunk_id": _nonempty(hit.get("chunk_id"), "HIT_CHUNK_ID_REQUIRED"), + "chunk_sha256": _nonempty(hit.get("chunk_sha256"), "HIT_CHUNK_SHA256_REQUIRED"), + "drop_reason_code": reason, + } + + +def _hit_filter_mismatch(hit: Mapping[str, Any], filter_value: Mapping[str, Any]) -> str | None: + if set(hit) != HIT_KEYS: + raise RetrievalError("RETRIEVAL_HIT_KEYS_INVALID") + metadata = hit.get("metadata") + if not isinstance(metadata, Mapping): + raise RetrievalError("RETRIEVAL_HIT_METADATA_INVALID") + if set(metadata) != METADATA_KEYS: + raise RetrievalError("RETRIEVAL_HIT_METADATA_KEYS_INVALID") + if metadata.get("approval_status") != "APPROVED": + return "UNAPPROVED_CHUNK" + scalar_to_set = { + "case_type_id": "case_type_ids", + "sub_rule_id": "sub_rule_ids", + "rule_branch_key": "rule_branch_keys", + "corpus_scope_id": "corpus_scope_ids", + "element_set_id": "element_set_ids", + "doctrine_role": "doctrine_roles", + } + for filter_key, metadata_key in scalar_to_set.items(): + values = metadata.get(metadata_key) + if not isinstance(values, list) or filter_value.get(filter_key) not in values: + return "FILTER_MISMATCH" + if metadata.get("jurisdiction") != "KR": + return "FILTER_MISMATCH" + if hit.get("chunk_sha256") != metadata.get("chunk_sha256"): + return "HASH_MISMATCH" + return None + + +def _receipt( + *, + query_plan: Mapping[str, Any], + query_row: Mapping[str, Any], + request: Mapping[str, Any], + response: Mapping[str, Any], + collection: Mapping[str, Any], + credential_ref: str, +) -> dict[str, Any]: + configuration = { + key: query_row.get(key) + for key in ( + "alpha", + "top_k", + "embedding_configuration_digest", + "reranker_configuration_digest", + "index_configuration_digest", + ) + } + core = { + "schema_version": "stage2_requirement_fact_retrieval_receipt.v1", + "run_binding_digest": query_plan["run_binding_digest"], + "atomic_claim_id": query_plan["atomic_claim_id"], + "query_plan_sha256": query_plan["query_plan_sha256"], + "request_sha256": _digest(request), + "response_sha256": _digest(response), + "transport": "MCP_OVER_HTTP_JSON_RPC", + "endpoint": "https://weaviate.eroomai.com/mcp", + "tool_name": "search_hybrid", + "collection_name": collection["collection_name"], + "tenant": collection["tenant"], + "snapshot_sha256": collection["snapshot_sha256"], + "filter_sha256": _digest(query_row["filter"]), + "configuration_digest": _digest(configuration), + "attempt_count": int(response.get("retry_count", 0)) + 1, + "replay_status": "NOT_REQUESTED", + "read_only_verified": True, + "credential_ref": _nonempty(credential_ref, "CREDENTIAL_REF_REQUIRED"), + "credential_material_persisted": False, + } + return {**core, "receipt_sha256": _digest(core)} + + +def retrieve_exact( + query_plan: Mapping[str, Any], + collection: Mapping[str, Any], + search_hybrid: Callable[[Mapping[str, Any]], Mapping[str, Any]], + *, + credential_ref: str, +) -> tuple[dict[str, Any], list[dict[str, Any]]]: + """Execute READY rows without fallback and return canonical result + receipts.""" + + rows = query_plan.get("rows") + if not isinstance(rows, Sequence) or isinstance(rows, (str, bytes)): + raise RetrievalError("QUERY_PLAN_ROWS_INVALID") + selected: list[dict[str, Any]] = [] + dropped: list[dict[str, Any]] = [] + receipts: list[dict[str, Any]] = [] + issue_codes: set[str] = set() + technical_incomplete = False + for query_row in rows: + if not isinstance(query_row, Mapping) or query_row.get("query_status") != "READY": + raise RetrievalError("QUERY_PLAN_READY_ROW_REQUIRED") + request = build_request(query_row, collection) + request_sha256 = _digest(request) + response = search_hybrid(request) + if not isinstance(response, Mapping) or response.get("request_sha256") != request_sha256: + raise RetrievalError("RETRIEVAL_REQUEST_ECHO_MISMATCH") + if set(response) != RESPONSE_KEYS: + raise RetrievalError("RETRIEVAL_RESPONSE_KEYS_INVALID") + if response.get("schema_version") != "stage2_weaviate_hybrid_search_response.v1": + raise RetrievalError("RETRIEVAL_RESPONSE_SCHEMA_MISMATCH") + if response.get("request_id") != request["request_id"]: + raise RetrievalError("RETRIEVAL_REQUEST_ID_ECHO_MISMATCH") + retry_count = response.get("retry_count") + if not isinstance(retry_count, int) or isinstance(retry_count, bool) or not 0 <= retry_count <= 2: + raise RetrievalError("RETRIEVAL_RETRY_COUNT_INVALID") + response_status = response.get("response_status") + if response_status not in {"COMPLETE", "NO_HIT", "TECHNICAL_INCOMPLETE"}: + raise RetrievalError("RETRIEVAL_RESPONSE_STATUS_INVALID") + if response_status == "TECHNICAL_INCOMPLETE": + technical_incomplete = True + issue_codes.add("RETRIEVAL_TECHNICAL_INCOMPLETE") + raw_hits = response.get("selected_hits") + raw_drops = response.get("dropped_hits") + if not isinstance(raw_hits, list) or not isinstance(raw_drops, list): + raise RetrievalError("RETRIEVAL_RESPONSE_ROWS_INVALID") + for hit in raw_hits: + if not isinstance(hit, Mapping): + raise RetrievalError("RETRIEVAL_HIT_INVALID") + reason = _hit_filter_mismatch(hit, query_row["filter"]) + if reason is None: + selected.append(dict(hit)) + else: + dropped.append(_drop(hit, reason)) + issue_codes.add(reason) + for row in raw_drops: + if ( + not isinstance(row, Mapping) + or set(row) != DROP_KEYS + or row.get("drop_reason_code") not in DROP_REASONS + ): + raise RetrievalError("DROPPED_HIT_INVALID") + dropped.append(dict(row)) + receipts.append( + _receipt( + query_plan=query_plan, + query_row=query_row, + request=request, + response=response, + collection=collection, + credential_ref=credential_ref, + ) + ) + + unique: dict[str, dict[str, Any]] = {} + for hit in sorted( + selected, + key=lambda value: ( + int(value.get("post_rerank_rank", 2**31 - 1)), + -float(value.get("reranker_score") or value.get("hybrid_score") or 0), + str(value.get("chunk_id", "")), + ), + ): + chunk_id = _nonempty(hit.get("chunk_id"), "HIT_CHUNK_ID_REQUIRED") + if chunk_id in unique: + dropped.append(_drop(hit, "DUPLICATE_CHUNK")) + issue_codes.add("DUPLICATE_CHUNK") + else: + unique[chunk_id] = hit + selected_hits = list(unique.values()) + result_status = ( + "NOT_RUN_UNRESOLVED_BINDING" + if not rows + else "TECHNICAL_INCOMPLETE" + if technical_incomplete + else "COMPLETE" + if selected_hits + else "NO_HIT" + ) + if not rows: + issue_codes.add("QUERY_NOT_RUN_UNRESOLVED_BINDING") + core = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": query_plan["run_binding_digest"], + "atomic_claim_id": query_plan["atomic_claim_id"], + "query_plan_sha256": query_plan["query_plan_sha256"], + "snapshot_id": _nonempty(collection.get("snapshot_id"), "SNAPSHOT_ID_REQUIRED"), + "snapshot_sha256": _nonempty( + collection.get("snapshot_sha256"), "SNAPSHOT_SHA256_REQUIRED" + ), + "result_status": result_status, + "selected_hits": selected_hits, + "dropped_hits": sorted( + dropped, + key=lambda value: ( + str(value.get("chunk_id", "")), + str(value.get("drop_reason_code", "")), + ), + ), + "issue_codes": sorted(issue_codes), + } + return {**core, "result_sha256": _digest(core)}, receipts diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c29_pack_verify.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c29_pack_verify.py new file mode 100644 index 00000000..4ee4b1cf --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c29_pack_verify.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +"""C29 retrieval provenance and Stage-1-slot crosswalk verifier.""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Mapping, Sequence + + +class PackError(ValueError): + """Controlled C29 contract failure.""" + + +def _digest(value: Any) -> str: + raw = ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + return hashlib.sha256(raw).hexdigest() + + +def _sha256(value: Any, code: str) -> str: + if ( + not isinstance(value, str) + or len(value) != 64 + or any(character not in "0123456789abcdef" for character in value) + ): + raise PackError(code) + return value + + +def verify_pack( + binding: Mapping[str, Any], + retrieval_result: Mapping[str, Any], + corpus_release: Mapping[str, Any], + crosswalk_rows: Sequence[Mapping[str, Any]], + *, + claim_group_id: str, + retrieval_receipt_refs: Sequence[Mapping[str, Any]], +) -> dict[str, Any]: + """Return the exact ``requirement_fact_pack`` schema contract.""" + + run_binding_digest = retrieval_result.get("run_binding_digest") + atomic_claim_id = retrieval_result.get("atomic_claim_id") + run_binding_digest = _sha256(run_binding_digest, "RUN_BINDING_DIGEST_INVALID") + if not isinstance(atomic_claim_id, str) or not atomic_claim_id: + raise PackError("ATOMIC_CLAIM_ID_REQUIRED") + expected_snapshot = corpus_release.get("snapshot_id") + expected_snapshot_hash = corpus_release.get("snapshot_sha256") + if ( + retrieval_result.get("snapshot_id") != expected_snapshot + or retrieval_result.get("snapshot_sha256") != expected_snapshot_hash + ): + raise PackError("RETRIEVAL_RESULT_SNAPSHOT_MISMATCH") + corpus_release_sha256 = _sha256( + corpus_release.get("corpus_release_sha256"), "CORPUS_RELEASE_SHA256_INVALID" + ) + expected_snapshot_hash = _sha256(expected_snapshot_hash, "SNAPSHOT_SHA256_INVALID") + + crosswalk = { + (row.get("corpus_element_id"), row.get("doctrine_role")): row + for row in crosswalk_rows + if isinstance(row, Mapping) + } + items: list[dict[str, Any]] = [] + unmapped: set[str] = set() + conflicting: set[str] = set() + issue_codes: set[str] = set(retrieval_result.get("issue_codes", [])) + for hit in retrieval_result.get("selected_hits", []): + if not isinstance(hit, Mapping): + raise PackError("RETRIEVAL_HIT_INVALID") + metadata = hit.get("metadata") + if not isinstance(metadata, Mapping): + raise PackError("RETRIEVAL_HIT_METADATA_REQUIRED") + chunk_id = hit.get("chunk_id") + if not isinstance(chunk_id, str) or not chunk_id: + raise PackError("CHUNK_ID_REQUIRED") + mismatch = ( + binding.get("case_type_id") not in metadata.get("case_type_ids", []) + or binding.get("sub_rule_id") not in metadata.get("sub_rule_ids", []) + or binding.get("rule_branch_key") not in metadata.get("rule_branch_keys", []) + ) + if mismatch: + conflicting.add(chunk_id) + issue_codes.add("RETRIEVAL_BINDING_MISMATCH") + continue + for corpus_element_id in sorted(set(metadata.get("corpus_element_ids", []))): + for doctrine_role in sorted(set(metadata.get("doctrine_roles", []))): + row = crosswalk.get((corpus_element_id, doctrine_role)) + if row is None or row.get("mapping_status") in {None, "UNMAPPED"}: + unmapped.add(chunk_id) + issue_codes.add("SLOT_CROSSWALK_MISSING") + continue + if row.get("mapping_status") == "CONFLICT": + conflicting.add(chunk_id) + issue_codes.add("SLOT_CROSSWALK_CONFLICT") + continue + items.append( + { + "corpus_element_id": corpus_element_id, + "doctrine_role": doctrine_role, + "chunk_id": chunk_id, + "chunk_sha256": hit["chunk_sha256"], + "source_document_id": hit["source_document_id"], + "source_document_sha256": hit["source_document_sha256"], + "source_locator": hit["source_locator"], + "authority_ids": sorted(set(metadata.get("authority_ids", []))), + "proposition_ids": sorted(set(metadata.get("proposition_ids", []))), + "stage1_slot_refs": sorted(set(row.get("stage1_slot_refs", []))), + "mapping_status": row["mapping_status"], + "fact_creation_allowed": False, + "legal_decision_override_allowed": False, + } + ) + items.sort( + key=lambda item: ( + item["corpus_element_id"], + item["doctrine_role"], + item["chunk_id"], + ) + ) + core = { + "schema_version": "stage2_requirement_fact_pack.v1", + "run_binding_digest": run_binding_digest, + "claim_group_id": claim_group_id, + "atomic_claim_ids": [atomic_claim_id], + "corpus_release_sha256": corpus_release_sha256, + "snapshot_id": expected_snapshot, + "snapshot_sha256": expected_snapshot_hash, + "retrieval_receipt_refs": [dict(row) for row in retrieval_receipt_refs], + "items": items, + "unmapped_chunk_ids": sorted(unmapped), + "conflicting_chunk_ids": sorted(conflicting), + "authority_class": "CAUSE_AND_ELEMENT_CHECK", + "issue_codes": sorted(issue_codes), + } + return {**core, "pack_sha256": _digest(core)} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c29_pack_verify.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c29_pack_verify.txt new file mode 100644 index 00000000..4ee4b1cf --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/c29_pack_verify.txt @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +"""C29 retrieval provenance and Stage-1-slot crosswalk verifier.""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Mapping, Sequence + + +class PackError(ValueError): + """Controlled C29 contract failure.""" + + +def _digest(value: Any) -> str: + raw = ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + return hashlib.sha256(raw).hexdigest() + + +def _sha256(value: Any, code: str) -> str: + if ( + not isinstance(value, str) + or len(value) != 64 + or any(character not in "0123456789abcdef" for character in value) + ): + raise PackError(code) + return value + + +def verify_pack( + binding: Mapping[str, Any], + retrieval_result: Mapping[str, Any], + corpus_release: Mapping[str, Any], + crosswalk_rows: Sequence[Mapping[str, Any]], + *, + claim_group_id: str, + retrieval_receipt_refs: Sequence[Mapping[str, Any]], +) -> dict[str, Any]: + """Return the exact ``requirement_fact_pack`` schema contract.""" + + run_binding_digest = retrieval_result.get("run_binding_digest") + atomic_claim_id = retrieval_result.get("atomic_claim_id") + run_binding_digest = _sha256(run_binding_digest, "RUN_BINDING_DIGEST_INVALID") + if not isinstance(atomic_claim_id, str) or not atomic_claim_id: + raise PackError("ATOMIC_CLAIM_ID_REQUIRED") + expected_snapshot = corpus_release.get("snapshot_id") + expected_snapshot_hash = corpus_release.get("snapshot_sha256") + if ( + retrieval_result.get("snapshot_id") != expected_snapshot + or retrieval_result.get("snapshot_sha256") != expected_snapshot_hash + ): + raise PackError("RETRIEVAL_RESULT_SNAPSHOT_MISMATCH") + corpus_release_sha256 = _sha256( + corpus_release.get("corpus_release_sha256"), "CORPUS_RELEASE_SHA256_INVALID" + ) + expected_snapshot_hash = _sha256(expected_snapshot_hash, "SNAPSHOT_SHA256_INVALID") + + crosswalk = { + (row.get("corpus_element_id"), row.get("doctrine_role")): row + for row in crosswalk_rows + if isinstance(row, Mapping) + } + items: list[dict[str, Any]] = [] + unmapped: set[str] = set() + conflicting: set[str] = set() + issue_codes: set[str] = set(retrieval_result.get("issue_codes", [])) + for hit in retrieval_result.get("selected_hits", []): + if not isinstance(hit, Mapping): + raise PackError("RETRIEVAL_HIT_INVALID") + metadata = hit.get("metadata") + if not isinstance(metadata, Mapping): + raise PackError("RETRIEVAL_HIT_METADATA_REQUIRED") + chunk_id = hit.get("chunk_id") + if not isinstance(chunk_id, str) or not chunk_id: + raise PackError("CHUNK_ID_REQUIRED") + mismatch = ( + binding.get("case_type_id") not in metadata.get("case_type_ids", []) + or binding.get("sub_rule_id") not in metadata.get("sub_rule_ids", []) + or binding.get("rule_branch_key") not in metadata.get("rule_branch_keys", []) + ) + if mismatch: + conflicting.add(chunk_id) + issue_codes.add("RETRIEVAL_BINDING_MISMATCH") + continue + for corpus_element_id in sorted(set(metadata.get("corpus_element_ids", []))): + for doctrine_role in sorted(set(metadata.get("doctrine_roles", []))): + row = crosswalk.get((corpus_element_id, doctrine_role)) + if row is None or row.get("mapping_status") in {None, "UNMAPPED"}: + unmapped.add(chunk_id) + issue_codes.add("SLOT_CROSSWALK_MISSING") + continue + if row.get("mapping_status") == "CONFLICT": + conflicting.add(chunk_id) + issue_codes.add("SLOT_CROSSWALK_CONFLICT") + continue + items.append( + { + "corpus_element_id": corpus_element_id, + "doctrine_role": doctrine_role, + "chunk_id": chunk_id, + "chunk_sha256": hit["chunk_sha256"], + "source_document_id": hit["source_document_id"], + "source_document_sha256": hit["source_document_sha256"], + "source_locator": hit["source_locator"], + "authority_ids": sorted(set(metadata.get("authority_ids", []))), + "proposition_ids": sorted(set(metadata.get("proposition_ids", []))), + "stage1_slot_refs": sorted(set(row.get("stage1_slot_refs", []))), + "mapping_status": row["mapping_status"], + "fact_creation_allowed": False, + "legal_decision_override_allowed": False, + } + ) + items.sort( + key=lambda item: ( + item["corpus_element_id"], + item["doctrine_role"], + item["chunk_id"], + ) + ) + core = { + "schema_version": "stage2_requirement_fact_pack.v1", + "run_binding_digest": run_binding_digest, + "claim_group_id": claim_group_id, + "atomic_claim_ids": [atomic_claim_id], + "corpus_release_sha256": corpus_release_sha256, + "snapshot_id": expected_snapshot, + "snapshot_sha256": expected_snapshot_hash, + "retrieval_receipt_refs": [dict(row) for row in retrieval_receipt_refs], + "items": items, + "unmapped_chunk_ids": sorted(unmapped), + "conflicting_chunk_ids": sorted(conflicting), + "authority_class": "CAUSE_AND_ELEMENT_CHECK", + "issue_codes": sorted(issue_codes), + } + return {**core, "pack_sha256": _digest(core)} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/calculators/registry_engine.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/calculators/registry_engine.py new file mode 100644 index 00000000..d7e7dc97 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/calculators/registry_engine.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 +"""Safe Decimal interpreter that emits canonical S2_20 calculation receipts.""" + +from __future__ import annotations + +from decimal import Decimal, InvalidOperation, ROUND_DOWN, ROUND_HALF_UP, ROUND_UP +import hashlib +import json +import re +from typing import Any, Mapping, Sequence + + +OPERATORS = frozenset({"ADD", "SUBTRACT", "MULTIPLY", "DIVIDE", "MIN", "MAX"}) +ROUNDING_MODES = { + "ROUND_HALF_UP": ROUND_HALF_UP, + "ROUND_DOWN": ROUND_DOWN, + "ROUND_UP": ROUND_UP, + "TRUNCATE": ROUND_DOWN, +} +CALCULATOR_ID = re.compile(r"^CE-(?:0[1-9]|1[0-3]|R[1-4])$") +SHA256 = re.compile(r"^[a-f0-9]{64}$") + + +class CalculationError(ValueError): + """Controlled calculator contract failure.""" + + +def _decimal(value: Any) -> Decimal: + try: + return Decimal(str(value)) + except (InvalidOperation, ValueError) as exc: + raise CalculationError("INVALID_DECIMAL_OPERAND") from exc + + +def _canonical(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + + +def _digest(value: Any) -> str: + return hashlib.sha256(_canonical(value)).hexdigest() + + +def _sha256(value: Any, code: str) -> str: + if not isinstance(value, str) or SHA256.fullmatch(value) is None: + raise CalculationError(code) + return value + + +def activate_calculators( + signature: Mapping[str, Any], + requested_metrics: Sequence[str], + activation_rows: Sequence[Mapping[str, Any]], +) -> list[dict[str, Any]]: + """Evaluate the closed activation registry without a default branch.""" + + rows: list[dict[str, Any]] = [] + for rule in sorted( + activation_rows, + key=lambda row: (str(row.get("calculator_id")), str(row.get("activation_row_id"))), + ): + calculator_id = rule.get("calculator_id") + allowed_metrics = rule.get("requested_metrics", []) + predicates = rule.get("equals", {}) + if ( + not isinstance(calculator_id, str) + or CALCULATOR_ID.fullmatch(calculator_id) is None + or not isinstance(allowed_metrics, list) + or not isinstance(predicates, Mapping) + or not predicates + ): + raise CalculationError("INVALID_ACTIVATION_ROW") + metric_match = bool(set(requested_metrics) & set(allowed_metrics)) + predicate_match = all(signature.get(key, "UNKNOWN") == value for key, value in predicates.items()) + rows.append( + { + "calculator_id": calculator_id, + "activation_row_id": rule.get("activation_row_id"), + "activation_status": "ACTIVATED" if metric_match and predicate_match else "NOT_APPLICABLE", + } + ) + return rows + + +def _operand_row(operand_id: str, supplied: Any, unit_id: str, currency: str | None) -> dict[str, Any]: + if supplied is None: + return { + "operand_id": operand_id, + "source_ref": f"OPERAND:{operand_id}", + "status": "MISSING", + "value_type": "DECIMAL", + "value": None, + "unit_id": unit_id, + "currency": currency, + "value_sha256": None, + } + if isinstance(supplied, Mapping): + status = supplied.get("status", "PRESENT") + value = supplied.get("value") + source_ref = supplied.get("source_ref", f"OPERAND:{operand_id}") + value_type = supplied.get("value_type", "DECIMAL") + row_unit = supplied.get("unit_id", unit_id) + row_currency = supplied.get("currency", currency) + else: + status = "PRESENT" + value = supplied + source_ref = f"OPERAND:{operand_id}" + value_type = "DECIMAL" + row_unit = unit_id + row_currency = currency + if status not in {"PRESENT", "MISSING", "CONFLICT"}: + raise CalculationError("OPERAND_STATUS_INVALID") + if status != "PRESENT": + value = None + normalized = format(_decimal(value), "f") if status == "PRESENT" and value_type == "DECIMAL" else value + return { + "operand_id": operand_id, + "source_ref": str(source_ref), + "status": status, + "value_type": value_type, + "value": normalized, + "unit_id": row_unit, + "currency": row_currency, + "value_sha256": _digest(normalized) if status == "PRESENT" else None, + } + + +def _law_value_uses( + law_value_ids: Sequence[str], law_values: Mapping[str, Mapping[str, Any]] +) -> tuple[list[dict[str, Any]], list[str]]: + rows: list[dict[str, Any]] = [] + missing: list[str] = [] + for law_value_id in law_value_ids: + source = law_values.get(law_value_id) + if not isinstance(source, Mapping) or source.get("approval_status") != "APPROVED": + missing.append(law_value_id) + continue + rows.append( + { + "law_value_id": law_value_id, + "authority_id": source["authority_id"], + "authority_release_sha256": _sha256( + source.get("authority_release_sha256"), "AUTHORITY_RELEASE_SHA256_INVALID" + ), + "law_value_release_sha256": _sha256( + source.get("law_value_release_sha256"), "LAW_VALUE_RELEASE_SHA256_INVALID" + ), + "effective_interval": dict(source["effective_interval"]), + "value": format(_decimal(source["value"]), "f"), + "unit_id": source["unit_id"], + "approval_status": "APPROVED", + } + ) + return rows, missing + + +def execute_formula( + calculator: Mapping[str, Any], + operands: Mapping[str, Any], + law_values: Mapping[str, Mapping[str, Any]], + *, + run_binding_digest: str, + atomic_claim_id: str, + claim_group_id: str, + activation_row_id: str, + descriptor_sha256: str, +) -> dict[str, Any]: + """Execute one admitted formula and emit every required receipt field.""" + + run_binding_digest = _sha256(run_binding_digest, "RUN_BINDING_DIGEST_INVALID") + descriptor_sha256 = _sha256(descriptor_sha256, "DESCRIPTOR_SHA256_INVALID") + calculator_id = calculator.get("calculator_id") + if not isinstance(calculator_id, str) or CALCULATOR_ID.fullmatch(calculator_id) is None: + raise CalculationError("CALCULATOR_ID_INVALID") + formula_id = calculator.get("formula_id") + if not isinstance(formula_id, str) or not formula_id: + raise CalculationError("FORMULA_ID_REQUIRED") + required = calculator.get("input_operand_ids", calculator.get("required_operands", [])) + if not isinstance(required, list) or not required or not all( + isinstance(item, str) and item for item in required + ): + raise CalculationError("FORMULA_OPERANDS_INVALID") + output_metric_id = calculator.get("output_metric_id") + output_unit_id = calculator.get("output_unit_id", calculator.get("unit")) + if not isinstance(output_metric_id, str) or not output_metric_id: + raise CalculationError("OUTPUT_METRIC_ID_REQUIRED") + if not isinstance(output_unit_id, str) or not output_unit_id: + raise CalculationError("OUTPUT_UNIT_ID_REQUIRED") + currency = calculator.get("currency") + rounding_rule = calculator.get("rounding_rule") + effective_interval = calculator.get("effective_interval") + if not isinstance(rounding_rule, Mapping) or rounding_rule.get("mode") not in ROUNDING_MODES: + raise CalculationError("ROUNDING_RULE_INVALID") + if not isinstance(effective_interval, Mapping): + raise CalculationError("EFFECTIVE_INTERVAL_REQUIRED") + formula_scope = {key: value for key, value in calculator.items() if key != "formula_sha256"} + formula_sha256 = calculator.get("formula_sha256") or _digest(formula_scope) + formula_sha256 = _sha256(formula_sha256, "FORMULA_SHA256_INVALID") + + operand_rows = [ + _operand_row(operand_id, operands.get(operand_id), output_unit_id, currency) + for operand_id in required + ] + missing = sorted( + row["operand_id"] for row in operand_rows if row["status"] != "PRESENT" + ) + law_value_ids = calculator.get("law_value_ids", []) + if not isinstance(law_value_ids, list): + raise CalculationError("LAW_VALUE_IDS_INVALID") + law_value_uses, missing_law_values = _law_value_uses(law_value_ids, law_values) + issue_codes: list[str] = [] + calculation_status = "CALCULATED" + if missing: + calculation_status = "MISSING_OPERAND" + issue_codes.append("CALCULATION_OPERAND_MISSING") + elif missing_law_values: + calculation_status = "LAW_VALUE_GAP" + issue_codes.extend(f"CALCULATION_LAW_VALUE_UNRESOLVED:{item}" for item in missing_law_values) + + result: dict[str, Any] | None = None + result_sha256: str | None = None + if calculation_status == "CALCULATED": + operator = calculator.get("operator") + if operator not in OPERATORS: + raise CalculationError("UNSUPPORTED_FORMULA_OPERATOR") + values = [_decimal(operands[name]["value"] if isinstance(operands[name], Mapping) else operands[name]) for name in required] + if operator == "ADD": + computed = sum(values, Decimal("0")) + elif operator == "SUBTRACT": + computed = values[0] - sum(values[1:], Decimal("0")) + elif operator == "MULTIPLY": + computed = Decimal("1") + for value in values: + computed *= value + elif operator == "DIVIDE": + if len(values) != 2 or values[1] == 0: + raise CalculationError("INVALID_DIVISION") + computed = values[0] / values[1] + elif operator == "MIN": + computed = min(values) + else: + computed = max(values) + quantum = _decimal(rounding_rule["quantum"]) + rounded = format(computed.quantize(quantum, rounding=ROUNDING_MODES[rounding_rule["mode"]]), "f") + result = { + "metric_id": output_metric_id, + "value": rounded, + "unit_id": output_unit_id, + "currency": currency, + } + result_sha256 = _digest(result) + + identity = { + "run_binding_digest": run_binding_digest, + "atomic_claim_id": atomic_claim_id, + "claim_group_id": claim_group_id, + "calculator_id": calculator_id, + "formula_id": formula_id, + } + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": f"CR-{_digest(identity)[:24]}", + "run_binding_digest": run_binding_digest, + "atomic_claim_id": atomic_claim_id, + "claim_group_id": claim_group_id, + "calculator_id": calculator_id, + "activation_status": "ACTIVATED", + "calculation_status": calculation_status, + "activation_row_id": activation_row_id, + "descriptor_sha256": descriptor_sha256, + "formula_id": formula_id, + "formula_sha256": formula_sha256, + "operand_rows": operand_rows, + "operand_refs": sorted(required), + "units": sorted({row["unit_id"] for row in operand_rows if row["unit_id"]}), + "law_value_uses": law_value_uses, + "effective_interval": dict(effective_interval), + "rounding_rule": dict(rounding_rule), + "period_rows": [], + "missing_operands": missing, + "result": result, + "result_sha256": result_sha256, + "issue_codes": sorted(issue_codes), + } + return {**core, "receipt_sha256": _digest(core)} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/calculators/registry_engine.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/calculators/registry_engine.txt new file mode 100644 index 00000000..d7e7dc97 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/calculators/registry_engine.txt @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 +"""Safe Decimal interpreter that emits canonical S2_20 calculation receipts.""" + +from __future__ import annotations + +from decimal import Decimal, InvalidOperation, ROUND_DOWN, ROUND_HALF_UP, ROUND_UP +import hashlib +import json +import re +from typing import Any, Mapping, Sequence + + +OPERATORS = frozenset({"ADD", "SUBTRACT", "MULTIPLY", "DIVIDE", "MIN", "MAX"}) +ROUNDING_MODES = { + "ROUND_HALF_UP": ROUND_HALF_UP, + "ROUND_DOWN": ROUND_DOWN, + "ROUND_UP": ROUND_UP, + "TRUNCATE": ROUND_DOWN, +} +CALCULATOR_ID = re.compile(r"^CE-(?:0[1-9]|1[0-3]|R[1-4])$") +SHA256 = re.compile(r"^[a-f0-9]{64}$") + + +class CalculationError(ValueError): + """Controlled calculator contract failure.""" + + +def _decimal(value: Any) -> Decimal: + try: + return Decimal(str(value)) + except (InvalidOperation, ValueError) as exc: + raise CalculationError("INVALID_DECIMAL_OPERAND") from exc + + +def _canonical(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" + ).encode("utf-8") + + +def _digest(value: Any) -> str: + return hashlib.sha256(_canonical(value)).hexdigest() + + +def _sha256(value: Any, code: str) -> str: + if not isinstance(value, str) or SHA256.fullmatch(value) is None: + raise CalculationError(code) + return value + + +def activate_calculators( + signature: Mapping[str, Any], + requested_metrics: Sequence[str], + activation_rows: Sequence[Mapping[str, Any]], +) -> list[dict[str, Any]]: + """Evaluate the closed activation registry without a default branch.""" + + rows: list[dict[str, Any]] = [] + for rule in sorted( + activation_rows, + key=lambda row: (str(row.get("calculator_id")), str(row.get("activation_row_id"))), + ): + calculator_id = rule.get("calculator_id") + allowed_metrics = rule.get("requested_metrics", []) + predicates = rule.get("equals", {}) + if ( + not isinstance(calculator_id, str) + or CALCULATOR_ID.fullmatch(calculator_id) is None + or not isinstance(allowed_metrics, list) + or not isinstance(predicates, Mapping) + or not predicates + ): + raise CalculationError("INVALID_ACTIVATION_ROW") + metric_match = bool(set(requested_metrics) & set(allowed_metrics)) + predicate_match = all(signature.get(key, "UNKNOWN") == value for key, value in predicates.items()) + rows.append( + { + "calculator_id": calculator_id, + "activation_row_id": rule.get("activation_row_id"), + "activation_status": "ACTIVATED" if metric_match and predicate_match else "NOT_APPLICABLE", + } + ) + return rows + + +def _operand_row(operand_id: str, supplied: Any, unit_id: str, currency: str | None) -> dict[str, Any]: + if supplied is None: + return { + "operand_id": operand_id, + "source_ref": f"OPERAND:{operand_id}", + "status": "MISSING", + "value_type": "DECIMAL", + "value": None, + "unit_id": unit_id, + "currency": currency, + "value_sha256": None, + } + if isinstance(supplied, Mapping): + status = supplied.get("status", "PRESENT") + value = supplied.get("value") + source_ref = supplied.get("source_ref", f"OPERAND:{operand_id}") + value_type = supplied.get("value_type", "DECIMAL") + row_unit = supplied.get("unit_id", unit_id) + row_currency = supplied.get("currency", currency) + else: + status = "PRESENT" + value = supplied + source_ref = f"OPERAND:{operand_id}" + value_type = "DECIMAL" + row_unit = unit_id + row_currency = currency + if status not in {"PRESENT", "MISSING", "CONFLICT"}: + raise CalculationError("OPERAND_STATUS_INVALID") + if status != "PRESENT": + value = None + normalized = format(_decimal(value), "f") if status == "PRESENT" and value_type == "DECIMAL" else value + return { + "operand_id": operand_id, + "source_ref": str(source_ref), + "status": status, + "value_type": value_type, + "value": normalized, + "unit_id": row_unit, + "currency": row_currency, + "value_sha256": _digest(normalized) if status == "PRESENT" else None, + } + + +def _law_value_uses( + law_value_ids: Sequence[str], law_values: Mapping[str, Mapping[str, Any]] +) -> tuple[list[dict[str, Any]], list[str]]: + rows: list[dict[str, Any]] = [] + missing: list[str] = [] + for law_value_id in law_value_ids: + source = law_values.get(law_value_id) + if not isinstance(source, Mapping) or source.get("approval_status") != "APPROVED": + missing.append(law_value_id) + continue + rows.append( + { + "law_value_id": law_value_id, + "authority_id": source["authority_id"], + "authority_release_sha256": _sha256( + source.get("authority_release_sha256"), "AUTHORITY_RELEASE_SHA256_INVALID" + ), + "law_value_release_sha256": _sha256( + source.get("law_value_release_sha256"), "LAW_VALUE_RELEASE_SHA256_INVALID" + ), + "effective_interval": dict(source["effective_interval"]), + "value": format(_decimal(source["value"]), "f"), + "unit_id": source["unit_id"], + "approval_status": "APPROVED", + } + ) + return rows, missing + + +def execute_formula( + calculator: Mapping[str, Any], + operands: Mapping[str, Any], + law_values: Mapping[str, Mapping[str, Any]], + *, + run_binding_digest: str, + atomic_claim_id: str, + claim_group_id: str, + activation_row_id: str, + descriptor_sha256: str, +) -> dict[str, Any]: + """Execute one admitted formula and emit every required receipt field.""" + + run_binding_digest = _sha256(run_binding_digest, "RUN_BINDING_DIGEST_INVALID") + descriptor_sha256 = _sha256(descriptor_sha256, "DESCRIPTOR_SHA256_INVALID") + calculator_id = calculator.get("calculator_id") + if not isinstance(calculator_id, str) or CALCULATOR_ID.fullmatch(calculator_id) is None: + raise CalculationError("CALCULATOR_ID_INVALID") + formula_id = calculator.get("formula_id") + if not isinstance(formula_id, str) or not formula_id: + raise CalculationError("FORMULA_ID_REQUIRED") + required = calculator.get("input_operand_ids", calculator.get("required_operands", [])) + if not isinstance(required, list) or not required or not all( + isinstance(item, str) and item for item in required + ): + raise CalculationError("FORMULA_OPERANDS_INVALID") + output_metric_id = calculator.get("output_metric_id") + output_unit_id = calculator.get("output_unit_id", calculator.get("unit")) + if not isinstance(output_metric_id, str) or not output_metric_id: + raise CalculationError("OUTPUT_METRIC_ID_REQUIRED") + if not isinstance(output_unit_id, str) or not output_unit_id: + raise CalculationError("OUTPUT_UNIT_ID_REQUIRED") + currency = calculator.get("currency") + rounding_rule = calculator.get("rounding_rule") + effective_interval = calculator.get("effective_interval") + if not isinstance(rounding_rule, Mapping) or rounding_rule.get("mode") not in ROUNDING_MODES: + raise CalculationError("ROUNDING_RULE_INVALID") + if not isinstance(effective_interval, Mapping): + raise CalculationError("EFFECTIVE_INTERVAL_REQUIRED") + formula_scope = {key: value for key, value in calculator.items() if key != "formula_sha256"} + formula_sha256 = calculator.get("formula_sha256") or _digest(formula_scope) + formula_sha256 = _sha256(formula_sha256, "FORMULA_SHA256_INVALID") + + operand_rows = [ + _operand_row(operand_id, operands.get(operand_id), output_unit_id, currency) + for operand_id in required + ] + missing = sorted( + row["operand_id"] for row in operand_rows if row["status"] != "PRESENT" + ) + law_value_ids = calculator.get("law_value_ids", []) + if not isinstance(law_value_ids, list): + raise CalculationError("LAW_VALUE_IDS_INVALID") + law_value_uses, missing_law_values = _law_value_uses(law_value_ids, law_values) + issue_codes: list[str] = [] + calculation_status = "CALCULATED" + if missing: + calculation_status = "MISSING_OPERAND" + issue_codes.append("CALCULATION_OPERAND_MISSING") + elif missing_law_values: + calculation_status = "LAW_VALUE_GAP" + issue_codes.extend(f"CALCULATION_LAW_VALUE_UNRESOLVED:{item}" for item in missing_law_values) + + result: dict[str, Any] | None = None + result_sha256: str | None = None + if calculation_status == "CALCULATED": + operator = calculator.get("operator") + if operator not in OPERATORS: + raise CalculationError("UNSUPPORTED_FORMULA_OPERATOR") + values = [_decimal(operands[name]["value"] if isinstance(operands[name], Mapping) else operands[name]) for name in required] + if operator == "ADD": + computed = sum(values, Decimal("0")) + elif operator == "SUBTRACT": + computed = values[0] - sum(values[1:], Decimal("0")) + elif operator == "MULTIPLY": + computed = Decimal("1") + for value in values: + computed *= value + elif operator == "DIVIDE": + if len(values) != 2 or values[1] == 0: + raise CalculationError("INVALID_DIVISION") + computed = values[0] / values[1] + elif operator == "MIN": + computed = min(values) + else: + computed = max(values) + quantum = _decimal(rounding_rule["quantum"]) + rounded = format(computed.quantize(quantum, rounding=ROUNDING_MODES[rounding_rule["mode"]]), "f") + result = { + "metric_id": output_metric_id, + "value": rounded, + "unit_id": output_unit_id, + "currency": currency, + } + result_sha256 = _digest(result) + + identity = { + "run_binding_digest": run_binding_digest, + "atomic_claim_id": atomic_claim_id, + "claim_group_id": claim_group_id, + "calculator_id": calculator_id, + "formula_id": formula_id, + } + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": f"CR-{_digest(identity)[:24]}", + "run_binding_digest": run_binding_digest, + "atomic_claim_id": atomic_claim_id, + "claim_group_id": claim_group_id, + "calculator_id": calculator_id, + "activation_status": "ACTIVATED", + "calculation_status": calculation_status, + "activation_row_id": activation_row_id, + "descriptor_sha256": descriptor_sha256, + "formula_id": formula_id, + "formula_sha256": formula_sha256, + "operand_rows": operand_rows, + "operand_refs": sorted(required), + "units": sorted({row["unit_id"] for row in operand_rows if row["unit_id"]}), + "law_value_uses": law_value_uses, + "effective_interval": dict(effective_interval), + "rounding_rule": dict(rounding_rule), + "period_rows": [], + "missing_operands": missing, + "result": result, + "result_sha256": result_sha256, + "issue_codes": sorted(issue_codes), + } + return {**core, "receipt_sha256": _digest(core)} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.py index 0dc15e71..9afc2c79 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.py +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.py @@ -149,7 +149,7 @@ _RAW_VALUE_UNSET = object() # literal placeholders in the offline parity mirror and its unit tests. INLINE_USER_HASH = "{{__user_hash__}}" INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}" -EXPECTED_STAGE2_RELEASE_SHA256 = "0f21af3ddca538d9b6a5853dac23222c0b632d0e573de371a8de2709e691699d" +EXPECTED_STAGE2_RELEASE_SHA256 = "70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92" INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json" INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean" INLINE_STAGE2_RELEASE_PATH = ( diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.txt index 0dc15e71..9afc2c79 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.txt +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_00_ingress.txt @@ -149,7 +149,7 @@ _RAW_VALUE_UNSET = object() # literal placeholders in the offline parity mirror and its unit tests. INLINE_USER_HASH = "{{__user_hash__}}" INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}" -EXPECTED_STAGE2_RELEASE_SHA256 = "0f21af3ddca538d9b6a5853dac23222c0b632d0e573de371a8de2709e691699d" +EXPECTED_STAGE2_RELEASE_SHA256 = "70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92" INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json" INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean" INLINE_STAGE2_RELEASE_PATH = ( diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.py new file mode 100644 index 00000000..96a9c44e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.py @@ -0,0 +1,3917 @@ +#!/usr/bin/env python3 +"""Release-bound deterministic S2_20 ReliefPlan executor. + +The code is self-contained. It does not call an LLM, import workspace +Python, execute shell commands, install packages at runtime, discover +files by directory scan, or mutate S2_10 legal judgments. +""" + +from __future__ import annotations + +import base64 +import binascii +from concurrent.futures import ThreadPoolExecutor +import contextlib +from datetime import date +from decimal import Decimal, InvalidOperation, ROUND_DOWN, ROUND_HALF_UP, ROUND_UP +import hashlib +import io +import itertools +import json +import re +import sys +import unicodedata +from pathlib import PurePosixPath +from typing import Any, Iterable, Mapping, Sequence + + +WORKFLOW_ID = "S2_20" +ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0" +EXPECTED_STAGE2_RELEASE_SHA256 = "70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92" +LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp" +WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp" +MCP_PROTOCOL_VERSION = "2025-03-26" +INLINE_USER_HASH = "{{__user_hash__}}" +INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}" +REQUEST_PATH = "stage2_control/s2_20_request.json" +ASSET_ROOT = "Default_Agent/Stage_2_Clean" +MAX_FILE_BYTES = 32 * 1024 * 1024 +MAX_TOTAL_BYTES = 256 * 1024 * 1024 +HEX64 = re.compile(r"^[a-f0-9]{64}$") +CLUSTER_ID = re.compile(r"^CL-[a-f0-9]{24}$") +IDENT = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$") +RELIEF_RULE_PATH = re.compile(r"^rules/relief/CT-[0-9]{3}\.md$") +LEGAL_REVIEW_PATH = re.compile(r"^legal_review/[A-Za-z0-9][A-Za-z0-9._-]{0,191}\.json$") +DISPOSITIONS = ("selected", "alternative", "excluded", "deferred") +TARGET_SIGNATURE_FIELDS = ( + "claim_family", "legal_effect", "performance_kind", "object_kind", + "party_role_pattern", "source_obligation_kind", "remedy_mode", + "registry_action", "procedural_posture", "special_statute_tags", +) + + +class S220Error(Exception): + def __init__(self, code: str, message: str, *, details: Any | None = None) -> None: + super().__init__(message) + self.code = code + self.message = message + self.details = details + + def as_dict(self) -> dict[str, Any]: + row: dict[str, Any] = {"code": self.code, "message": self.message} + if self.details is not None: + row["details"] = self.details + return row + + +def nfc(value: str) -> str: + return unicodedata.normalize("NFC", value) + + +def no_duplicates(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise S220Error("DUPLICATE_JSON_KEY", f"duplicate JSON key: {key}") + result[key] = value + return result + + +def load_json(raw: bytes, *, label: str) -> Any: + if len(raw) > MAX_FILE_BYTES: + raise S220Error("SOURCE_SIZE_LIMIT", f"file exceeds limit: {label}") + try: + text = raw.decode("utf-8", errors="strict") + value = json.loads( + text, + object_pairs_hook=no_duplicates, + parse_constant=lambda token: (_ for _ in ()).throw( + S220Error("NON_FINITE_NUMBER", f"non-finite number: {token}") + ), + ) + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise S220Error("JSON_PARSE_ERROR", f"strict JSON parse failed: {label}") from exc + return value + + +def canonical_bytes(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) + + "\n" + ).encode("utf-8") + + +def digest(value: Any) -> str: + payload = value if isinstance(value, bytes) else canonical_bytes(value) + return hashlib.sha256(payload).hexdigest() + + +def safe_path(value: Any, *, code: str = "PATH_INVALID") -> str: + if not isinstance(value, str) or not value or "\x00" in value: + raise S220Error(code, "path must be a non-empty string") + if value.startswith("/") or "\\" in value: + raise S220Error(code, "absolute or backslash path is forbidden") + normalized = PurePosixPath(value) + if any(part in {"", ".", ".."} for part in normalized.parts): + raise S220Error(code, "path traversal or ambiguous component") + return normalized.as_posix() + + +def join_path(*parts: str) -> str: + return safe_path("/".join(part.strip("/") for part in parts if part)) + + +def require_hex(value: Any, *, code: str) -> str: + if not isinstance(value, str) or HEX64.fullmatch(value) is None: + raise S220Error(code, "expected lower-case SHA-256") + return value + + +def stable_id(prefix: str, *parts: Any) -> str: + body = digest([nfc(str(part)) for part in parts])[:24] + return f"{prefix}-{body}" + + +def _parse_mcp_payload(raw: bytes, expected_id: int) -> Mapping[str, Any]: + content_type_json = raw.lstrip().startswith(b"{") + candidates: list[Any] = [] + if content_type_json: + candidates = [load_json(raw, label="mcp-response")] + else: + data_lines: list[str] = [] + events: list[bytes] = [] + for line in raw.decode("utf-8", errors="strict").splitlines(): + if not line: + if data_lines: + events.append("\n".join(data_lines).encode("utf-8")) + data_lines = [] + continue + if line.startswith((":", "event:", "id:", "retry:")): + continue + if not line.startswith("data:"): + raise S220Error("MCP_SSE_SHAPE", "unexpected SSE line") + data_lines.append(line[5:].lstrip()) + if data_lines: + events.append("\n".join(data_lines).encode("utf-8")) + candidates = [load_json(event, label="mcp-sse-event") for event in events] + matches = [row for row in candidates if isinstance(row, dict) and row.get("id") == expected_id] + if len(matches) != 1: + raise S220Error("MCP_RESPONSE_ID", "exactly one matching JSON-RPC result required") + row = matches[0] + if row.get("jsonrpc") != "2.0" or row.get("error") is not None: + raise S220Error("MCP_JSONRPC_ERROR", "MCP returned an invalid/error response") + if not isinstance(row.get("result"), dict): + raise S220Error("MCP_RESULT_SHAPE", "MCP result must be an object") + return row + + +def _tool_text(result: Mapping[str, Any], tool: str) -> str: + if result.get("isError") is True: + rendered = str(result.get("content", "")) + code = "LOCALDOCS_NOT_FOUND" if "not found" in rendered.lower() else "MCP_TOOL_ERROR" + raise S220Error(code, f"{tool} returned isError=true") + content = result.get("content") + if not isinstance(content, list) or len(content) != 1: + raise S220Error("MCP_CONTENT_CARDINALITY", "one content block required") + block = content[0] + if not isinstance(block, dict) or block.get("type") != "text" or not isinstance(block.get("text"), str): + raise S220Error("MCP_CONTENT_SHAPE", "one text block required") + return block["text"] + + +def _binary_from_text(text: str, path: str) -> bytes: + value = load_json(text.encode("utf-8"), label=path) + if isinstance(value, dict) and isinstance(value.get("results"), list): + rows = value["results"] + if len(rows) != 1 or not isinstance(rows[0], dict): + raise S220Error("LOCALDOCS_RESULT_CARDINALITY", "one binary result required") + value = rows[0].get("content", rows[0].get("text")) + if isinstance(value, str): + value = load_json(value.encode("utf-8"), label=path) + if not isinstance(value, dict) or not isinstance(value.get("content_base64"), str): + raise S220Error("LOCALDOCS_BINARY_ENVELOPE", "content_base64 is required") + try: + raw = base64.b64decode(value["content_base64"].encode("ascii"), validate=True) + except (UnicodeEncodeError, binascii.Error, ValueError) as exc: + raise S220Error("LOCALDOCS_BASE64", "strict base64 required") from exc + if value.get("byte_length", value.get("size", len(raw))) != len(raw): + raise S220Error("LOCALDOCS_LENGTH", f"byte length mismatch: {path}") + if value.get("sha256", digest(raw)) != digest(raw): + raise S220Error("LOCALDOCS_HASH", f"raw hash mismatch: {path}") + return raw + + +class McpClient: + def __init__(self, endpoint: str, name: str, *, client: Any | None = None) -> None: + try: + import httpx # type: ignore + except ImportError as exc: + raise S220Error("HTTPX_UNAVAILABLE", "httpx==0.28.1 is required") from exc + self.endpoint = endpoint + self.name = name + self.client = client or httpx.Client(timeout=60) + self.headers = {"Content-Type": "application/json", "Accept": "application/json, text/event-stream"} + self.ids = itertools.count(10) + self.initialized = False + + def close(self) -> None: + close = getattr(self.client, "close", None) + if callable(close): + close() + + def _post(self, body: Mapping[str, Any], expected_id: int | None) -> Mapping[str, Any] | None: + try: + response = self.client.post(self.endpoint, json=dict(body), headers=dict(self.headers)) + response.raise_for_status() + except Exception as exc: + raise S220Error("MCP_TRANSPORT", f"{self.name} transport failed") from exc + session = response.headers.get("mcp-session-id") + if session: + self.headers["mcp-session-id"] = session + if expected_id is None: + return None + return _parse_mcp_payload(bytes(response.content), expected_id) + + def initialize(self) -> None: + row = self._post( + { + "jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": { + "protocolVersion": MCP_PROTOCOL_VERSION, + "capabilities": {}, + "clientInfo": { + "name": "liti-s2-20-inline", "version": "1.0.0", + "user_id": INLINE_USER_HASH, "workspace_id": INLINE_WORKSPACE_HASH, + }, + }, + }, + 1, + ) + if row is None or row["result"].get("protocolVersion") != MCP_PROTOCOL_VERSION: + raise S220Error("MCP_PROTOCOL", "MCP protocol negotiation failed") + self._post({"jsonrpc": "2.0", "method": "notifications/initialized"}, None) + self.initialized = True + + def call(self, tool: str, arguments: Mapping[str, Any]) -> Mapping[str, Any]: + if not self.initialized: + raise S220Error("MCP_NOT_INITIALIZED", "initialize before tools/call") + message_id = next(self.ids) + row = self._post( + {"jsonrpc": "2.0", "id": message_id, "method": "tools/call", "params": {"name": tool, "arguments": dict(arguments)}}, + message_id, + ) + if row is None: + raise S220Error("MCP_EMPTY", f"empty response: {tool}") + return row["result"] + + def read_binary(self, path: str) -> bytes: + safe = safe_path(path) + return _binary_from_text(_tool_text(self.call("read_binary_doc", {"doc_name": safe}), "read_binary_doc"), safe) + + def read_optional(self, path: str) -> bytes | None: + try: + return self.read_binary(path) + except S220Error as exc: + if exc.code == "LOCALDOCS_NOT_FOUND": + return None + raise + + def write_immutable(self, path: str, payload: bytes) -> str: + safe = safe_path(path) + existing = self.read_optional(safe) + if existing is not None: + if existing != payload: + raise S220Error("IMMUTABLE_CONFLICT", f"existing bytes differ: {safe}") + return digest(existing) + encoded = base64.b64encode(payload).decode("ascii") + _tool_text( + self.call("write_binary_file", {"path": safe, "content_base64": encoded, "overwrite": False}), + "write_binary_file", + ) + observed = self.read_binary(safe) + if observed != payload: + raise S220Error("WRITE_READBACK_MISMATCH", f"read-back differs: {safe}") + return digest(observed) + + +REQUEST_KEYS = { + "schema_version", "request_id", "attempt_id", "run_binding_digest", + "user_identity_hash", "workspace_identity_hash", "stage2_run_root_ref", + "plan_output_root_ref", "host_cas_receipt_ref", "host_cas_receipt_sha256", + "detached_admission_receipt_ref", "detached_admission_receipt_sha256", + "outer_execution_receipt_target_ref", "expected_s2_10_publish_status_sha256", + "expected_parent_stage2_release_sha256", "expected_s2_10_release_sha256", + "expected_s2_20_agent_sha256", "expected_s2_20_executor_binding_sha256", +} + + +def validate_request(raw: bytes) -> dict[str, Any]: + value = load_json(raw, label=REQUEST_PATH) + if not isinstance(value, dict) or set(value) != REQUEST_KEYS: + keys = set(value) if isinstance(value, dict) else set() + raise S220Error("REQUEST_CLOSED_SHAPE", "request keys differ", details={"missing": sorted(REQUEST_KEYS - keys), "extra": sorted(keys - REQUEST_KEYS)}) + if value["schema_version"] != "stage2_s2_20_request.v1": + raise S220Error("REQUEST_VERSION", "unsupported request schema") + for key in ("request_id", "attempt_id"): + if not isinstance(value[key], str) or IDENT.fullmatch(value[key]) is None: + raise S220Error("REQUEST_ID_INVALID", f"invalid {key}") + for key in ( + "run_binding_digest", "user_identity_hash", "workspace_identity_hash", + "host_cas_receipt_sha256", "expected_s2_10_publish_status_sha256", + "expected_parent_stage2_release_sha256", "expected_s2_10_release_sha256", + "expected_s2_20_agent_sha256", "expected_s2_20_executor_binding_sha256", + ): + require_hex(value[key], code=f"{key.upper()}_INVALID") + require_hex(value["detached_admission_receipt_sha256"], code="ADMISSION_HASH_INVALID") + run = value["run_binding_digest"] + expected_root = f"stage2_runs/by-binding/{run}" + if value["stage2_run_root_ref"] != expected_root or value["plan_output_root_ref"] != f"{expected_root}/plan": + raise S220Error("REQUEST_ROOT_BINDING", "request/output roots must derive from run binding") + if value["user_identity_hash"] != INLINE_USER_HASH or value["workspace_identity_hash"] != INLINE_WORKSPACE_HASH: + raise S220Error("WORKSPACE_IDENTITY_MISMATCH", "request identity differs from Agent substitution") + exact_paths = { + "host_cas_receipt_ref": f"stage2_control/host_cas/{run}/S2_20.json", + "detached_admission_receipt_ref": f"{ASSET_ROOT}/manifest/stage2_deterministic_admission_receipt.json", + "outer_execution_receipt_target_ref": f"{expected_root}/control/s2_20_outer_execution_receipt.json", + } + for key, expected in exact_paths.items(): + safe_path(value[key]) + if value[key] != expected: + raise S220Error("REQUEST_TRUST_ANCHOR_PATH", f"{key} must equal its canonical run-bound path") + return value + + +def read_stable(localdocs: McpClient, paths: Iterable[str]) -> tuple[dict[str, bytes], str]: + ordered = sorted(set(safe_path(path) for path in paths)) + first: dict[str, bytes] = {} + total = 0 + for path in ordered: + raw = localdocs.read_binary(path) + total += len(raw) + if total > MAX_TOTAL_BYTES: + raise S220Error("TOTAL_READ_LIMIT", "input hydration exceeds total limit") + first[path] = raw + second = {path: localdocs.read_binary(path) for path in ordered} + if any(first[path] != second[path] for path in ordered): + raise S220Error("INPUT_TOCTOU", "input changed between bounded reads") + rows = [[path, digest(first[path]), len(first[path])] for path in ordered] + return first, digest(rows) + + +def option_disposition( + option: Mapping[str, Any], policy: Mapping[str, Any] +) -> tuple[str, list[str]]: + decision = option.get("decision") + client = option.get("client_disposition") + relation = option.get("relation") + if policy.get("policy_status") == "APPROVED": + observed = { + "legal_decision": decision, + "client_instruction": "NO_SUE" if client == "DEFERRED_BY_CLIENT_INSTRUCTION" else "UNSPECIFIED", + "precondition_status": "UNRESOLVED" if option.get("precondition_refs") or relation == "precondition_candidate" else "NOT_APPLICABLE", + "compatibility_status": "INCOMPATIBLE" if option.get("incompatible_with") else "COMPATIBLE", + } + matches = [ + row for row in policy.get("rules", []) + if isinstance(row, dict) + and all(row.get(key) == value for key, value in observed.items()) + ] + if len(matches) != 1: + raise S220Error( + "DISPOSITION_POLICY_CONFLICT" if matches else "DISPOSITION_POLICY_GAP", + "approved option-disposition policy must match exactly one row", + ) + result = matches[0].get("result_disposition") + if result not in {"SELECTED", "ALTERNATIVE", "EXCLUDED", "DEFERRED"}: + raise S220Error("DISPOSITION_POLICY_GAP", "policy row cannot authorize a disposition") + return str(result).lower(), [str(matches[0].get("reason_code"))] + reason_codes = ["DISPOSITION_POLICY_NOT_ADMITTED"] + if decision == "EXCLUDED": + return "excluded", reason_codes + if client == "DEFERRED_BY_CLIENT_INSTRUCTION": + return "deferred", reason_codes + if relation in {"alternative", "precondition_candidate", "accessory"}: + return "alternative", reason_codes + if decision in {"SUPPORTED", "CONDITIONAL", "UNRESOLVED"}: + return "selected", reason_codes + raise S220Error("DISPOSITION_POLICY_GAP", "claim option has no closed disposition") + + +def project_signature( + source: Mapping[str, Any], + c20_projection: Mapping[str, Any], + c21_projection: Mapping[str, Any], + remedy_context: Mapping[str, Any], + projection_registry: Mapping[str, Any], +) -> dict[str, Any]: + """Closed S2_10+C20+C21+remedy projection used by C25. + + No label, free text, filename, retrieval result, or model output may + supplement these explicit typed inputs. A missing/ambiguous value + remains UNKNOWN and therefore cannot produce an exact case binding. + """ + expected_source_fields = { + "vocabulary_version", "legal_basis_family", "right_holder_refs", + "obligor_refs", "performance_kind", "object_refs", "legal_effect", + "source_occurrence_refs", + } + if set(source) != expected_source_fields: + raise S220Error( + "BINDING_SIGNATURE_SOURCE_SHAPE", + "normalized claim signature must use the closed S2_10 vocabulary", + details={ + "missing": sorted(expected_source_fields - set(source)), + "extra": sorted(set(source) - expected_source_fields), + }, + ) + if source.get("vocabulary_version") != "stage2.claim_signature_vocab.v1": + raise S220Error("BINDING_SIGNATURE_SOURCE_VERSION", "unsupported source vocabulary") + source_values: dict[str, Any] = dict(source) + source_values.update( + { + "c20_remedy_projection": remedy_context.get("remedy_mode"), + "c20_dependency_projection": c20_projection.get("dependency_kind"), + "c21_party_projection": c21_projection.get("party_role_pattern"), + "c21_object_projection": c21_projection.get("object_kind"), + } + ) + projected: dict[str, Any] = { + key: ([] if key == "special_statute_tags" else "UNKNOWN") + for key in TARGET_SIGNATURE_FIELDS + } + rules = projection_registry.get("projection_rules", []) + rule_ids: list[str] = [] + provenance: list[dict[str, Any]] = [] + if projection_registry.get("registry_status") == "APPROVED" and isinstance(rules, list): + by_target: dict[str, Mapping[str, Any]] = {} + for rule in rules: + if not isinstance(rule, dict) or rule.get("target_field") not in TARGET_SIGNATURE_FIELDS: + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_INVALID", "closed projection row required") + target_field = str(rule["target_field"]) + if target_field in by_target: + raise S220Error("BINDING_SIGNATURE_PROJECTION_MULTI_RULE", target_field) + by_target[target_field] = rule + if set(by_target) != set(TARGET_SIGNATURE_FIELDS): + raise S220Error("BINDING_SIGNATURE_PROJECTION_COVERAGE", "all target fields require one rule") + for target_field in TARGET_SIGNATURE_FIELDS: + rule = by_target[target_field] + rule_id = str(rule.get("projection_rule_id", "")) + source_fields = rule.get("source_fields", []) + allowed = rule.get("allowed_output_values", []) + operator = rule.get("derivation_operator") + if not rule_id or not isinstance(source_fields, list) or not isinstance(allowed, list): + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_INVALID", target_field) + if rule.get("implementation_status") != "IMPLEMENTED" or rule.get("semantic_review_status") != "APPROVED": + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_NOT_ADMITTED", target_field) + candidates: list[str] = [] + for field in source_fields: + value = source_values.get(str(field)) + values = value if isinstance(value, list) else [value] + candidates.extend(nfc(item) for item in values if isinstance(item, str) and item) + permitted = sorted({item for item in candidates if item in allowed}) + if operator in {"DIRECT_COPY", "CLOSED_LOOKUP"} and len(permitted) == 1: + projected[target_field] = permitted[0] + elif operator == "SET_UNION" and target_field == "special_statute_tags": + projected[target_field] = permitted + elif operator == "SET_INTERSECTION" and target_field == "special_statute_tags": + projected[target_field] = permitted if permitted and len(permitted) == len(set(candidates)) else [] + rule_ids.append(rule_id) + provenance.append( + { + "target_field": target_field, + "value": projected[target_field], + "source_paths": sorted(str(item) for item in source_fields), + "status": "DERIVED" if projected[target_field] not in ("UNKNOWN", []) else "UNKNOWN", + } + ) + if not rule_ids: + rule_ids = ["PENDING_BINDING_SIGNATURE_PROJECTION"] + provenance = [ + { + "target_field": target_field, + "value": projected[target_field], + "source_paths": [], + "status": "UNKNOWN", + } + for target_field in TARGET_SIGNATURE_FIELDS + ] + return { + "schema_version": "stage2.binding_target_signature.v1", + "source_vocabulary_version": source["vocabulary_version"], + "target_vocabulary_version": projection_registry.get("target_vocabulary_version", "stage2.case_type_signature_vocab.v1"), + "projection_rule_ids": sorted(rule_ids), + "source_signature_refs": sorted(str(item) for item in source.get("source_occurrence_refs", []) if isinstance(item, str)), + "target_signature": projected, + "target_signature_sha256": digest(projected), + "signature": projected, + "signature_sha256": digest(projected), + "provenance": provenance, + } + + +def eval_predicate(signature: Mapping[str, Any], predicate: Mapping[str, Any]) -> bool: + operator = predicate.get("operator") + field = predicate.get("field") + if operator not in {"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"} or field not in TARGET_SIGNATURE_FIELDS: + raise S220Error("PREDICATE_GRAMMAR", "unsupported predicate operator/field") + actual = signature.get(str(field), "UNKNOWN") + expected = predicate.get("value") + if operator == "EXISTS": + if not isinstance(expected, bool): + raise S220Error("PREDICATE_GRAMMAR", "EXISTS requires a boolean value") + return (actual not in (None, "UNKNOWN", [])) is expected + if operator == "EQ": + return actual == expected + if operator == "IN": + return actual in (expected if isinstance(expected, list) else []) + actual_set = set(actual if isinstance(actual, list) else [actual]) + expected_set = set(expected if isinstance(expected, list) else []) + return expected_set <= actual_set if operator == "ALL_OF" else actual_set.isdisjoint(expected_set) + + +def eval_closed_predicate( + signature: Mapping[str, Any], predicate: Mapping[str, Any] +) -> bool: + if predicate.get("legal_review_status") != "APPROVED": + return False + clauses = predicate.get("clauses") + if not isinstance(clauses, list) or not clauses or any(not isinstance(item, dict) for item in clauses): + raise S220Error("PREDICATE_GRAMMAR", "approved predicate requires non-empty clauses") + return all(eval_predicate(signature, item) for item in clauses) + + +def bind_case( + signature: Mapping[str, Any], registry: Mapping[str, Any] +) -> tuple[str | None, str, list[str], list[str]]: + rows = registry.get("rows", registry.get("case_types", [])) + matches: list[tuple[str, str]] = [] + for row in rows if isinstance(rows, list) else []: + if not isinstance(row, dict): + continue + if row.get("row_kind") not in {"CLAIM", "GENERIC"}: + continue + if row.get("claim_capable_disposition") != "CLAIM_CAPABLE" or row.get("legal_classification_status") != "APPROVED": + continue + predicates = row.get("case_type_predicates", []) + if ( + isinstance(predicates, list) + and len(predicates) == 1 + and isinstance(predicates[0], dict) + and eval_closed_predicate(signature, predicates[0]) + ): + matches.append((str(row.get("case_type_id")), str(predicates[0].get("predicate_id")))) + matches = sorted(set(matches)) + if len(matches) == 1: + return matches[0][0], "BOUND", [], [matches[0][1]] + if not matches: + return None, "UNRESOLVED", ["CASE_TYPE_BINDING_ZERO_MATCH"], [] + return None, "CONFLICT", ["CASE_TYPE_BINDING_MULTI_MATCH"], [] + + +def bind_rule( + case_type_id: str | None, + signature: Mapping[str, Any], + rule_registry: Mapping[str, Any], +) -> tuple[dict[str, Any] | None, list[str], list[str]]: + if case_type_id is None: + return None, ["SUB_RULE_BINDING_ZERO_MATCH"], [] + registry_rows = rule_registry.get("rows", rule_registry.get("case_type_rule_rows", rule_registry.get("rule_bindings", []))) + rows = [row for row in registry_rows if isinstance(row, dict) and row.get("case_type_id") == case_type_id] + branches: list[dict[str, Any]] = [] + for row in rows: + row_branches = row.get("rule_branches", row.get("sub_rule_bindings", [])) + for branch in row_branches if isinstance(row_branches, list) else []: + if not isinstance(branch, dict) or branch.get("legal_content_status") != "APPROVED": + continue + predicates = branch.get("sub_rule_predicates", []) + if ( + isinstance(predicates, list) + and len(predicates) == 1 + and isinstance(predicates[0], dict) + and eval_closed_predicate(signature, predicates[0]) + ): + branches.append(branch) + if len(branches) == 1: + branch = branches[0] + predicate_ids = [str(item.get("predicate_id")) for item in branch.get("sub_rule_predicates", []) if isinstance(item, dict) and item.get("predicate_id")] + return branch, [], sorted(predicate_ids or [str(branch.get("predicate_id", "UNKNOWN"))]) + if len(branches) > 1: + return None, ["SUB_RULE_BINDING_MULTI_MATCH"], [] + return None, ["RULE_BRANCH_MISSING"], [] + + +def _source_ref(path: str, sha256: str, pointer: str = "") -> dict[str, str]: + return {"path": path, "sha256": sha256, "json_pointer": pointer} + + +def validate_rule_foreign_keys( + case_type_id: str, + branch: Mapping[str, Any], + assets: Mapping[str, Any], +) -> tuple[list[str], dict[str, list[dict[str, str]]]]: + """C26 exact FK/hash/status closure for an otherwise matched branch.""" + codes: list[str] = [] + refs: dict[str, list[dict[str, str]]] = { + "structured_relief_rule_refs": [], + "markdown_source_refs": [], + "renderer_refs": [], + "party_set_rule_refs": [], + "ce13_branch_refs": [], + "review_policy_refs": [], + } + legal_bytes = assets.get("release_selected_legal_bytes", {}) + markdown_path = branch.get("markdown_source_path") + markdown_hash = branch.get("markdown_source_sha256") + if ( + not isinstance(markdown_path, str) + or not isinstance(markdown_hash, str) + or not isinstance(legal_bytes, dict) + or markdown_path not in legal_bytes + or digest(legal_bytes[markdown_path]) != markdown_hash + ): + codes.append("RELIEF_RULE_SOURCE_HASH_MISMATCH") + else: + refs["markdown_source_refs"].append(_source_ref(markdown_path, markdown_hash)) + receipt_path = branch.get("legal_review_receipt_ref") + receipt_hash = branch.get("legal_review_receipt_sha256") + if ( + not isinstance(receipt_path, str) + or not isinstance(receipt_hash, str) + or receipt_path not in legal_bytes + or digest(legal_bytes[receipt_path]) != receipt_hash + ): + codes.append("RULE_BRANCH_LEGAL_RECEIPT_HASH_MISMATCH") + + relief_rules = assets.get("relief_rules", {}) + relief_rows = relief_rules.get("rule_branches", []) if isinstance(relief_rules, dict) else [] + relief_matches = [ + row for row in relief_rows + if isinstance(row, dict) + and row.get("case_type_id") == case_type_id + and row.get("sub_rule_id") == branch.get("sub_rule_id") + and row.get("rule_branch_key", f"{case_type_id}::{row.get('sub_rule_id')}") == branch.get("rule_branch_key") + ] + if ( + not isinstance(relief_rules, dict) + or relief_rules.get("execution_eligible") is not True + or relief_rules.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(relief_matches) != 1 + ): + codes.append("STRUCTURED_RELIEF_RULE_FK_UNRESOLVED") + else: + refs["structured_relief_rule_refs"].append( + _source_ref( + "registry/drafting/case_type_relief_rules.yml", + str(assets["asset_hashes"]["relief_rules"]), + f"/rule_branches/{relief_rows.index(relief_matches[0])}", + ) + ) + + renderer_descriptors = assets.get("renderer_descriptors", {}) + renderer_hashes = assets.get("renderer_hashes", {}) + for renderer_id in branch.get("renderer_ids", []): + descriptor = renderer_descriptors.get(renderer_id) if isinstance(renderer_descriptors, dict) else None + renderer_hash = renderer_hashes.get(renderer_id) if isinstance(renderer_hashes, dict) else None + if ( + not isinstance(descriptor, dict) + or descriptor.get("renderer_id") != renderer_id + or descriptor.get("execution_eligible") is not True + or descriptor.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or not isinstance(renderer_hash, str) + ): + codes.append("RENDERER_FK_UNRESOLVED") + else: + renderer_paths = { + "R01": "renderers/R01_money_payment.yml", + "R02": "renderers/R02_delivery_possession.yml", + "R03": "renderers/R03_declaration_registry.yml", + "R04": "renderers/R04_special_nonmoney_performance.yml", + "R05": "renderers/R05_declaratory.yml", + "R06": "renderers/R06_constitutive_judgment_challenge.yml", + } + refs["renderer_refs"].append(_source_ref(renderer_paths[renderer_id], renderer_hash)) + + party_registry = assets.get("party_rules", {}) + party_rows = party_registry.get("rules", []) if isinstance(party_registry, dict) else [] + party_matches = [ + row for row in party_rows + if isinstance(row, dict) + and row.get("party_set_rule_id") == branch.get("party_set_rule_id") + and row.get("legal_content_status") == "APPROVED" + ] + if party_registry.get("registry_status") != "APPROVED" or len(party_matches) != 1: + codes.append("PARTY_SET_RULE_FK_UNRESOLVED") + else: + refs["party_set_rule_refs"].append( + _source_ref( + "registry/party/party_set_rules.yml", + str(assets["asset_hashes"]["party_rules"]), + f"/rules/{party_rows.index(party_matches[0])}", + ) + ) + + scope_registry = assets.get("corpus_scope", {}) + scope_rows = scope_registry.get("rows", []) if isinstance(scope_registry, dict) else [] + scope_matches = [ + row for row in scope_rows + if isinstance(row, dict) + and row.get("corpus_scope_id") == branch.get("corpus_scope_id") + and row.get("case_type_id") == case_type_id + and branch.get("sub_rule_id") in row.get("sub_rule_ids", []) + and row.get("element_set_id") == branch.get("element_set_id") + and row.get("approval_status") == "APPROVED" + ] + if scope_registry.get("registry_status") != "APPROVED" or len(scope_matches) != 1: + codes.append("CORPUS_SCOPE_FK_UNRESOLVED") + + ce13 = assets.get("calculator_descriptors", {}).get("CE-13") + ce13_rows = ce13.get("formula_rows", []) if isinstance(ce13, dict) else [] + ce13_matches = [ + row for row in ce13_rows + if isinstance(row, dict) + and row.get("formula_id") == branch.get("ce13_branch_id") + ] + if ( + not isinstance(ce13, dict) + or ce13.get("execution_eligible") is not True + or ce13.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(ce13_matches) != 1 + ): + codes.append("CE13_BRANCH_FK_UNRESOLVED") + else: + refs["ce13_branch_refs"].append( + _source_ref( + "registry/calculations/CE-13_court_value_cost.yml", + str(assets["calculator_descriptor_hashes"]["CE-13"]), + f"/formula_rows/{ce13_rows.index(ce13_matches[0])}", + ) + ) + + review_registry = assets.get("review_policy", {}) + policy_rows = review_registry.get("policy_rows", []) if isinstance(review_registry, dict) else [] + policy_matches = [ + row for row in policy_rows + if isinstance(row, dict) and row.get("review_policy_id", row.get("policy_id")) == branch.get("review_policy_id") + ] + if ( + not isinstance(review_registry, dict) + or review_registry.get("execution_eligible") is not True + or review_registry.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(policy_matches) != 1 + ): + codes.append("REVIEW_POLICY_FK_UNRESOLVED") + else: + refs["review_policy_refs"].append( + _source_ref( + "registry/review/review_policy_registry.yml", + str(assets["asset_hashes"]["review_policy"]), + f"/policy_rows/{policy_rows.index(policy_matches[0])}", + ) + ) + return sorted(set(codes)), { + key: sorted(value, key=lambda row: (row["path"], row["json_pointer"])) + for key, value in refs.items() + } + + +def build_portfolio( + verdicts: Sequence[Mapping[str, Any]], + verdict_sources: Mapping[str, Mapping[str, str]], + disposition_policy: Mapping[str, Any], +) -> tuple[list[dict[str, Any]], dict[str, list[str]], list[dict[str, Any]], list[dict[str, Any]]]: + atomic: list[dict[str, Any]] = [] + partitions = {name: [] for name in DISPOSITIONS} + dispositions: list[dict[str, Any]] = [] + option_to_group: dict[str, str] = {} + relation_rows: list[tuple[str, str, str]] = [] + for verdict in verdicts: + cluster_id = str(verdict.get("cluster_id", "UNKNOWN")) + source = verdict_sources.get(cluster_id, {}) + options = verdict.get("claim_options", []) + for option_index, option in enumerate(options if isinstance(options, list) else []): + if not isinstance(option, dict): + continue + option_id = str(option.get("claim_option_id", "")) + if not option_id: + raise S220Error("CLAIM_OPTION_ID_MISSING", "canonical option ID is required") + disposition, disposition_reason_codes = option_disposition(option, disposition_policy) + partitions[disposition].append(option_id) + active = disposition in {"selected", "alternative"} + atomic_id = stable_id("AC", option_id) if active else None + group_id = stable_id("CG", option.get("same_underlying_recovery_key") or option_id) if active else None + if active and atomic_id is not None and group_id is not None: + option_to_group[option_id] = group_id + signature = option.get("normalized_claim_signature", {}) + source_path = source.get("path", f"map_s2_10/domain_verdicts/{cluster_id}.json") + source_sha = source.get("sha256", digest(verdict)) + atomic.append( + { + "atomic_claim_id": atomic_id, + "claim_option_id": option_id, + "claim_group_id": group_id, + "cluster_id": cluster_id, + "disposition": disposition.upper(), + "legal_decision": option.get("decision"), + "relation": option.get("relation"), + "client_disposition": option.get("client_disposition"), + "client_instruction_refs": sorted(str(item) for item in option.get("client_instruction_refs", []) if isinstance(item, str)), + "right_holder_refs": sorted(str(item) for item in signature.get("right_holder_refs", []) if isinstance(item, str)), + "obligor_refs": sorted(str(item) for item in signature.get("obligor_refs", []) if isinstance(item, str)), + "object_refs": sorted(str(item) for item in signature.get("object_refs", []) if isinstance(item, str)), + "same_underlying_recovery_key": option.get("same_underlying_recovery_key"), + "incompatible_with": sorted(str(item) for item in option.get("incompatible_with", []) if isinstance(item, str)), + "precondition_refs": sorted(str(item) for item in option.get("precondition_refs", []) if isinstance(item, str)), + "element_statuses": option.get("element_statuses", []), + "defense_statuses": option.get("defense_statuses", []), + "remedy_candidates": option.get("remedy_candidates", []), + "normalized_claim_signature": signature, + "c20_projection": { + "dependency_kind": option.get("relation", "UNKNOWN"), + "incompatible_option_ids": sorted(str(item) for item in option.get("incompatible_with", []) if isinstance(item, str)), + "precondition_option_ids": sorted(str(item) for item in option.get("precondition_refs", []) if isinstance(item, str)), + }, + "normalized_signature_sha256": digest(signature), + "source_refs": [{ + "source_artifact_path": source_path, + "source_artifact_sha256": source_sha, + "json_pointer": f"/claim_options/{option_index}", + }], + "typed_provenance": sorted( + [item for item in option.get("typed_provenance", []) if isinstance(item, dict)], + key=digest, + ), + "source_option_sha256": digest(option), + } + ) + relation_rows.extend((option_id, target, "INCOMPATIBLE") for target in option.get("incompatible_with", []) if isinstance(target, str)) + relation_rows.extend((option_id, target, "PRECONDITION") for target in option.get("precondition_refs", []) if isinstance(target, str)) + dispositions.append( + { + "schema_version": "stage2_option_disposition.v1", + "claim_option_id": option_id, + "source_cluster_id": cluster_id, + "source_domain_verdict_sha256": source.get("sha256", digest(verdict)), + "legal_decision": option.get("decision"), + "client_instruction": "NO_SUE" if option.get("client_disposition") == "DEFERRED_BY_CLIENT_INSTRUCTION" else "UNSPECIFIED", + "disposition": disposition.upper(), + "reason_codes": sorted(set(disposition_reason_codes + [f"S2_10_{str(option.get('decision', 'UNKNOWN'))}", f"RELATION_{str(option.get('relation', 'UNKNOWN')).upper()}"])), + "atomic_claim_ids": [atomic_id] if atomic_id is not None else [], + "claim_group_id": group_id, + "issue_refs": [], + } + ) + for values in partitions.values(): + values.sort() + seen = [item for values in partitions.values() for item in values] + if len(seen) != len(set(seen)) or set(seen) != {row["claim_option_id"] for row in dispositions}: + raise S220Error("OPTION_CONSERVATION", "disposition partition is not exact") + dependencies: list[dict[str, Any]] = [] + for source_option, target_option, relation in sorted(set(relation_rows)): + source_group = option_to_group.get(source_option) + target_group = option_to_group.get(target_option) + if source_group is None or target_group is None or source_group == target_group: + continue + dependencies.append( + { + "source_group_id": source_group, + "target_group_id": target_group, + "relation": relation, + "source_option_relation_refs": [f"{source_option}->{target_option}"], + } + ) + return ( + sorted(atomic, key=lambda row: row["atomic_claim_id"]), + partitions, + sorted(dispositions, key=lambda row: row["claim_option_id"]), + sorted(dependencies, key=digest), + ) + + +def build_party_projection( + party_context: Mapping[str, Any], object_registry: Mapping[str, Any] +) -> dict[str, Any]: + """C21 facts-only projection; no required-party legal conclusion.""" + parties = party_context.get("parties", []) + party_refs = sorted( + str(row.get("party_identity", {}).get("party_context_id")) + for row in parties + if isinstance(row, dict) + and isinstance(row.get("party_identity"), dict) + and row["party_identity"].get("party_context_id") + ) + identity_issues = sorted( + str(row.get("party_identity", {}).get("party_context_id")) + for row in parties + if isinstance(row, dict) + and isinstance(row.get("party_identity"), dict) + and row["party_identity"].get("identity_disposition") == "IDENTITY_UNRESOLVED" + ) + objects = object_registry.get("objects", []) + object_refs = sorted( + str(row.get("object_context_id")) + for row in objects + if isinstance(row, dict) and row.get("object_context_id") + ) + object_identity_issues = sorted( + str(row.get("object_context_id")) + for row in objects + if isinstance(row, dict) + and row.get("object_context_id") + and row.get("identity_disposition") == "IDENTITY_UNRESOLVED" + ) + present_role_tokens = sorted( + { + nfc(str(row[key])) + for row in parties + if isinstance(row, dict) + for key in ("party_title", "defendant_role") + if isinstance(row.get(key), str) and row.get(key) + } + ) + return { + "schema_version": "stage2_party_projection.v1", + "party_refs": party_refs, + "object_refs": object_refs, + "title_chain_refs": [], + "identity_issue_refs": sorted(set(identity_issues + object_identity_issues)), + "present_role_tokens": present_role_tokens, + "legal_required_party_determination_made": False, + } + + +def claim_c21_projection( + claim: Mapping[str, Any], party_projection: Mapping[str, Any] +) -> dict[str, str]: + known_parties = set(str(item) for item in party_projection.get("party_refs", [])) + known_objects = set(str(item) for item in party_projection.get("object_refs", [])) + holders = set(str(item) for item in claim.get("right_holder_refs", [])) + obligors = set(str(item) for item in claim.get("obligor_refs", [])) + objects = set(str(item) for item in claim.get("object_refs", [])) + if holders and obligors and holders | obligors <= known_parties: + party_role_pattern = f"RH_{len(holders)}_OB_{len(obligors)}" + else: + party_role_pattern = "UNKNOWN" + if not objects: + object_kind = "NO_OBJECT_REF" + elif objects <= known_objects: + object_kind = "IDENTIFIED_OBJECT_SET" + else: + object_kind = "UNKNOWN" + return { + "party_role_pattern": party_role_pattern, + "object_kind": object_kind, + } + + +def calculate_all( + atomic: Sequence[Mapping[str, Any]], + bindings: Sequence[Mapping[str, Any]], + activation_registry: Mapping[str, Any], + descriptor_hashes: Mapping[str, str], + descriptor_objects: Mapping[str, Mapping[str, Any]], + law_value_tables: Sequence[Mapping[str, Any]], + request: Mapping[str, Any], +) -> list[dict[str, Any]]: + """C22 closed dispatcher and Decimal formula interpreter. + + The present distribution contains no admitted legal formula rows. + This implementation nevertheless closes the admitted branch: an exact + activation row and release-selected descriptor are required, every + absent operand/law value remains explicit, and no numeric fallback is + permitted. + """ + descriptors = activation_registry.get("calculator_descriptors", []) + calculator_ids = sorted( + str(row["calculator_id"]) + for row in descriptors + if isinstance(row, dict) and isinstance(row.get("calculator_id"), str) + ) + descriptor_refs = { + str(row["calculator_id"]): row + for row in descriptors + if isinstance(row, dict) and isinstance(row.get("calculator_id"), str) + } + activation_rows = activation_registry.get("activation_rows", []) + if not isinstance(activation_rows, list): + raise S220Error("CALCULATION_ACTIVATION_ROWS_INVALID", "activation_rows must be an array") + registry_ready = ( + activation_registry.get("execution_eligible") is True + and activation_registry.get("status") == "LEGAL_CONTENT_ADMITTED" + ) + binding_by_atomic = { + str(row["atomic_claim_id"]): row + for row in bindings + if isinstance(row, dict) and isinstance(row.get("atomic_claim_id"), str) + } + law_values: dict[str, Mapping[str, Any]] = {} + for table in law_value_tables: + if not isinstance(table, Mapping) or table.get("release_eligible") is not True: + continue + for token in table.get("tokens", []): + if not isinstance(token, Mapping) or token.get("review_status") != "VERIFIED": + continue + law_value_id = token.get("law_value_id") + if isinstance(law_value_id, str) and law_value_id: + if law_value_id in law_values: + raise S220Error("LAW_VALUE_DUPLICATE", f"duplicate law value: {law_value_id}") + law_values[law_value_id] = token + + rounding_modes = { + "ROUND_HALF_UP": ROUND_HALF_UP, + "ROUND_DOWN": ROUND_DOWN, + "ROUND_UP": ROUND_UP, + "TRUNCATE": ROUND_DOWN, + } + + def decimal_value(value: Any) -> Decimal: + try: + return Decimal(str(value)) + except (InvalidOperation, ValueError) as exc: + raise S220Error("INVALID_DECIMAL_OPERAND", "operand is not an exact decimal") from exc + + def claim_metrics(claim: Mapping[str, Any]) -> list[str]: + values: set[str] = set() + for remedy in claim.get("remedy_candidates", []): + if not isinstance(remedy, Mapping): + continue + if isinstance(remedy.get("remedy_kind"), str): + values.add(str(remedy["remedy_kind"])) + values.update( + str(item) + for item in remedy.get("condition_codes", []) + if isinstance(item, str) and item + ) + return sorted(values) + + def row_matches( + activation_row: Mapping[str, Any], + claim: Mapping[str, Any], + binding: Mapping[str, Any], + requested_metrics: Sequence[str], + ) -> bool: + row_metrics = activation_row.get("requested_metrics", []) + equals = activation_row.get("equals", {}) + if not isinstance(row_metrics, list) or not isinstance(equals, Mapping): + raise S220Error("INVALID_ACTIVATION_ROW", "activation row shape is invalid") + if not set(str(item) for item in row_metrics) & set(requested_metrics): + return False + actual = { + "normalized_claim_signature": str(claim.get("normalized_signature_sha256", "")), + "case_type_id": str(binding.get("case_type_id", "")), + "rule_branch_key": str(binding.get("rule_branch_key", "")), + } + for key, expected in equals.items(): + if key == "requested_metric": + if str(expected) not in requested_metrics: + return False + elif key not in actual or actual[key] != str(expected): + return False + return True + + def empty_receipt( + claim: Mapping[str, Any], calculator_id: str, status: str, issues: Sequence[str] + ) -> dict[str, Any]: + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": stable_id("CR", claim["atomic_claim_id"], calculator_id), + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": claim["atomic_claim_id"], + "claim_group_id": claim["claim_group_id"], + "calculator_id": calculator_id, + "activation_status": status, + "calculation_status": status, + "activation_row_id": None, + "descriptor_sha256": descriptor_hashes.get(calculator_id), + "formula_id": None, + "formula_sha256": None, + "operand_rows": [], + "operand_refs": [], + "units": [], + "law_value_uses": [], + "effective_interval": None, + "rounding_rule": None, + "period_rows": [], + "missing_operands": [], + "result": None, + "result_sha256": None, + "issue_codes": sorted(set(str(item) for item in issues)), + } + return {**core, "receipt_sha256": digest(core)} + + def operand_row(operand_id: str, unit_id: str) -> dict[str, Any]: + return { + "operand_id": operand_id, + "source_ref": f"UNRESOLVED_STAGE1_CALCULATION_REQUEST:{operand_id}", + "status": "MISSING", + "value_type": "DECIMAL", + "value": None, + "unit_id": unit_id, + "currency": "KRW" if unit_id == "KRW" else None, + "value_sha256": None, + } + + receipts: list[dict[str, Any]] = [] + for row in sorted(atomic, key=lambda item: str(item["atomic_claim_id"])): + binding = binding_by_atomic.get(str(row["atomic_claim_id"]), {}) + requested_metrics = claim_metrics(row) + for calculator_id in calculator_ids: + if not registry_ready: + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_ACTIVATION_REGISTRY_NOT_ADMITTED"])) + continue + candidate_rows = [ + item for item in activation_rows + if isinstance(item, Mapping) and item.get("calculator_id") == calculator_id + ] + matches = [ + item for item in candidate_rows + if row_matches(item, row, binding, requested_metrics) + ] + if len(matches) > 1: + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_CONFLICT", ["CALCULATION_ACTIVATION_CONFLICT"])) + continue + if not matches: + relevant_metrics = { + str(metric) + for item in candidate_rows + for metric in item.get("requested_metrics", []) + if isinstance(metric, str) + } + if relevant_metrics & set(requested_metrics): + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_ACTIVATION_GAP"])) + else: + receipts.append(empty_receipt(row, calculator_id, "NOT_APPLICABLE", [])) + continue + activation_row = matches[0] + activation_row_id = activation_row.get("activation_row_id") + descriptor_ref = descriptor_refs.get(calculator_id, {}) + descriptor = descriptor_objects.get(calculator_id, {}) + if ( + not isinstance(activation_row_id, str) + or descriptor_ref.get("execution_eligible") is not True + or descriptor_ref.get("status") != "LEGAL_CONTENT_ADMITTED" + or descriptor.get("execution_eligible") is not True + or descriptor.get("status") != "LEGAL_CONTENT_ADMITTED" + or descriptor_ref.get("descriptor_sha256") != descriptor_hashes.get(calculator_id) + or descriptor.get("descriptor_sha256") != descriptor_hashes.get(calculator_id) + ): + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_DESCRIPTOR_NOT_ADMITTED_OR_HASH_MISMATCH"])) + continue + formulas = [item for item in descriptor.get("formula_rows", []) if isinstance(item, Mapping)] + formula_matches = [item for item in formulas if item.get("output_metric_id") in requested_metrics] + if len(formula_matches) != 1: + status = "ACTIVATION_CONFLICT" if len(formula_matches) > 1 else "ACTIVATION_GAP" + receipts.append(empty_receipt(row, calculator_id, status, ["CALCULATION_FORMULA_SELECTION_NOT_EXACT"])) + continue + formula = formula_matches[0] + required_operands = sorted(str(item) for item in formula.get("input_operand_ids", []) if isinstance(item, str)) + output_unit = str(formula.get("output_unit_id", "")) + operand_rows = [operand_row(item, output_unit) for item in required_operands] + missing_operands = list(required_operands) + law_value_uses: list[dict[str, Any]] = [] + missing_law_values: list[str] = [] + for law_value_id in formula.get("law_value_ids", []): + source = law_values.get(str(law_value_id)) + if not isinstance(source, Mapping): + missing_law_values.append(str(law_value_id)) + continue + law_value_uses.append( + { + "law_value_id": str(law_value_id), + "authority_id": str(source["authority_id"]), + "authority_release_sha256": require_hex(source["authority_release_sha256"], code="AUTHORITY_RELEASE_SHA256_INVALID"), + "law_value_release_sha256": require_hex(source["law_value_release_sha256"], code="LAW_VALUE_RELEASE_SHA256_INVALID"), + "effective_interval": source["effective_interval"], + "value": format(decimal_value(source["value"]), "f"), + "unit_id": str(source.get("unit_id", source.get("unit"))), + "approval_status": "APPROVED", + } + ) + calculation_status = "MISSING_OPERAND" if missing_operands else ("LAW_VALUE_GAP" if missing_law_values else "CALCULATED") + result: dict[str, Any] | None = None + result_sha256: str | None = None + if calculation_status == "CALCULATED": + # The current S2_00 handoff deliberately preserves only + # calculation-request references, not numeric values. This + # branch becomes reachable only after exact PRESENT operand + # rows are supplied by a compatible handoff revision. + values = [decimal_value(item["value"]) for item in operand_rows] + operator = formula.get("operator") + if operator == "ADD": + computed = sum(values, Decimal("0")) + elif operator == "SUBTRACT": + computed = values[0] - sum(values[1:], Decimal("0")) + elif operator == "MULTIPLY": + computed = Decimal("1") + for value in values: + computed *= value + elif operator == "DIVIDE": + if len(values) != 2 or values[1] == 0: + raise S220Error("CALCULATION_INVALID_DIVISION", "division requires two operands and non-zero divisor") + computed = values[0] / values[1] + elif operator == "MIN": + computed = min(values) + elif operator == "MAX": + computed = max(values) + else: + raise S220Error("CALCULATION_OPERATOR_UNSUPPORTED", "formula operator is outside the closed set") + rounding = formula.get("rounding_rule", {}) + if not isinstance(rounding, Mapping) or rounding.get("mode") not in rounding_modes: + raise S220Error("CALCULATION_ROUNDING_INVALID", "rounding rule is invalid") + rounded = computed.quantize(decimal_value(rounding["quantum"]), rounding=rounding_modes[str(rounding["mode"])]) + result = { + "metric_id": str(formula["output_metric_id"]), + "value": format(rounded, "f"), + "unit_id": output_unit, + "currency": "KRW" if output_unit == "KRW" else None, + } + result_sha256 = digest(result) + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": stable_id("CR", row["atomic_claim_id"], calculator_id), + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": row["atomic_claim_id"], + "claim_group_id": row["claim_group_id"], + "calculator_id": calculator_id, + "activation_status": "ACTIVATED", + "calculation_status": calculation_status, + "activation_row_id": activation_row_id, + "descriptor_sha256": descriptor_hashes[calculator_id], + "formula_id": str(formula["formula_id"]), + "formula_sha256": require_hex(formula["formula_sha256"], code="FORMULA_SHA256_INVALID"), + "operand_rows": operand_rows, + "operand_refs": required_operands, + "units": sorted({item["unit_id"] for item in operand_rows if item["unit_id"]}), + "law_value_uses": sorted(law_value_uses, key=lambda item: item["law_value_id"]), + "effective_interval": formula["effective_interval"], + "rounding_rule": formula["rounding_rule"], + "period_rows": [], + "missing_operands": missing_operands, + "result": result, + "result_sha256": result_sha256, + "issue_codes": sorted( + (["CALCULATION_OPERAND_MISSING"] if missing_operands else []) + + [f"CALCULATION_LAW_VALUE_UNRESOLVED:{item}" for item in missing_law_values] + ), + } + receipts.append({**core, "receipt_sha256": digest(core)}) + return sorted(receipts, key=lambda row: row["calculation_receipt_id"]) + + +def call_with_transient_retry( + client: McpClient, + tool: str, + arguments: Mapping[str, Any], + *, + max_attempts: int = 3, +) -> tuple[Mapping[str, Any], int]: + if not isinstance(max_attempts, int) or isinstance(max_attempts, bool) or not 1 <= max_attempts <= 3: + raise S220Error("RETRIEVAL_RETRY_POLICY_INVALID", "max_attempts must be an integer in 1..3") + for attempt_count in range(1, max_attempts + 1): + try: + return client.call(tool, arguments), attempt_count + except S220Error as exc: + if exc.code != "MCP_TRANSPORT" or attempt_count == max_attempts: + raise + raise S220Error("RETRIEVAL_RETRY_EXHAUSTED", "unreachable retry state") + + +def canonicalize_retrieval_hits( + candidates: Sequence[Mapping[str, Any]], +) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + ordered = sorted( + (dict(row) for row in candidates), + key=lambda row: ( + int(row.get("post_rerank_rank", 2**31 - 1)), + -float(row.get("reranker_score") or row.get("hybrid_score") or 0), + str(row.get("chunk_id", "")), + ), + ) + unique: dict[str, dict[str, Any]] = {} + dropped: list[dict[str, Any]] = [] + for candidate in ordered: + chunk_id = str(candidate.get("chunk_id", "")) + chunk_sha256 = str(candidate.get("chunk_sha256", "")) + if not chunk_id or HEX64.fullmatch(chunk_sha256) is None: + raise S220Error("RETRIEVAL_HIT_IDENTITY_INVALID", "canonical hit requires chunk ID and hash") + if chunk_id in unique: + dropped.append( + { + "chunk_id": chunk_id, + "chunk_sha256": chunk_sha256, + "drop_reason_code": "DUPLICATE_CHUNK", + } + ) + else: + unique[chunk_id] = candidate + return list(unique.values()), dropped + + +def build_retrieval_branch( + atomic: Sequence[Mapping[str, Any]], + bindings: Sequence[Mapping[str, Any]], + corpus_release: Mapping[str, Any], + corpus_scope: Mapping[str, Any], + request: Mapping[str, Any], +) -> dict[str, Any]: + """C27->C28->C29 closed branch; pending corpus is explicit, never widened.""" + by_atomic = {str(row["atomic_claim_id"]): row for row in bindings} + queries: list[dict[str, Any]] = [] + results: list[dict[str, Any]] = [] + receipts: list[dict[str, Any]] = [] + pack_items: dict[str, list[dict[str, Any]]] = {} + unmapped_by_atomic: dict[str, list[str]] = {} + conflicting_by_atomic: dict[str, list[str]] = {} + corpus_admitted = ( + corpus_release.get("status") == "ADMITTED" + and corpus_release.get("available") is True + and corpus_release.get("sealed") is True + and corpus_scope.get("registry_status") == "APPROVED" + and isinstance(corpus_scope.get("rows"), list) + and bool(corpus_scope.get("rows")) + ) + snapshot_id = str(corpus_release.get("snapshot_id") or "UNAVAILABLE") + snapshot_sha256 = str(corpus_release.get("snapshot_sha256") or ("0" * 64)) + scope_rows = [row for row in corpus_scope.get("rows", []) if isinstance(row, dict)] + ready_query_rows: list[dict[str, Any]] = [] + for claim in atomic: + atomic_id = str(claim["atomic_claim_id"]) + binding = by_atomic[atomic_id] + pack_items[atomic_id] = [] + unmapped_by_atomic[atomic_id] = [] + conflicting_by_atomic[atomic_id] = [] + binding_ready = ( + binding.get("case_type_binding_status") == "BOUND" + and binding.get("sub_rule_binding_status") == "BOUND" + ) + matched_scopes = [ + row for row in scope_rows + if row.get("corpus_scope_id") == binding.get("corpus_scope_id") + and row.get("case_type_id") == binding.get("case_type_id") + and binding.get("sub_rule_id") in row.get("sub_rule_ids", []) + and row.get("element_set_id") == binding.get("element_set_id") + and row.get("approval_status") == "APPROVED" + ] + query_rows: list[dict[str, Any]] = [] + if corpus_admitted and binding_ready and len(matched_scopes) == 1: + scope = matched_scopes[0] + crosswalk = [row for row in scope.get("slot_crosswalk_rows", []) if isinstance(row, dict)] + for doctrine_role in sorted({str(row.get("doctrine_role")) for row in crosswalk if row.get("doctrine_role")}): + expected_ids = sorted( + str(row["corpus_element_id"]) + for row in crosswalk + if row.get("doctrine_role") == doctrine_role and row.get("corpus_element_id") + ) + search_filter = { + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "corpus_scope_id": binding["corpus_scope_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "jurisdiction": "KR", + "snapshot_id": snapshot_id, + "approval_status": "APPROVED", + "effective_on": str(corpus_release.get("effective_on") or corpus_release.get("as_of") or ""), + } + if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", search_filter["effective_on"]): + raise S220Error("CORPUS_EFFECTIVE_DATE_UNBOUND", "admitted corpus requires effective_on") + query_projection = { + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "corpus_element_ids": expected_ids, + } + query_text = json.dumps(query_projection, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + row = { + "query_id": stable_id("Q", atomic_id, doctrine_role), + "atomic_claim_id": atomic_id, + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "corpus_scope_id": binding["corpus_scope_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "query_status": "READY", + "query_text": query_text, + "query_sha256": digest(query_text.encode("utf-8")), + "filter": search_filter, + "alpha": corpus_release.get("alpha"), + "top_k": corpus_release.get("top_k"), + "embedding_configuration_digest": corpus_release.get("embedding_configuration_digest"), + "reranker_configuration_digest": corpus_release.get("reranker_configuration_digest"), + "index_configuration_digest": corpus_release.get("index_configuration_digest"), + "expected_corpus_element_ids": expected_ids, + "issue_codes": [], + } + for key in ("embedding_configuration_digest", "reranker_configuration_digest", "index_configuration_digest"): + require_hex(row[key], code=f"CORPUS_{key.upper()}_UNBOUND") + if not isinstance(row["alpha"], (int, float)) or isinstance(row["alpha"], bool) or not 0 <= row["alpha"] <= 1: + raise S220Error("CORPUS_ALPHA_UNBOUND", "admitted corpus requires alpha in [0,1]") + if not isinstance(row["top_k"], int) or isinstance(row["top_k"], bool) or not 1 <= row["top_k"] <= 100: + raise S220Error("CORPUS_TOP_K_UNBOUND", "admitted corpus requires top_k 1..100") + query_rows.append(row) + ready_query_rows.append(row) + query_base = { + "schema_version": "stage2_requirement_fact_query_plan.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "rows": sorted(query_rows, key=lambda row: row["query_id"]), + } + query = {**query_base, "query_plan_sha256": digest(query_base)} + queries.append(query) + if not query_rows: + issue_codes = ["QUERY_NOT_RUN_UNRESOLVED_BINDING"] + if binding_ready and not corpus_admitted: + issue_codes.append("CORPUS_NOT_ADMITTED") + elif binding_ready and len(matched_scopes) != 1: + issue_codes.append("CORPUS_SCOPE_FK_UNRESOLVED") + result_base = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": query["query_plan_sha256"], + "snapshot_id": snapshot_id, + "snapshot_sha256": snapshot_sha256, + "result_status": "NOT_RUN_UNRESOLVED_BINDING", + "selected_hits": [], + "dropped_hits": [], + "issue_codes": sorted(set(issue_codes)), + } + results.append({**result_base, "result_sha256": digest(result_base)}) + + if ready_query_rows: + collection = corpus_release.get("collection") + tenant = corpus_release.get("tenant") + credential_ref = corpus_release.get("credential_ref") + if not all(isinstance(item, str) and item for item in (collection, tenant, credential_ref)): + raise S220Error("CORPUS_TRANSPORT_BINDING_UNBOUND", "collection, tenant and credential_ref are required") + weaviate = McpClient(WEAVIATE_MCP_URL, "weaviate") + try: + weaviate.initialize() + by_plan = {row["atomic_claim_id"]: row for row in queries} + selected_by_atomic: dict[str, list[dict[str, Any]]] = {} + dropped_by_atomic: dict[str, list[dict[str, Any]]] = {} + requests_by_atomic: dict[str, list[dict[str, Any]]] = {} + responses_by_atomic: dict[str, list[Any]] = {} + filters_by_atomic: dict[str, list[dict[str, Any]]] = {} + attempts_by_atomic: dict[str, list[int]] = {} + for query_row in sorted(ready_query_rows, key=lambda row: row["query_id"]): + atomic_id = str(query_row["atomic_claim_id"]) + arguments = { + "collection_name": collection, + "tenant": tenant, + "query": query_row["query_text"], + "filters": query_row["filter"], + "alpha": query_row["alpha"], + "limit": query_row["top_k"], + } + tool_result, attempt_count = call_with_transient_retry( + weaviate, "search_hybrid", arguments, max_attempts=3 + ) + text = _tool_text(tool_result, "search_hybrid") + response = load_json(text.encode("utf-8"), label="weaviate-search-response") + if isinstance(response, dict): + raw_hits = response.get("hits", response.get("objects", response.get("results"))) + else: + raw_hits = response + if not isinstance(raw_hits, list): + raise S220Error("WEAVIATE_RESPONSE_SHAPE", "search response must expose one hit array") + accepted: list[dict[str, Any]] = [] + dropped: list[dict[str, Any]] = [] + for rank, raw_hit in enumerate(raw_hits, 1): + if not isinstance(raw_hit, dict) or not isinstance(raw_hit.get("metadata"), dict): + raise S220Error("WEAVIATE_HIT_SHAPE", "hit and metadata objects are required") + metadata = raw_hit["metadata"] + chunk_id = str(raw_hit.get("chunk_id", metadata.get("chunk_id", ""))) + chunk_sha = str(raw_hit.get("chunk_sha256", metadata.get("chunk_sha256", ""))) + reason: str | None = None + if metadata.get("approval_status") != "APPROVED": + reason = "UNAPPROVED_CHUNK" + elif metadata.get("chunk_sha256") != chunk_sha or HEX64.fullmatch(chunk_sha) is None: + reason = "HASH_MISMATCH" + elif not ( + query_row["filter"]["case_type_id"] in metadata.get("case_type_ids", []) + and query_row["filter"]["sub_rule_id"] in metadata.get("sub_rule_ids", []) + and query_row["filter"]["rule_branch_key"] in metadata.get("rule_branch_keys", []) + and query_row["filter"]["corpus_scope_id"] in metadata.get("corpus_scope_ids", []) + and query_row["filter"]["element_set_id"] in metadata.get("element_set_ids", []) + and query_row["doctrine_role"] in metadata.get("doctrine_roles", []) + and metadata.get("jurisdiction") == "KR" + ): + reason = "FILTER_MISMATCH" + if reason: + dropped.append({"chunk_id": chunk_id or "UNBOUND", "chunk_sha256": chunk_sha if HEX64.fullmatch(chunk_sha) else "0" * 64, "drop_reason_code": reason}) + continue + accepted.append( + { + "chunk_id": chunk_id, + "chunk_sha256": chunk_sha, + "source_document_id": str(raw_hit.get("source_document_id", metadata.get("document_id"))), + "source_document_sha256": str(raw_hit.get("source_document_sha256", metadata.get("document_sha256"))), + "source_locator": str(raw_hit.get("source_locator", metadata.get("source_locator"))), + "pre_rerank_rank": int(raw_hit.get("pre_rerank_rank", rank)), + "hybrid_score": raw_hit.get("hybrid_score", raw_hit.get("score", 0)), + "post_rerank_rank": int(raw_hit.get("post_rerank_rank", rank)), + "reranker_score": raw_hit.get("reranker_score"), + "metadata": metadata, + } + ) + selected_by_atomic.setdefault(atomic_id, []).extend(accepted) + dropped_by_atomic.setdefault(atomic_id, []).extend(dropped) + requests_by_atomic.setdefault(atomic_id, []).append(arguments) + responses_by_atomic.setdefault(atomic_id, []).append(response) + filters_by_atomic.setdefault(atomic_id, []).append(query_row["filter"]) + attempts_by_atomic.setdefault(atomic_id, []).append(attempt_count) + for atomic_id in sorted(requests_by_atomic): + plan = by_plan[atomic_id] + hits, duplicate_drops = canonicalize_retrieval_hits( + selected_by_atomic.get(atomic_id, []) + ) + dropped_by_atomic.setdefault(atomic_id, []).extend(duplicate_drops) + dropped = sorted(dropped_by_atomic.get(atomic_id, []), key=lambda row: (row["chunk_id"], row["drop_reason_code"])) + result_base = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": plan["query_plan_sha256"], + "snapshot_id": snapshot_id, + "snapshot_sha256": snapshot_sha256, + "result_status": "COMPLETE" if hits else "NO_HIT", + "selected_hits": hits, + "dropped_hits": dropped, + "issue_codes": [] if hits else ["RETRIEVAL_NO_HIT"], + } + results.append({**result_base, "result_sha256": digest(result_base)}) + receipt_base = { + "schema_version": "stage2_requirement_fact_retrieval_receipt.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": plan["query_plan_sha256"], + "request_sha256": digest(requests_by_atomic.get(atomic_id, [])), + "response_sha256": digest(responses_by_atomic.get(atomic_id, [])), + "transport": "MCP_OVER_HTTP_JSON_RPC", + "endpoint": WEAVIATE_MCP_URL, + "tool_name": "search_hybrid", + "collection_name": collection, + "tenant": tenant, + "snapshot_sha256": snapshot_sha256, + "filter_sha256": digest(filters_by_atomic.get(atomic_id, [])), + "configuration_digest": digest( + { + "collection_name": collection, + "tenant": tenant, + "query_count": len(requests_by_atomic.get(atomic_id, [])), + } + ), + "attempt_count": max(attempts_by_atomic.get(atomic_id, [1])), + "replay_status": "NOT_REQUESTED", + "read_only_verified": True, + "credential_ref": credential_ref, + "credential_material_persisted": False, + } + receipts.append({**receipt_base, "receipt_sha256": digest(receipt_base)}) + binding = by_atomic[atomic_id] + scope = next(row for row in scope_rows if row.get("corpus_scope_id") == binding.get("corpus_scope_id")) + crosswalk_by_element = { + str(row["corpus_element_id"]): row + for row in scope.get("slot_crosswalk_rows", []) + if isinstance(row, dict) and row.get("corpus_element_id") + } + for hit in hits: + matched_element = False + for element_id in sorted(str(item) for item in hit["metadata"].get("corpus_element_ids", [])): + crosswalk = crosswalk_by_element.get(element_id) + if crosswalk is None: + unmapped_by_atomic[atomic_id].append(hit["chunk_id"]) + continue + matched_element = True + if crosswalk.get("mapping_status") == "CONFLICT": + conflicting_by_atomic[atomic_id].append(hit["chunk_id"]) + pack_items[atomic_id].append( + { + "corpus_element_id": element_id, + "doctrine_role": crosswalk["doctrine_role"], + "chunk_id": hit["chunk_id"], + "chunk_sha256": hit["chunk_sha256"], + "source_document_id": hit["source_document_id"], + "source_document_sha256": hit["source_document_sha256"], + "source_locator": hit["source_locator"], + "authority_ids": sorted(str(item) for item in hit["metadata"].get("authority_ids", [])), + "proposition_ids": sorted(str(item) for item in hit["metadata"].get("proposition_ids", [])), + "stage1_slot_refs": sorted(str(item) for item in crosswalk.get("stage1_slot_refs", [])), + "mapping_status": crosswalk.get("mapping_status", "UNMAPPED"), + "fact_creation_allowed": False, + "legal_decision_override_allowed": False, + } + ) + if not matched_element: + unmapped_by_atomic[atomic_id].append(hit["chunk_id"]) + finally: + weaviate.close() + key = lambda row: str(row["atomic_claim_id"]) + return { + "queries": sorted(queries, key=key), + "results": sorted(results, key=key), + "receipts": sorted(receipts, key=lambda row: str(row["atomic_claim_id"])), + "pack_items": { + atomic_id: sorted(rows, key=lambda row: (row["corpus_element_id"], row["chunk_id"])) + for atomic_id, rows in sorted(pack_items.items()) + }, + "unmapped_by_atomic": {key: sorted(set(value)) for key, value in sorted(unmapped_by_atomic.items())}, + "conflicting_by_atomic": {key: sorted(set(value)) for key, value in sorted(conflicting_by_atomic.items())}, + } + + +def execute_core(inputs: Mapping[str, Any], assets: Mapping[str, Any], request: Mapping[str, Any], snapshot_digest: str) -> dict[str, Any]: + verdicts = inputs["verdicts"] + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="s2-20-c20-c21") as pool: + c20_future = pool.submit( + build_portfolio, + verdicts, + inputs["verdict_sources"], + assets["disposition_policy"], + ) + c21_future = pool.submit( + build_party_projection, + inputs["party_context"], + inputs.get("object_registry", {}), + ) + atomic_work, partitions, disposition_rows, group_dependencies = c20_future.result() + party_projection = c21_future.result() + for row in atomic_work: + remedy_kinds = sorted( + { + str(item.get("remedy_kind")) + for item in row.get("remedy_candidates", []) + if isinstance(item, dict) and item.get("remedy_kind") + } + ) + row["signature_projection"] = project_signature( + row["normalized_claim_signature"], + row["c20_projection"], + claim_c21_projection(row, party_projection), + {"remedy_mode": remedy_kinds[0] if len(remedy_kinds) == 1 else "UNKNOWN"}, + assets["signature_projection_registry"], + ) + case_registry = assets["case_registry"] + rule_registry = assets["rule_registry"] + bindings: list[dict[str, Any]] = [] + rule_asset_refs: dict[str, dict[str, list[dict[str, str]]]] = {} + issues: list[dict[str, Any]] = [] + for row in atomic_work: + signature = row["signature_projection"]["target_signature"] + case_type_id, status, codes, case_predicate_ids = bind_case(signature, case_registry) + rule, rule_codes, rule_predicate_ids = bind_rule(case_type_id, signature, rule_registry) + fk_codes: list[str] = [] + refs = { + "structured_relief_rule_refs": [], "markdown_source_refs": [], + "renderer_refs": [], "party_set_rule_refs": [], + "ce13_branch_refs": [], "review_policy_refs": [], + } + if rule is not None and case_type_id is not None: + fk_codes, refs = validate_rule_foreign_keys(case_type_id, rule, assets) + all_codes = sorted(set(codes + rule_codes + fk_codes)) + sub_status = ( + "BOUND" + if rule is not None and not fk_codes + else ("CONFLICT" if any("MULTI" in code for code in rule_codes) else "UNRESOLVED") + ) + rule_asset_refs[row["atomic_claim_id"]] = refs + binding = { + "atomic_claim_id": row["atomic_claim_id"], + "target_signature": signature, + "target_signature_sha256": row["signature_projection"]["target_signature_sha256"], + "projection_rule_ids": row["signature_projection"]["projection_rule_ids"], + "case_type_binding_status": status, + "matched_case_type_predicate_ids": case_predicate_ids, + "case_type_id": case_type_id, + "sub_rule_binding_status": sub_status, + "matched_sub_rule_predicate_ids": rule_predicate_ids, + "sub_rule_id": rule.get("sub_rule_id") if rule else None, + "rule_branch_key": rule.get("rule_branch_key") if rule else None, + "renderer_ids": sorted(str(item) for item in (rule.get("renderer_ids", []) if rule else []) if isinstance(item, str)), + "corpus_scope_id": rule.get("corpus_scope_id") if rule else None, + "element_set_id": rule.get("element_set_id") if rule else None, + "party_set_rule_id": rule.get("party_set_rule_id") if rule else None, + "ce13_branch_id": rule.get("ce13_branch_id") if rule else None, + "legal_content_status": "APPROVED" if rule is not None and not fk_codes else ("PENDING_LEGAL_CLASSIFICATION" if status != "BOUND" else "PENDING_LEGAL_CONTENT"), + "binding_issue_codes": all_codes, + } + bindings.append(binding) + for code in all_codes: + issues.append({"issue_id": stable_id("ISS", row["atomic_claim_id"], code), "code": code, "scope_ref": row["atomic_claim_id"], "status": "OPEN"}) + + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="s2-20-c22-c27") as pool: + c22_future = pool.submit( + calculate_all, + atomic_work, + bindings, + assets["activation_registry"], + assets["calculator_descriptor_hashes"], + assets.get("calculator_descriptors", {}), + assets.get("law_value_tables", []), + request, + ) + c27_future = pool.submit( + build_retrieval_branch, + atomic_work, + bindings, + assets["corpus_release"], + assets["corpus_scope"], + request, + ) + calculation_receipts = c22_future.result() + retrieval = c27_future.result() + + groups: dict[str, dict[str, Any]] = {} + for row in atomic_work: + group = groups.setdefault( + row["claim_group_id"], + { + "schema_version": "stage2_claim_group.v1", + "claim_group_id": row["claim_group_id"], + "atomic_claim_ids": [], + "party_refs": [], + "object_refs": [], + "case_type_ids": [], + "rule_branch_keys": [], + "renderer_ids": [], + "party_set_rule_ids": [], + "calculation_receipt_ids": [], + "corpus_scope_ids": [], + "amount_slots": [], + "period_slots": [], + "counter_performance_refs": [], + "procedural_declaration_refs": [], + "dependencies": [], + "drafting_permission": "WORKNOTE_ONLY", + "issue_refs": [], + }, + ) + group["atomic_claim_ids"].append(row["atomic_claim_id"]) + bound_by_id = {row["atomic_claim_id"]: row for row in bindings} + calc_by_atomic: dict[str, list[dict[str, Any]]] = {} + for receipt in calculation_receipts: + calc_by_atomic.setdefault(str(receipt["atomic_claim_id"]), []).append(receipt) + retrieval_by_atomic = {row["atomic_claim_id"]: row for row in retrieval["results"]} + atom_work_by_id = {row["atomic_claim_id"]: row for row in atomic_work} + for group in groups.values(): + group["atomic_claim_ids"].sort() + group_bindings = [bound_by_id[item] for item in group["atomic_claim_ids"]] + group_calculations = [receipt for item in group["atomic_claim_ids"] for receipt in calc_by_atomic.get(item, [])] + group["party_refs"] = sorted({ref for item in group["atomic_claim_ids"] for ref in atom_work_by_id[item]["right_holder_refs"] + atom_work_by_id[item]["obligor_refs"]}) + group["object_refs"] = sorted({ref for item in group["atomic_claim_ids"] for ref in atom_work_by_id[item]["object_refs"]}) + group["case_type_ids"] = sorted({str(row["case_type_id"]) for row in group_bindings if row["case_type_id"]}) + group["rule_branch_keys"] = sorted({str(row["rule_branch_key"]) for row in group_bindings if row["rule_branch_key"]}) + group["renderer_ids"] = sorted({item for row in group_bindings for item in row["renderer_ids"]}) + group["party_set_rule_ids"] = sorted({str(row["party_set_rule_id"]) for row in group_bindings if row["party_set_rule_id"]}) + group["calculation_receipt_ids"] = sorted(str(row["calculation_receipt_id"]) for row in group_calculations) + group["corpus_scope_ids"] = sorted({str(row["corpus_scope_id"]) for row in group_bindings if row["corpus_scope_id"]}) + group["dependencies"] = [row for row in group_dependencies if row["source_group_id"] == group["claim_group_id"]] + permission_codes: list[str] = [] + if any(row["case_type_binding_status"] != "BOUND" or row["sub_rule_binding_status"] != "BOUND" for row in group_bindings): + permission_codes.append("BINDING_NOT_EXACT") + if any(row["activation_status"] in {"ACTIVATION_GAP", "ACTIVATION_CONFLICT"} for row in group_calculations): + permission_codes.append("CALCULATION_NOT_CLOSED") + if any(retrieval_by_atomic[item]["result_status"] != "COMPLETE" for item in group["atomic_claim_ids"]): + permission_codes.append("REQUIREMENT_FACT_PACK_NOT_ADMITTED") + if assets["party_rules_eligible"] is not True: + permission_codes.append("PARTY_SET_RULES_NOT_ADMITTED") + if assets["signature_projection_eligible"] is not True: + permission_codes.append("SIGNATURE_PROJECTION_NOT_ADMITTED") + if assets["disposition_policy_eligible"] is not True: + permission_codes.append("DISPOSITION_POLICY_NOT_ADMITTED") + if assets["authority_admitted"] is not True: + permission_codes.append("AUTHORITY_RELEASE_NOT_ADMITTED") + group_issue_refs: list[str] = [] + for code in sorted(set(permission_codes)): + issue_id = stable_id("ISS", group["claim_group_id"], code) + group_issue_refs.append(issue_id) + issues.append({"issue_id": issue_id, "code": code, "scope_ref": group["claim_group_id"], "status": "OPEN"}) + group["issue_refs"] = group_issue_refs + legal_decisions = {str(atom_work_by_id[item]["legal_decision"]) for item in group["atomic_claim_ids"]} + group["drafting_permission"] = ( + "CLEAN_DRAFT" + if not permission_codes + else ("WORKNOTE_ONLY" if "UNRESOLVED" in legal_decisions else "REVIEW_DRAFT") + ) + + group_rows = sorted(groups.values(), key=lambda row: row["claim_group_id"]) + permission_by_group = {row["claim_group_id"]: row["drafting_permission"] for row in group_rows} + issue_refs_by_scope: dict[str, list[str]] = {} + for issue in issues: + issue_refs_by_scope.setdefault(str(issue["scope_ref"]), []).append(str(issue["issue_id"])) + atomic_rows: list[dict[str, Any]] = [] + for row in atomic_work: + binding = bound_by_id[row["atomic_claim_id"]] + atomic_rows.append( + { + "schema_version": "stage2_atomic_claim.v1", + "atomic_claim_id": row["atomic_claim_id"], + "source_claim_option_ids": [row["claim_option_id"]], + "normalized_signature_sha256": row["normalized_signature_sha256"], + "right_holder_refs": row["right_holder_refs"], + "obligor_refs": row["obligor_refs"], + "object_refs": row["object_refs"], + "remedy_mode": str(row["signature_projection"]["target_signature"].get("remedy_mode") or "UNKNOWN"), + "case_type_binding_status": binding["case_type_binding_status"], + "case_type_id": binding["case_type_id"], + "sub_rule_binding_status": binding["sub_rule_binding_status"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "claim_group_id": row["claim_group_id"], + "drafting_permission": permission_by_group[row["claim_group_id"]], + "source_refs": row["source_refs"], + "issue_refs": sorted(issue_refs_by_scope.get(row["atomic_claim_id"], [])), + } + ) + + issue_base = inputs["issue_ledger"] + base_rows = issue_base.get("issues", []) if isinstance(issue_base, dict) else [] + issue_plan = { + "schema_version": "stage2_issue_ledger_plan.v1", + "base_ledger_sha256": digest(inputs["issue_ledger_raw"]), + "preserved_base_issue_ids": sorted(str(row.get("issue_id")) for row in base_rows if isinstance(row, dict) and row.get("issue_id")), + "plan_issues": sorted(issues, key=lambda row: row["issue_id"]), + } + option_report = { + "schema_version": "stage2_option_conservation_report.v1", + "run_binding_digest": request["run_binding_digest"], + "all_option_ids": sorted(row["claim_option_id"] for row in disposition_rows), + "selected_option_ids": partitions["selected"], + "alternative_option_ids": partitions["alternative"], + "excluded_option_ids": partitions["excluded"], + "deferred_option_ids": partitions["deferred"], + "partition_disjoint": True, + "partition_union_exact": True, + "silent_loss_count": 0, + "duplicate_recovery_issue_refs": [], + "status": "PASS", + } + party_rule_index = { + str(rule.get("party_set_rule_id")): rule + for rule in assets.get("party_rules", {}).get("rules", []) + if isinstance(rule, dict) and rule.get("party_set_rule_id") + } + party_rows: list[dict[str, Any]] = [] + present_role_tokens = set(str(item) for item in party_projection.get("present_role_tokens", [])) + for group in group_rows: + rule_id = group["party_set_rule_ids"][0] if len(group["party_set_rule_ids"]) == 1 else "UNBOUND" + rule = party_rule_index.get(rule_id) + required_roles = sorted( + str(item) for item in (rule.get("required_role_tokens", []) if isinstance(rule, dict) else []) + ) + missing_roles = sorted(set(required_roles) - present_role_tokens) + if rule_id == "UNBOUND" or not isinstance(rule, dict): + missing_roles = sorted(set(missing_roles + ["PARTY_SET_RULE_UNBOUND"])) + identity_conflicts = party_projection["identity_issue_refs"] + party_rows.append( + { + "claim_group_id": group["claim_group_id"], + "party_set_rule_id": rule_id, + "required_role_tokens": required_roles, + "present_party_refs": group["party_refs"], + "missing_role_tokens": missing_roles, + "identity_conflict_refs": identity_conflicts, + "status": "REVIEW_REQUIRED" if missing_roles or identity_conflicts else "COMPLETE", + "issue_refs": group["issue_refs"], + } + ) + party_report = { + "schema_version": "stage2_party_completeness_report.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": party_rows, + "report_status": "REVIEW_REQUIRED" if any(row["status"] != "COMPLETE" for row in party_rows) else "COMPLETE", + } + evidence_inventory = inputs.get("evidence_inventory", {}) + evidence_items = evidence_inventory.get("items", []) if isinstance(evidence_inventory, dict) else [] + if not isinstance(evidence_items, list) or any(not isinstance(item, dict) for item in evidence_items): + raise S220Error("EVIDENCE_INVENTORY_SHAPE", "evidence inventory items must be objects") + evidence_ids = [item.get("evidence_id") for item in evidence_items] + if any(not isinstance(item, str) or not item for item in evidence_ids): + raise S220Error("EVIDENCE_ID_REQUIRED", "every evidence inventory row requires evidence_id") + if len(evidence_ids) != len(set(evidence_ids)): + raise S220Error("EVIDENCE_ID_DUPLICATE", "evidence inventory contains duplicate evidence_id") + exhibit_rows = [ + { + "evidence_ref": evidence_id, + "source_key": evidence_id, + "existing_label": None, + "assigned_label": f"갑 제{ordinal}호증", + "assignment_status": "DETERMINISTICALLY_ASSIGNED", + } + for ordinal, evidence_id in enumerate(sorted(evidence_ids), 1) + ] + exhibit_base = { + "schema_version": "stage2_exhibit_register.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": exhibit_rows, + } + exhibit_register = {**exhibit_base, "register_digest": digest(exhibit_base)} + report_documents = { + "option_conservation_report.json": (option_report, "schemas/relief_plan.schema.json#/$defs/option_conservation_report"), + "party_completeness_report.json": (party_report, "schemas/relief_plan.schema.json#/$defs/party_completeness_report"), + "exhibit_register.json": (exhibit_register, "schemas/relief_plan.schema.json#/$defs/exhibit_register"), + } + issue_raw = canonical_bytes(issue_plan) + plan_base = { + "schema_version": "stage2_canonical_relief_plan.v1", + "producer_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": snapshot_digest, + "parent_stage2_release_sha256": request["expected_parent_stage2_release_sha256"], + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "option_dispositions": disposition_rows, + "atomic_claims": atomic_rows, + "claim_groups": group_rows, + "group_dependencies": group_dependencies, + "selected_option_ids": partitions["selected"], + "alternative_option_ids": partitions["alternative"], + "excluded_option_ids": partitions["excluded"], + "deferred_option_ids": partitions["deferred"], + "report_refs": [ + {"path": path, "schema_ref": schema_ref, "sha256": digest(canonical_bytes(document))} + for path, (document, schema_ref) in sorted(report_documents.items()) + ], + "issue_ledger_ref": { + "path": "issue_ledger.plan.json", + "schema_ref": "schemas/relief_plan.schema.json#/$defs/issue_ledger_plan", + "sha256": digest(issue_raw), + }, + } + plan = {**plan_base, "plan_content_digest": digest(plan_base)} + issue_codes = [str(row["code"]) for row in issue_plan["plan_issues"]] + issue_summary = { + "total": len(issue_codes), + "legal": sum("LEGAL" in code or "AUTHORITY" in code for code in issue_codes), + "binding": sum("BINDING" in code or "RULE" in code for code in issue_codes), + "corpus": sum("CORPUS" in code or "REQUIREMENT_FACT" in code for code in issue_codes), + "calculation": sum("CALCULATION" in code or code.startswith("CE_") for code in issue_codes), + "technical": 0, + } + return { + "plan": plan, + "groups": group_rows, + "atomic": atomic_rows, + "bindings": bindings, + "issues": issue_plan, + "calculations": calculation_receipts, + "retrieval": retrieval, + "rule_asset_refs": rule_asset_refs, + "party_projection": party_projection, + "partitions": partitions, + "option_report": option_report, + "party_report": party_report, + "exhibit_register": exhibit_register, + "asset_hashes": assets["asset_hashes"], + "corpus_release": assets["corpus_release"], + "s2_20_code_sha256": assets["s2_20_code_sha256"], + "schema_documents": assets["schema_documents"], + "issue_summary": issue_summary, + } + + +def _require_mapping(value: Any, *, code: str, label: str) -> Mapping[str, Any]: + if not isinstance(value, dict): + raise S220Error(code, f"object required: {label}") + return value + + +def _verify_binding_text(raw: bytes, request: Mapping[str, Any]) -> None: + try: + text = raw.decode("utf-8", errors="strict") + except UnicodeDecodeError as exc: + raise S220Error("EXECUTOR_BINDING_UTF8", "executor binding must be UTF-8") from exc + marker = "\n - stage_id: S2_20\n" + framed = "\n" + text + if framed.count(marker) != 1: + raise S220Error("EXECUTOR_BINDING_S2_20_ROW", "exactly one S2_20 binding row is required") + section = framed.split(marker, 1)[1] + section = section.split("\n - stage_id:", 1)[0] + section = section.split("\nlegacy_fallbacks:", 1)[0] + + # Parse the closed scalar mapping by indentation instead of treating + # untrusted YAML as a bag of substrings. Sequence values are checked + # separately below; aliases, tags, flow mappings and duplicate scalar + # paths are not accepted in the live trust row. + stack: list[tuple[int, str]] = [] + scalars: dict[Any, str] = {} + for raw_line in section.splitlines(): + if not raw_line.strip(): + continue + if "\t" in raw_line or raw_line.lstrip().startswith(("#", "&", "*", "!", "{", "[")): + raise S220Error("EXECUTOR_BINDING_YAML_PROFILE", "binding uses a forbidden YAML construct") + indent = len(raw_line) - len(raw_line.lstrip(" ")) + stripped = raw_line.strip() + if stripped.startswith("- "): + continue + match = re.fullmatch(r"([A-Za-z0-9_]+):(.*)", stripped) + if match is None: + raise S220Error("EXECUTOR_BINDING_YAML_PROFILE", f"unsupported binding line: {stripped}") + while stack and stack[-1][0] >= indent: + stack.pop() + key, tail = match.group(1), match.group(2).strip() + path = tuple(item[1] for item in stack) + (key,) + if tail: + if path in scalars: + raise S220Error("EXECUTOR_BINDING_DUPLICATE_KEY", ".".join(path)) + if len(tail) >= 2 and tail[0] == tail[-1] and tail[0] in {'"', "'"}: + tail = tail[1:-1] + scalars[path] = tail + else: + stack.append((indent, key)) + + required_values = { + ("binding_id",): "S2-BINDING-S2_20-CODE-EXECUTOR-V1", + ("workflow_id",): "S2_20", + ("execution_class",): "NON-LLM-DETERMINISTIC", + ("active_runtime_authority",): "true", + ("agent_script_ref", "path"): "agent_scripts/Stage_2_S2_20.yml", + ("agent_script_ref", "sha256"): request["expected_s2_20_agent_sha256"], + ("workflow_contract_ref", "path"): "workflows/S2_20_canonical_relief_plan_reduce.yml", + ("inline_code_receipt_ref", "path"): "manifest/s2_20_inline_code_receipt.json", + ("stage2_release_ref", "path"): "manifest/stage2_release.json", + ("stage2_release_ref", "sha256"): request["expected_parent_stage2_release_sha256"], + ("expected_release_sha256",): request["expected_parent_stage2_release_sha256"], + ("agent_script_sha256",): request["expected_s2_20_agent_sha256"], + ("mcp_server_id",): "code-executor", + ("tool_name",): "run_code", + ("language",): "python", + ("network",): "agent-network", + ("timeout_seconds",): "300", + ("runtime_image_status",): "PINNED_VERIFIED", + ("dependency_lock", "requirements"): "httpx==0.28.1", + ("dependency_lock", "lock_status"): "STATIC_PIN_BOUND", + ("localdocs_contract", "fixed_request_path"): "stage2_control/s2_20_request.json", + ("external_mcp_contract", "endpoint"): WEAVIATE_MCP_URL, + ("external_mcp_contract", "operation_mode"): "READ_ONLY_EXACT_FILTER", + ("external_mcp_contract", "admission_status"): "LIVE_VERIFIED", + ("egress_profile_status",): "LIVE_VERIFIED", + ("live_admission_status",): "LIVE_CANARY_ADMITTED", + } + mismatches = sorted( + ".".join(path) + for path, expected in required_values.items() + if scalars.get(path) != expected + ) + for path in (("runtime_image_digest",), ("canonical_code_sha256",)): + if HEX64.fullmatch(scalars.get(path, "")) is None: + mismatches.append(".".join(path)) + for path in (("external_mcp_contract", "collection"), ("external_mcp_contract", "tenant")): + value = scalars.get(path, "") + if not value or value.startswith("PENDING"): + mismatches.append(".".join(path)) + if any(value.startswith("PENDING") for value in scalars.values()): + mismatches.append("pending_scalar") + + allowlist_marker = " read_path_allowlist:\n" + allowlist_tail = section.split(allowlist_marker, 1) + if len(allowlist_tail) != 2: + mismatches.append("localdocs_contract.read_path_allowlist") + allowlist_text = "" + else: + allowlist_text = allowlist_tail[1].split(" write_root_rule:", 1)[0] + required_allowlist_entries = ( + f"{ASSET_ROOT}/schemas/deployment.schema.json", + f"{ASSET_ROOT}/rules/relief/.md", + f"{ASSET_ROOT}/legal_review/.json", + ) + for entry in required_allowlist_entries: + if f" - {entry}\n" not in allowlist_text + "\n": + mismatches.append(f"allowlist:{entry}") + if mismatches: + raise S220Error( + "EXECUTOR_BINDING_NOT_ADMITTED", + "S2_20 binding is incomplete or structurally inconsistent", + details={"mismatches": sorted(set(mismatches))}, + ) + + +def _admission_signed_material(admission: Mapping[str, Any]) -> dict[str, Any]: + return { + key: value + for key, value in admission.items() + if key not in {"signature", "signature_verification_status", "signed_payload_sha256"} + } + + +def _verify_external_trust( + raw_map: Mapping[str, bytes], + fixed: Mapping[str, str], + request: Mapping[str, Any], + schema_documents: Mapping[str, Any], +) -> tuple[Mapping[str, Any], Mapping[str, Any], Mapping[str, Any]]: + if digest(raw_map[fixed["binding"]]) != request["expected_s2_20_executor_binding_sha256"]: + raise S220Error("EXECUTOR_BINDING_HASH_MISMATCH", "executor binding bytes differ") + _verify_binding_text(raw_map[fixed["binding"]], request) + if digest(raw_map[fixed["agent"]]) != request["expected_s2_20_agent_sha256"]: + raise S220Error("S2_20_AGENT_HASH_MISMATCH", "deployment Agent bytes differ") + if digest(raw_map[fixed["s2_10_release"]]) != request["expected_s2_10_release_sha256"]: + raise S220Error("S2_10_RELEASE_HASH_MISMATCH", "S2_10 child release bytes differ") + + # Host CAS is the first semantic write-authority gate. No plan + # artifact, including a diagnostic, may be published unless this + # exact run-bound receipt has passed its release-bound schema. + host_cas = _require_mapping(load_json(raw_map[fixed["host_cas"]], label=fixed["host_cas"]), code="HOST_CAS_SHAPE", label="host CAS") + _validate_loaded_value( + host_cas, + raw_map[fixed["host_cas"]], + "schemas/deployment.schema.json#/$defs/s2_20_host_cas_receipt", + label=fixed["host_cas"], + schema_documents=schema_documents, + ) + expected_scope = f"S2_20:{request['run_binding_digest']}" + if ( + host_cas.get("run_binding_digest") != request["run_binding_digest"] + or host_cas.get("exclusivity_key") != expected_scope + ): + raise S220Error("HOST_CAS_NOT_ACQUIRED", "host CAS receipt does not prove the exact single writer") + + if digest(raw_map[fixed["admission"]]) != request["detached_admission_receipt_sha256"]: + raise S220Error("ADMISSION_RECEIPT_HASH_MISMATCH", "detached admission bytes differ") + admission = _require_mapping(load_json(raw_map[fixed["admission"]], label=fixed["admission"]), code="ADMISSION_SHAPE", label="admission") + _validate_loaded_value( + admission, + raw_map[fixed["admission"]], + "schemas/deployment.schema.json#/$defs/stage2_deterministic_admission_receipt", + label=fixed["admission"], + schema_documents=schema_documents, + ) + if admission.get("admission_status") not in {"CANARY_ADMITTED", "PRODUCTION_ADMITTED"}: + raise S220Error("ADMISSION_PENDING", "direct execution requires canary or production admission") + if admission.get("parent_release_sha256") != request["expected_parent_stage2_release_sha256"]: + raise S220Error("ADMISSION_PARENT_RELEASE", "admission parent release differs") + if admission.get("executor_binding_sha256") != request["expected_s2_20_executor_binding_sha256"]: + raise S220Error("ADMISSION_EXECUTOR_BINDING", "admission executor binding differs") + if "S2_20" not in admission.get("present_deterministic_stage_ids", []): + raise S220Error("ADMISSION_STAGE_SET", "S2_20 is absent from deterministic admission") + signature = admission.get("signature") + if ( + not isinstance(signature, str) + or not signature + or signature.startswith("PENDING") + or admission.get("signature_verification_status") != "BACKEND_VERIFIED" + ): + raise S220Error("ADMISSION_SIGNATURE", "backend-verified external admission signature is required") + signed_payload_sha256 = require_hex( + admission.get("signed_payload_sha256"), code="ADMISSION_SIGNED_PAYLOAD_HASH" + ) + signed_material = _admission_signed_material(admission) + if signed_payload_sha256 != digest(signed_material): + raise S220Error("ADMISSION_SIGNED_PAYLOAD_HASH", "admission signed payload digest differs") + capability_sha256 = require_hex( + admission.get("backend_capability_receipt_sha256"), + code="ADMISSION_BACKEND_CAPABILITY_HASH", + ) + if capability_sha256 != host_cas.get("backend_capability_receipt_sha256"): + raise S220Error( + "ADMISSION_BACKEND_CAPABILITY_MISMATCH", + "admission verification is not bound to the host CAS backend capability", + ) + matching_stage_receipts = [ + row for row in admission.get("stage_receipts", []) + if isinstance(row, dict) and row.get("asset_id") == "RECEIPT-S2_20-INLINE-CODE" + ] + if ( + len(matching_stage_receipts) != 1 + or matching_stage_receipts[0].get("path") != "manifest/s2_20_inline_code_receipt.json" + or matching_stage_receipts[0].get("sha256") != digest(raw_map[fixed["inline_receipt"]]) + ): + raise S220Error("ADMISSION_STAGE_RECEIPT", "admission does not bind the exact S2_20 inline receipt") + child = _require_mapping(load_json(raw_map[fixed["s2_10_release"]], label=fixed["s2_10_release"]), code="S2_10_RELEASE_SHAPE", label="S2_10 release") + parent_ref = child.get("parent_stage2_release_ref", {}) + if not isinstance(parent_ref, dict) or parent_ref.get("sha256") != request["expected_parent_stage2_release_sha256"]: + raise S220Error("S2_10_PARENT_RELEASE_MISMATCH", "S2_10 child release is not bound to the parent") + release = _require_mapping(load_json(raw_map[fixed["release"]], label=fixed["release"]), code="PARENT_RELEASE_SHAPE", label="parent release") + return release, child, admission + + +def _require_object_self_hash( + value: Mapping[str, Any], field: str, *, code: str, label: str +) -> str: + declared = value.get(field) + require_hex(declared, code=code) + material = {key: item for key, item in value.items() if key != field} + if declared != digest(material): + raise S220Error(code, f"object self hash differs: {label}") + return str(declared) + + +def _validate_loaded_value( + value: Any, + raw: bytes, + schema_ref: str, + *, + label: str, + schema_documents: Mapping[str, Any], + require_canonical_bytes: bool = True, +) -> None: + validate_artifact_payload( + canonical_bytes(value), schema_ref, label, schema_documents + ) + if require_canonical_bytes and raw != canonical_bytes(value): + raise S220Error( + "UPSTREAM_CANONICAL_BYTES_MISMATCH", + f"upstream JSON is not canonical bytes: {label}", + ) + + +def _sealed_child_asset_hash(child_release: Mapping[str, Any], path: str) -> str: + rows = child_release.get("sealed_assets", []) + matches = [ + row for row in rows + if isinstance(row, dict) and row.get("path") == path + ] if isinstance(rows, list) else [] + if len(matches) != 1: + raise S220Error( + "S2_10_CHILD_ASSET_BINDING", + f"child release must seal exactly one asset row: {path}", + ) + return require_hex(matches[0].get("sha256"), code="S2_10_CHILD_ASSET_HASH") + + +def _validate_special_legal_asset( + value: Any, + *, + schema_version: str, + identity_field: str, + identity: str, + rows_field: str, + label: str, +) -> Mapping[str, Any]: + row = _require_mapping( + value, code="SPECIAL_LEGAL_ASSET_SHAPE", label=label + ) + if ( + row.get("schema_version") != schema_version + or row.get(identity_field) != identity + or row.get("status") not in {"PENDING_LEGAL_CONTENT", "LEGAL_CONTENT_ADMITTED"} + or not isinstance(row.get(rows_field), list) + ): + raise S220Error( + "SPECIAL_LEGAL_ASSET_CONTRACT", + f"special legal asset envelope differs: {label}", + ) + if row.get("execution_eligible") is True: + if row.get("status") != "LEGAL_CONTENT_ADMITTED" or not row.get(rows_field): + raise S220Error( + "SPECIAL_LEGAL_ASSET_NOT_ADMITTED", + f"executable special asset lacks admitted rows: {label}", + ) + elif row.get("execution_eligible") is not False: + raise S220Error( + "SPECIAL_LEGAL_ASSET_ELIGIBILITY", + f"execution_eligible must be boolean: {label}", + ) + return row + + +def validate_upstream_handoff( + all_raw: Mapping[str, bytes], + fixed: Mapping[str, str], + request: Mapping[str, Any], + root: str, + cluster_ids: Sequence[str], + wave_paths: Sequence[str], + schema_documents: Mapping[str, Any], + child_release: Mapping[str, Any], +) -> dict[str, Any]: + """Validate and preserve the complete S2_00/S2_10 handoff consumed here.""" + + def loaded(path: str, schema_ref: str) -> Any: + value = load_json(all_raw[path], label=path) + _validate_loaded_value( + value, + all_raw[path], + schema_ref, + label=path, + schema_documents=schema_documents, + ) + return value + + ingress = _require_mapping( + loaded(fixed["ingress_status"], "schemas/ingress.schema.json#/$defs/ingress_status"), + code="INGRESS_STATUS_SHAPE", + label="ingress status", + ) + cluster_plan = _require_mapping( + loaded(fixed["cluster_plan"], "schemas/context.schema.json#/$defs/cluster_plan"), + code="CLUSTER_PLAN_SHAPE", + label="cluster plan", + ) + bundle_plan = _require_mapping( + loaded(fixed["bundle_plan"], "schemas/context.schema.json#/$defs/bundle_plan"), + code="BUNDLE_PLAN_SHAPE", + label="bundle plan", + ) + context_values: dict[str, Any] = {} + for key, definition in ( + ("case_context", "case_context"), + ("evidence_inventory", "evidence_inventory"), + ("object_registry", "object_registry"), + ("party_context", "party_and_title_context"), + ("slot_crosswalk", "slot_crosswalk"), + ): + context_values[key] = loaded( + fixed[key], f"schemas/context.schema.json#/$defs/{definition}" + ) + issue_ledger = loaded( + fixed["issue_ledger"], + "schemas/review_status.schema.json#/$defs/issue_ledger_base", + ) + + binding_receipt = _require_mapping( + ingress.get("run_binding_receipt"), + code="INGRESS_RUN_BINDING_SHAPE", + label="ingress run binding", + ) + barrier = _require_mapping( + ingress.get("output_barrier"), + code="INGRESS_BARRIER_SHAPE", + label="ingress output barrier", + ) + expected_root = f"{root}/" + if ( + binding_receipt.get("run_binding_digest") != request["run_binding_digest"] + or binding_receipt.get("canonical_output_root") != expected_root + or barrier.get("canonical_output_root") != expected_root + or barrier.get("branch") != "NORMAL" + or barrier.get("written_last") is not True + or barrier.get("non_status_artifacts_read_back_verified") is not True + or ingress.get("route") not in {"TO_S2_10", "TO_S2_10_WITH_ISSUES"} + ): + raise S220Error( + "INGRESS_BARRIER_BINDING", + "S2_00 ingress status does not prove the exact normal-route binding", + ) + if sorted(ingress.get("executable_cluster_ids", [])) != sorted(cluster_ids): + raise S220Error( + "INGRESS_CLUSTER_SET", + "ingress executable cluster set differs from cluster plan", + ) + artifact_rows = barrier.get("artifacts", []) + if not isinstance(artifact_rows, list) or barrier.get("artifact_set_digest") != digest(artifact_rows): + raise S220Error( + "INGRESS_ARTIFACT_SET_DIGEST", + "ingress output artifact set digest differs", + ) + artifact_by_path: dict[str, Mapping[str, Any]] = {} + for row in artifact_rows: + if not isinstance(row, dict) or not isinstance(row.get("path"), str): + raise S220Error("INGRESS_ARTIFACT_ROW_SHAPE", "ingress artifact row is invalid") + if row["path"] in artifact_by_path: + raise S220Error("INGRESS_ARTIFACT_PATH_DUPLICATE", f"duplicate ingress artifact: {row['path']}") + artifact_by_path[row["path"]] = row + s2_00_consumed_paths = [ + fixed[key] + for key in ( + "case_context", "evidence_inventory", "object_registry", "cluster_plan", + "party_context", "slot_crosswalk", "bundle_plan", "issue_ledger", + ) + ] + [f"{root}/context/cluster_slices/{cluster_id}.json" for cluster_id in cluster_ids] + for path in s2_00_consumed_paths: + relative = path.removeprefix(f"{root}/") + row = artifact_by_path.get(relative) + if not isinstance(row, dict) or row.get("raw_sha256") != digest(all_raw[path]): + raise S220Error( + "INGRESS_ARTIFACT_LINK_MISMATCH", + f"ingress barrier does not bind consumed artifact: {relative}", + ) + + expected_context_schema_hash = digest(all_raw[fixed["schema_context"]]) + expected_run_id = ingress.get("run_id") + expected_input_set = binding_receipt.get("input_set_digest") + expected_release_digest = binding_receipt.get("stage2_release_digest") + for label, value in { + "cluster_plan": cluster_plan, + "bundle_plan": bundle_plan, + **context_values, + }.items(): + header = value.get("artifact_header") if isinstance(value, dict) else None + if ( + not isinstance(header, dict) + or header.get("schema_sha256") != expected_context_schema_hash + or header.get("run_id") != expected_run_id + or header.get("input_set_digest") != expected_input_set + or header.get("stage2_release_digest") != expected_release_digest + ): + raise S220Error( + "CONTEXT_HEADER_BINDING", + f"context artifact header differs: {label}", + ) + + cluster_slices: dict[str, Mapping[str, Any]] = {} + slice_raw_hashes: dict[str, str] = {} + for cluster_id in sorted(cluster_ids): + path = f"{root}/context/cluster_slices/{cluster_id}.json" + slice_value = _require_mapping( + loaded(path, "schemas/context.schema.json#/$defs/cluster_slice"), + code="CLUSTER_SLICE_SHAPE", + label=path, + ) + slice_material = {key: value for key, value in slice_value.items() if key != "slice_digest"} + if ( + slice_value.get("cluster_id") != cluster_id + or slice_value.get("immutable") is not True + or slice_value.get("slice_digest") != digest(slice_material) + ): + raise S220Error( + "CLUSTER_SLICE_BINDING", + f"cluster slice identity/self hash differs: {cluster_id}", + ) + header = slice_value.get("artifact_header") + if ( + not isinstance(header, dict) + or header.get("schema_sha256") != expected_context_schema_hash + or header.get("run_id") != expected_run_id + or header.get("input_set_digest") != expected_input_set + or header.get("stage2_release_digest") != expected_release_digest + ): + raise S220Error("CLUSTER_SLICE_HEADER", f"cluster slice header differs: {cluster_id}") + cluster_slices[cluster_id] = slice_value + slice_raw_hashes[cluster_id] = digest(all_raw[path]) + + child_agent_hash = _sealed_child_asset_hash( + child_release, "agent_scripts/Stage_2_S2_10.yml" + ) + child_binding_hash = _sealed_child_asset_hash( + child_release, "deployment/stage2_s2_10_llm_binding.yml" + ) + cohorts = bundle_plan.get("cohorts", []) + if not isinstance(cohorts, list): + raise S220Error("BUNDLE_COHORT_SHAPE", "bundle cohorts must be an array") + cohort_ids: list[str] = [] + executable_ids: list[str] = [] + non_executable_ids: list[str] = [] + member_wave_by_cluster: dict[str, int] = {} + for cohort in cohorts: + if not isinstance(cohort, dict) or not isinstance(cohort.get("bundle_cohort_id"), str): + raise S220Error("BUNDLE_COHORT_SHAPE", "bundle cohort object/ID required") + cohort_id = str(cohort["bundle_cohort_id"]) + cohort_ids.append(cohort_id) + status = cohort.get("cohort_status") + if status == "EXECUTABLE": + executable_ids.append(cohort_id) + elif status == "NON_EXECUTABLE": + non_executable_ids.append(cohort_id) + member_ids = cohort.get("cohort_member_cluster_ids", []) + member_slices = cohort.get("member_slices", []) + if ( + member_ids != sorted(member_ids) + or not isinstance(member_slices, list) + or member_slices != sorted(member_slices, key=lambda row: str(row.get("cluster_id", ""))) + or member_ids != [row.get("cluster_id") for row in member_slices if isinstance(row, dict)] + ): + raise S220Error("BUNDLE_MEMBER_SET", f"bundle membership differs: {cohort_id}") + selected_refs = cohort.get("selected_context_refs", []) + if ( + not isinstance(selected_refs, list) + or [row.get("order_index") for row in selected_refs if isinstance(row, dict)] != list(range(len(selected_refs))) + or cohort.get("selected_context_ordered_refs_sha256") != digest(selected_refs) + ): + raise S220Error("BUNDLE_SELECTED_CONTEXT", f"selected context order/hash differs: {cohort_id}") + if ( + cohort.get("s2_10_agent_sha256") != child_agent_hash + or cohort.get("s2_10_llm_binding_sha256") != child_binding_hash + ): + raise S220Error("BUNDLE_S2_10_BINDING", f"bundle S2_10 binding differs: {cohort_id}") + membership_hash = digest(member_ids) + member_slice_hash = digest(member_slices) + receipt = cohort.get("context_materialization_receipt") + echoed = { + "bundle_cohort_id": cohort_id, + "s2_10_agent_sha256": child_agent_hash, + "s2_10_llm_binding_sha256": child_binding_hash, + "selected_context_ordered_refs_sha256": digest(selected_refs), + "cohort_membership_sha256": membership_hash, + "member_slice_ref_set_sha256": member_slice_hash, + } + materialization = { + "handoff_contract_version": cohort.get("handoff_contract_version"), + **echoed, + } + if ( + not isinstance(receipt, dict) + or any(receipt.get(key) != value for key, value in echoed.items()) + or receipt.get("materialization_input_sha256") != digest(materialization) + ): + raise S220Error("BUNDLE_MATERIALIZATION_RECEIPT", f"materialization receipt differs: {cohort_id}") + if status == "EXECUTABLE": + if receipt.get("verification_status") != "PASS" or cohort.get("reason_codes") != []: + raise S220Error("BUNDLE_EXECUTABLE_STATUS", f"executable cohort is not PASS: {cohort_id}") + for member in member_slices: + cluster_id = str(member["cluster_id"]) + expected_path = f"context/cluster_slices/{cluster_id}.json" + if ( + cluster_id in member_wave_by_cluster + or cluster_id not in slice_raw_hashes + or member.get("path") != expected_path + or member.get("sha256") != slice_raw_hashes[cluster_id] + or not isinstance(member.get("dependency_wave_ordinal"), int) + ): + raise S220Error("BUNDLE_SLICE_LINK", f"bundle slice link differs: {cluster_id}") + member_wave_by_cluster[cluster_id] = int(member["dependency_wave_ordinal"]) + if len(cohort_ids) != len(set(cohort_ids)): + raise S220Error("BUNDLE_COHORT_DUPLICATE", "bundle cohort IDs must be unique") + if ( + executable_ids != bundle_plan.get("executable_bundle_cohort_ids") + or non_executable_ids != bundle_plan.get("non_executable_bundle_cohort_ids") + or set(member_wave_by_cluster) != set(cluster_ids) + ): + raise S220Error("BUNDLE_EXECUTABLE_COVERAGE", "bundle executable coverage differs") + expected_wave_ordinals = list(range(len(wave_paths))) + if sorted(set(member_wave_by_cluster.values())) != expected_wave_ordinals: + raise S220Error("BUNDLE_WAVE_ORDINALS", "bundle wave ordinals are not contiguous/exact") + + status = _require_mapping( + loaded(fixed["s2_10_status"], "schemas/s2_10.schema.json#/$defs/global_publish_status"), + code="S2_10_STATUS_SHAPE", + label="S2_10 publish status", + ) + if ( + status.get("status") != "COMPLETE" + or status.get("route") != "TO_S2_20" + or status.get("run_binding_digest") != request["run_binding_digest"] + or status.get("parent_stage2_release_sha256") != request["expected_parent_stage2_release_sha256"] + or status.get("s2_10_release_sha256") != request["expected_s2_10_release_sha256"] + or status.get("status_written_last") is not True + or sorted(status.get("expected_executable_cluster_ids", [])) != sorted(cluster_ids) + or sorted(status.get("valid_domain_verdict_cluster_ids", [])) != sorted(cluster_ids) + or status.get("terminal_technical_failure_cluster_ids") != [] + ): + raise S220Error("S2_10_BARRIER", "S2_10 global barrier differs") + _require_object_self_hash( + status, "status_sha256", code="S2_10_STATUS_SELF_DIGEST", label="S2_10 publish status" + ) + producer = child_release.get("producer_contract", {}) + producer_digest = producer.get("s2_10_producer_contract_digest") if isinstance(producer, dict) else None + require_hex(producer_digest, code="S2_10_PRODUCER_CONTRACT") + + verdicts: list[Mapping[str, Any]] = [] + verdict_sources: dict[str, dict[str, str]] = {} + item_receipts: dict[str, Mapping[str, Any]] = {} + issue_parts: dict[str, Mapping[str, Any]] = {} + assumption_parts: dict[str, Mapping[str, Any]] = {} + usage_parts: dict[str, Mapping[str, Any]] = {} + request_ids: set[str] = set() + for cluster_id in sorted(cluster_ids): + slice_path = f"{root}/context/cluster_slices/{cluster_id}.json" + verdict_path = f"{root}/map_s2_10/domain_verdicts/{cluster_id}.json" + item_path = f"{root}/map_s2_10/item_receipts/{cluster_id}.json" + issue_path = f"{root}/map_s2_10/issue_patches/{cluster_id}.json" + assumption_path = f"{root}/map_s2_10/assumption_parts/{cluster_id}.json" + usage_path = f"{root}/map_s2_10/usage_parts/{cluster_id}.json" + verdict = _require_mapping( + loaded(verdict_path, "schemas/s2_10.schema.json#/$defs/canonical_domain_verdict"), + code="DOMAIN_VERDICT_SHAPE", + label=cluster_id, + ) + verdict_hash = _require_object_self_hash( + verdict, "domain_verdict_sha256", code="DOMAIN_VERDICT_SELF_DIGEST", label=cluster_id + ) + if ( + verdict.get("cluster_id") != cluster_id + or verdict.get("run_binding_digest") != request["run_binding_digest"] + or verdict.get("cluster_slice_sha256") != digest(all_raw[slice_path]) + or verdict.get("producer_contract_digest") != producer_digest + ): + raise S220Error("DOMAIN_VERDICT_LINK", f"domain verdict link differs: {cluster_id}") + item = _require_mapping( + loaded(item_path, "schemas/s2_10.schema.json#/$defs/item_receipt"), + code="ITEM_RECEIPT_SHAPE", + label=item_path, + ) + _require_object_self_hash( + item, "receipt_sha256", code="ITEM_RECEIPT_SELF_DIGEST", label=cluster_id + ) + if ( + item.get("cluster_id") != cluster_id + or item.get("run_binding_digest") != request["run_binding_digest"] + or item.get("domain_verdict_path") != f"map_s2_10/domain_verdicts/{cluster_id}.json" + or item.get("domain_verdict_sha256") != verdict_hash + or item.get("read_back_sha256") != verdict_hash + or item.get("validation_status") != "PASS" + or item.get("persistence_status") not in {"PUBLISHED", "IDEMPOTENT_BYTE_IDENTICAL"} + or item.get("request_id") != verdict.get("request_id") + or item.get("wave_ordinal") != verdict.get("wave_ordinal") + or item.get("attempt_no") != verdict.get("attempt_no") + or item.get("raw_model_output_sha256") != verdict.get("raw_model_output_sha256") + ): + raise S220Error("ITEM_RECEIPT_INVALID", f"invalid S2_10 item receipt: {cluster_id}") + issue = _require_mapping( + loaded(issue_path, "schemas/s2_10.schema.json#/$defs/issue_part"), + code="ISSUE_PART_SHAPE", + label=issue_path, + ) + assumption = _require_mapping( + loaded(assumption_path, "schemas/s2_10.schema.json#/$defs/assumption_part"), + code="ASSUMPTION_PART_SHAPE", + label=assumption_path, + ) + usage = _require_mapping( + loaded(usage_path, "schemas/s2_10.schema.json#/$defs/usage_part"), + code="USAGE_PART_SHAPE", + label=usage_path, + ) + for part, expected_hash, label in ( + (issue, item.get("issue_part_sha256"), "issue"), + (assumption, item.get("assumption_part_sha256"), "assumption"), + (usage, item.get("usage_part_sha256"), "usage"), + ): + part_hash = _require_object_self_hash( + part, "part_sha256", code=f"{label.upper()}_PART_SELF_DIGEST", label=cluster_id + ) + header = part.get("header") + if ( + part_hash != expected_hash + or not isinstance(header, dict) + or header.get("producer_contract_digest") != producer_digest + or header.get("request_id") != item.get("request_id") + or header.get("run_binding_digest") != request["run_binding_digest"] + or header.get("wave_ordinal") != item.get("wave_ordinal") + or header.get("attempt_no") != item.get("attempt_no") + or header.get("cluster_id") != cluster_id + or header.get("domain_verdict_sha256") != verdict_hash + ): + raise S220Error(f"{label.upper()}_PART_LINK", f"{label} part link differs: {cluster_id}") + if assumption.get("assumptions") != verdict.get("assumptions"): + raise S220Error("ASSUMPTION_PART_CONTENT", f"assumption part differs from verdict: {cluster_id}") + request_ids.add(str(item.get("request_id"))) + verdicts.append(verdict) + verdict_sources[cluster_id] = { + "path": verdict_path.removeprefix(f"{root}/"), + "sha256": digest(all_raw[verdict_path]), + } + item_receipts[cluster_id] = item + issue_parts[cluster_id] = issue + assumption_parts[cluster_id] = assumption + usage_parts[cluster_id] = usage + if len(request_ids) != 1: + raise S220Error("S2_10_REQUEST_ID_SET", "all S2_10 items must share one request ID") + + wave_receipts: list[Mapping[str, Any]] = [] + wave_cluster_union: set[str] = set() + for ordinal, path in enumerate(wave_paths): + wave = _require_mapping( + loaded(path, "schemas/s2_10.schema.json#/$defs/wave_receipt"), + code="WAVE_RECEIPT_SHAPE", + label=path, + ) + _require_object_self_hash( + wave, "receipt_sha256", code="WAVE_RECEIPT_SELF_DIGEST", label=path + ) + expected_clusters = sorted( + cluster_id for cluster_id, wave_ordinal in member_wave_by_cluster.items() + if wave_ordinal == ordinal + ) + if ( + wave.get("request_id") not in request_ids + or wave.get("run_binding_digest") != request["run_binding_digest"] + or wave.get("wave_ordinal") != ordinal + or wave.get("is_final_wave") != (ordinal == len(wave_paths) - 1) + or sorted(wave.get("expected_wave_cluster_ids", [])) != expected_clusters + or sorted(wave.get("valid_domain_verdict_cluster_ids", [])) != expected_clusters + or wave.get("terminal_technical_failure_cluster_ids") != [] + or wave.get("wave_status") != "WAVE_COMPLETE" + or wave.get("route") != ("TO_S2_20" if ordinal == len(wave_paths) - 1 else "NEXT_WAVE") + or wave.get("written_once") is not True + ): + raise S220Error("WAVE_RECEIPT_INVALID", f"terminal wave receipt differs: {ordinal}") + for cluster_id in expected_clusters: + if item_receipts[cluster_id].get("wave_ordinal") != ordinal: + raise S220Error("ITEM_WAVE_LINK", f"item/wave ordinal differs: {cluster_id}") + wave_cluster_union.update(expected_clusters) + wave_receipts.append(wave) + if wave_cluster_union != set(cluster_ids): + raise S220Error("WAVE_CLUSTER_COVERAGE", "terminal wave coverage differs") + if status.get("terminal_wave_receipt_sha256s") != [ + wave["receipt_sha256"] for wave in wave_receipts + ]: + raise S220Error("WAVE_RECEIPT_HASH_LINK", "global status wave hashes differ") + + return { + "ingress_status": ingress, + "cluster_plan": cluster_plan, + "bundle_plan": bundle_plan, + "cluster_slices": cluster_slices, + "verdicts": verdicts, + "verdict_sources": verdict_sources, + "item_receipts": item_receipts, + "issue_patches": issue_parts, + "assumption_parts": assumption_parts, + "usage_parts": usage_parts, + "wave_receipts": wave_receipts, + "case_context": context_values["case_context"], + "evidence_inventory": context_values["evidence_inventory"], + "object_registry": context_values["object_registry"], + "party_context": context_values["party_context"], + "slot_crosswalk": context_values["slot_crosswalk"], + "issue_ledger": issue_ledger, + } + + +def hydrate( + localdocs: McpClient, + request: Mapping[str, Any], + publication_guard: dict[str, bool], +) -> tuple[dict[str, Any], dict[str, Any], str]: + root = request["stage2_run_root_ref"] + fixed = { + "release": f"{ASSET_ROOT}/manifest/stage2_release.json", + "module_manifest": f"{ASSET_ROOT}/manifest/module_manifest.json", + "s2_10_release": f"{ASSET_ROOT}/manifest/s2_10_release.json", + "authority_release": f"{ASSET_ROOT}/manifest/authority_release.json", + "case_coverage": f"{ASSET_ROOT}/manifest/case_type_coverage.json", + "binding": f"{ASSET_ROOT}/deployment/stage2_code_executor_binding.yml", + "agent": f"{ASSET_ROOT}/agent_scripts/Stage_2_S2_20.yml", + "inline_receipt": f"{ASSET_ROOT}/manifest/s2_20_inline_code_receipt.json", + "admission": request["detached_admission_receipt_ref"], + "host_cas": request["host_cas_receipt_ref"], + "ingress_status": f"{root}/ingress/ingress_status.json", + "case_context": f"{root}/context/case_context.json", + "evidence_inventory": f"{root}/context/evidence_inventory.json", + "object_registry": f"{root}/context/object_registry.json", + "s2_10_status": f"{root}/map_s2_10/s2_10_publish_status.json", + "cluster_plan": f"{root}/context/cluster_plan.json", + "party_context": f"{root}/context/party_and_title_context.json", + "slot_crosswalk": f"{root}/context/slot_crosswalk.json", + "bundle_plan": f"{root}/context/bundle_plan.json", + "issue_ledger": f"{root}/review/issue_ledger.base.json", + "case_registry": f"{ASSET_ROOT}/manifest/case_type_registry.yml", + "rule_registry": f"{ASSET_ROOT}/manifest/case_type_rule_registry.yml", + "signature_projection": f"{ASSET_ROOT}/registry/binding/binding_signature_projection.yml", + "disposition_policy": f"{ASSET_ROOT}/registry/planning/option_disposition_policy.yml", + "party_rules": f"{ASSET_ROOT}/registry/party/party_set_rules.yml", + "relief_rules": f"{ASSET_ROOT}/registry/drafting/case_type_relief_rules.yml", + "review_policy": f"{ASSET_ROOT}/registry/review/review_policy_registry.yml", + "authority_registry": f"{ASSET_ROOT}/registry/authority/authority_registry.yml", + "corpus_scope": f"{ASSET_ROOT}/registry/corpus/requirement_fact_scope.yml", + "activation": f"{ASSET_ROOT}/registry/calculations/calculation_activation_registry.yml", + "general_law_values": f"{ASSET_ROOT}/law_values/general_law_values.yml", + "court_fee_values": f"{ASSET_ROOT}/law_values/court_fee_values.yml", + "corpus_release": f"{ASSET_ROOT}/manifest/corpus_release.json", + "schema_relief_plan": f"{ASSET_ROOT}/schemas/relief_plan.schema.json", + "schema_binding_retrieval": f"{ASSET_ROOT}/schemas/binding_retrieval.schema.json", + "schema_calculation": f"{ASSET_ROOT}/schemas/calculation.schema.json", + "schema_corpus_collection": f"{ASSET_ROOT}/corpus/weaviate_collection.schema.json", + "schema_ingress": f"{ASSET_ROOT}/schemas/ingress.schema.json", + "schema_context": f"{ASSET_ROOT}/schemas/context.schema.json", + "schema_s2_10": f"{ASSET_ROOT}/schemas/s2_10.schema.json", + "schema_review_status": f"{ASSET_ROOT}/schemas/review_status.schema.json", + "schema_deployment": f"{ASSET_ROOT}/schemas/deployment.schema.json", + "inheritance_reserved_share": f"{ASSET_ROOT}/registry/temporal/inheritance_reserved_share.yml", + "forbidden_relief_rules": f"{ASSET_ROOT}/registry/drafting/forbidden_relief_rules.yml", + "counter_performance_rules": f"{ASSET_ROOT}/registry/drafting/counter_performance_rules.yml", + "procedural_declaration_rules": f"{ASSET_ROOT}/registry/drafting/procedural_declaration_rules.yml", + "actio_route_registry": f"{ASSET_ROOT}/routing/actio_pauliana_route_registry.yml", + "actio_mortgage_route": f"{ASSET_ROOT}/routing/actio_pauliana_mortgage_route.yml", + "actio_value_invariants": f"{ASSET_ROOT}/validators/actio_value_compensation_invariants.yml", + "renderer_R01": f"{ASSET_ROOT}/renderers/R01_money_payment.yml", + "renderer_R02": f"{ASSET_ROOT}/renderers/R02_delivery_possession.yml", + "renderer_R03": f"{ASSET_ROOT}/renderers/R03_declaration_registry.yml", + "renderer_R04": f"{ASSET_ROOT}/renderers/R04_special_nonmoney_performance.yml", + "renderer_R05": f"{ASSET_ROOT}/renderers/R05_declaratory.yml", + "renderer_R06": f"{ASSET_ROOT}/renderers/R06_constitutive_judgment_challenge.yml", + } + seed, _ = read_stable(localdocs, fixed.values()) + if digest(seed[fixed["release"]]) != EXPECTED_STAGE2_RELEASE_SHA256 or digest(seed[fixed["release"]]) != request["expected_parent_stage2_release_sha256"]: + raise S220Error("PARENT_RELEASE_HASH_MISMATCH", "parent release bytes differ") + if digest(seed[fixed["s2_10_status"]]) != request["expected_s2_10_publish_status_sha256"]: + raise S220Error("S2_10_STATUS_HASH_MISMATCH", "S2_10 publish status differs") + if digest(seed[fixed["host_cas"]]) != request["host_cas_receipt_sha256"]: + raise S220Error("HOST_CAS_HASH_MISMATCH", "host CAS receipt differs") + seed_schema_documents = build_schema_documents(seed, fixed) + validate_artifact_payload( + canonical_bytes(request), + "schemas/deployment.schema.json#/$defs/s2_20_request", + REQUEST_PATH, + seed_schema_documents, + ) + release, child_release, _ = _verify_external_trust( + seed, fixed, request, seed_schema_documents + ) + publication_guard["external_trust_verified"] = True + release_without_digest = {key: value for key, value in release.items() if key != "release_digest"} + if release.get("release_digest") != digest(release_without_digest): + raise S220Error("PARENT_RELEASE_SELF_DIGEST", "parent release self digest differs") + module_manifest = _require_mapping( + load_json(seed[fixed["module_manifest"]], label=fixed["module_manifest"]), + code="MODULE_MANIFEST_SHAPE", + label="module manifest", + ) + module_ref = release.get("module_manifest_ref", {}) + if not isinstance(module_ref, dict) or module_ref.get("sha256") != digest(seed[fixed["module_manifest"]]): + raise S220Error("MODULE_MANIFEST_RELEASE_BINDING", "parent release does not bind the module manifest") + module_rows = module_manifest.get("modules", module_manifest.get("module_rows", [])) + by_module_path = { + str(row.get("path")): row + for row in module_rows + if isinstance(row, dict) and isinstance(row.get("path"), str) + } + release_bound_keys = ( + "case_registry", "rule_registry", "signature_projection", "disposition_policy", + "party_rules", "relief_rules", "review_policy", "authority_registry", + "corpus_scope", "activation", "authority_release", "corpus_release", "case_coverage", + "general_law_values", "court_fee_values", + "schema_relief_plan", "schema_binding_retrieval", "schema_calculation", "schema_corpus_collection", + "schema_ingress", "schema_context", "schema_s2_10", "schema_review_status", "schema_deployment", + "inheritance_reserved_share", "forbidden_relief_rules", "counter_performance_rules", + "procedural_declaration_rules", "actio_route_registry", "actio_mortgage_route", + "actio_value_invariants", + "renderer_R01", "renderer_R02", "renderer_R03", "renderer_R04", "renderer_R05", "renderer_R06", + ) + for key in release_bound_keys: + relative = fixed[key].removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(seed[fixed[key]]): + raise S220Error("MODULE_MEMBER_HASH_MISMATCH", f"module manifest does not bind: {relative}") + for key in ("schema_context", "schema_s2_10", "schema_review_status"): + relative = fixed[key].removeprefix(f"{ASSET_ROOT}/") + if _sealed_child_asset_hash(child_release, relative) != digest(seed[fixed[key]]): + raise S220Error( + "S2_10_CHILD_SCHEMA_HASH_MISMATCH", + f"S2_10 child release does not bind schema: {relative}", + ) + cluster_plan = load_json(seed[fixed["cluster_plan"]], label=fixed["cluster_plan"]) + _validate_loaded_value( + cluster_plan, + seed[fixed["cluster_plan"]], + "schemas/context.schema.json#/$defs/cluster_plan", + label=fixed["cluster_plan"], + schema_documents=seed_schema_documents, + ) + ingress_status = load_json(seed[fixed["ingress_status"]], label=fixed["ingress_status"]) + _validate_loaded_value( + ingress_status, + seed[fixed["ingress_status"]], + "schemas/ingress.schema.json#/$defs/ingress_status", + label=fixed["ingress_status"], + schema_documents=seed_schema_documents, + ) + bundle_plan = load_json(seed[fixed["bundle_plan"]], label=fixed["bundle_plan"]) + _validate_loaded_value( + bundle_plan, + seed[fixed["bundle_plan"]], + "schemas/context.schema.json#/$defs/bundle_plan", + label=fixed["bundle_plan"], + schema_documents=seed_schema_documents, + ) + status = load_json(seed[fixed["s2_10_status"]], label=fixed["s2_10_status"]) + if not isinstance(status, dict) or status.get("status") != "COMPLETE" or status.get("route") != "TO_S2_20": + raise S220Error("S2_10_BARRIER", "S2_10 must be COMPLETE/TO_S2_20") + cluster_ids = cluster_plan.get("executable_cluster_ids", []) if isinstance(cluster_plan, dict) else [] + if ( + not isinstance(cluster_ids, list) + or any(not isinstance(item, str) or CLUSTER_ID.fullmatch(item) is None for item in cluster_ids) + ): + raise S220Error("CLUSTER_PLAN_SHAPE", "executable_cluster_ids must be canonical cluster IDs") + dynamic_paths: list[str] = [] + seed_activation = _require_mapping(load_json(seed[fixed["activation"]], label=fixed["activation"]), code="ACTIVATION_REGISTRY_SHAPE", label="activation registry") + descriptor_paths: dict[str, str] = {} + for descriptor in seed_activation.get("calculator_descriptors", []): + if not isinstance(descriptor, dict) or not isinstance(descriptor.get("calculator_id"), str) or not isinstance(descriptor.get("descriptor_path"), str): + raise S220Error("CALCULATOR_DESCRIPTOR_SHAPE", "calculator descriptor ID/path required") + path = join_path(ASSET_ROOT, descriptor["descriptor_path"]) + descriptor_paths[descriptor["calculator_id"]] = path + dynamic_paths.append(path) + seed_rule_registry = _require_mapping( + load_json(seed[fixed["rule_registry"]], label=fixed["rule_registry"]), + code="RULE_REGISTRY_SHAPE", + label="rule registry", + ) + release_selected_legal_paths: set[str] = set() + for registry_key, rows_key in ( + ("case_registry", "rows"), + ("party_rules", "rules"), + ("corpus_scope", "rows"), + ): + registry_value = _require_mapping( + load_json(seed[fixed[registry_key]], label=fixed[registry_key]), + code="LEGAL_REGISTRY_SHAPE", + label=registry_key, + ) + for legal_row in registry_value.get(rows_key, []): + if isinstance(legal_row, dict) and isinstance(legal_row.get("legal_review_receipt_ref"), str): + legal_review_ref = safe_path(str(legal_row["legal_review_receipt_ref"])) + if LEGAL_REVIEW_PATH.fullmatch(legal_review_ref) is None: + raise S220Error( + "LEGAL_REVIEW_PATH_FAMILY", + f"legal review receipt is outside the closed family: {legal_review_ref}", + ) + release_selected_legal_paths.add( + join_path(ASSET_ROOT, legal_review_ref) + ) + for rule_row in seed_rule_registry.get("rows", []): + if not isinstance(rule_row, dict): + continue + for branch in rule_row.get("rule_branches", []): + if not isinstance(branch, dict): + continue + for branch_path_key in ("markdown_source_path", "legal_review_receipt_ref"): + if isinstance(branch.get(branch_path_key), str): + branch_ref = safe_path(str(branch[branch_path_key])) + path_family = RELIEF_RULE_PATH if branch_path_key == "markdown_source_path" else LEGAL_REVIEW_PATH + if path_family.fullmatch(branch_ref) is None: + raise S220Error( + "LEGAL_ASSET_PATH_FAMILY", + f"{branch_path_key} is outside the closed family: {branch_ref}", + ) + release_selected_legal_paths.add( + join_path(ASSET_ROOT, branch_ref) + ) + dynamic_paths.extend(sorted(release_selected_legal_paths)) + for cluster_id in sorted(cluster_ids): + dynamic_paths.extend( + [ + f"{root}/context/cluster_slices/{cluster_id}.json", + f"{root}/map_s2_10/domain_verdicts/{cluster_id}.json", + f"{root}/map_s2_10/item_receipts/{cluster_id}.json", + f"{root}/map_s2_10/issue_patches/{cluster_id}.json", + f"{root}/map_s2_10/assumption_parts/{cluster_id}.json", + f"{root}/map_s2_10/usage_parts/{cluster_id}.json", + ] + ) + bundle_wave_ordinals = sorted({ + int(member["dependency_wave_ordinal"]) + for cohort in bundle_plan.get("cohorts", []) + if isinstance(cohort, dict) and cohort.get("cohort_status") == "EXECUTABLE" + for member in cohort.get("member_slices", []) + if isinstance(member, dict) and isinstance(member.get("dependency_wave_ordinal"), int) + }) + if ( + not bundle_wave_ordinals + or bundle_wave_ordinals[-1] >= len(cluster_ids) + or bundle_wave_ordinals != list(range(bundle_wave_ordinals[-1] + 1)) + ): + raise S220Error("BUNDLE_WAVE_ORDINALS", "executable bundle wave ordinals must be contiguous from zero") + wave_paths = [ + f"{root}/map_s2_10/wave_receipts/wave-{ordinal:04d}.json" + for ordinal in bundle_wave_ordinals + ] + dynamic_paths.extend(wave_paths) + all_raw, snapshot_digest = read_stable(localdocs, list(fixed.values()) + dynamic_paths) + schema_documents = build_schema_documents(all_raw, fixed) + upstream = validate_upstream_handoff( + all_raw, + fixed, + request, + root, + cluster_ids, + wave_paths, + schema_documents, + child_release, + ) + activation = load_json(all_raw[fixed["activation"]], label=fixed["activation"]) + case_registry = _require_mapping(load_json(all_raw[fixed["case_registry"]], label=fixed["case_registry"]), code="CASE_REGISTRY_SHAPE", label="case registry") + rule_registry = _require_mapping(load_json(all_raw[fixed["rule_registry"]], label=fixed["rule_registry"]), code="RULE_REGISTRY_SHAPE", label="rule registry") + case_rows = case_registry.get("rows", case_registry.get("case_types", [])) + rule_rows = rule_registry.get("rows", rule_registry.get("case_type_rule_rows", [])) + if not isinstance(case_rows, list) or len(case_rows) != 137 or len({row.get("case_type_id") for row in case_rows if isinstance(row, dict)}) != 137: + raise S220Error("CASE_REGISTRY_137_CLOSURE", "case registry must contain exactly 137 unique case_type_id rows") + if not isinstance(rule_rows, list) or len(rule_rows) != 137 or {row.get("case_type_id") for row in rule_rows if isinstance(row, dict)} != {row.get("case_type_id") for row in case_rows if isinstance(row, dict)}: + raise S220Error("RULE_REGISTRY_137_CLOSURE", "rule registry must cover the exact case registry set") + if not isinstance(activation, dict) or len(activation.get("calculator_descriptors", [])) != 17: + raise S220Error("CALCULATOR_17_CLOSURE", "calculation registry must enumerate CE-01..13 and CE-R1..R4") + for path in descriptor_paths.values(): + relative = path.removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(all_raw[path]): + raise S220Error("CALCULATOR_MODULE_HASH_MISMATCH", f"calculator descriptor is not release-bound: {relative}") + for path in sorted(release_selected_legal_paths): + relative = path.removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(all_raw[path]): + raise S220Error( + "LEGAL_ASSET_MODULE_HASH_MISMATCH", + f"selected legal asset is not release-bound: {relative}", + ) + inputs = { + **upstream, + "issue_ledger_raw": all_raw[fixed["issue_ledger"]], + } + corpus_release = _require_mapping(load_json(all_raw[fixed["corpus_release"]], label=fixed["corpus_release"]), code="CORPUS_RELEASE_SHAPE", label="corpus release") + signature_projection_registry = _require_mapping( + load_json(all_raw[fixed["signature_projection"]], label=fixed["signature_projection"]), + code="SIGNATURE_PROJECTION_REGISTRY_SHAPE", + label="signature projection registry", + ) + disposition_policy = _require_mapping( + load_json(all_raw[fixed["disposition_policy"]], label=fixed["disposition_policy"]), + code="DISPOSITION_POLICY_SHAPE", + label="option disposition policy", + ) + party_rules = _require_mapping( + load_json(all_raw[fixed["party_rules"]], label=fixed["party_rules"]), + code="PARTY_RULES_SHAPE", + label="party-set rules", + ) + corpus_scope = _require_mapping( + load_json(all_raw[fixed["corpus_scope"]], label=fixed["corpus_scope"]), + code="CORPUS_SCOPE_SHAPE", + label="requirement-fact scope registry", + ) + special_asset_contracts = { + "inheritance_reserved_share": ( + "stage2.s2_20.inheritance_reserved_share_temporal.v1", + "registry_id", "S2-20-INHERITANCE-RESERVED-SHARE-TEMPORAL", "branch_rows", + ), + "forbidden_relief_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-FORBIDDEN-RELIEF-RULES", "rule_rows", + ), + "counter_performance_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-COUNTER-PERFORMANCE-RULES", "rule_rows", + ), + "procedural_declaration_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-PROCEDURAL-DECLARATION-RULES", "rule_rows", + ), + "actio_route_registry": ( + "stage2.s2_20.actio_pauliana_route_registry.v1", + "registry_id", "S2-20-ACTIO-PAULIANA-ROUTES", "route_rows", + ), + "actio_mortgage_route": ( + "stage2.s2_20.actio_pauliana_mortgage_route.v1", + "registry_id", "S2-20-ACTIO-MORTGAGE-ROUTES", "route_rows", + ), + "actio_value_invariants": ( + "stage2.s2_20.actio_value_compensation_invariants.v1", + "validator_id", "S2-20-ACTIO-VALUE-COMPENSATION-INVARIANTS", "invariant_rows", + ), + } + special_legal_assets = { + key: _validate_special_legal_asset( + load_json(all_raw[fixed[key]], label=fixed[key]), + schema_version=contract[0], + identity_field=contract[1], + identity=contract[2], + rows_field=contract[3], + label=fixed[key], + ) + for key, contract in special_asset_contracts.items() + } + assets = { + "case_registry": case_registry, + "rule_registry": rule_registry, + "activation_registry": activation, + "corpus_release": corpus_release, + "corpus_scope": corpus_scope, + "party_rules": party_rules, + "relief_rules": load_json(all_raw[fixed["relief_rules"]], label=fixed["relief_rules"]), + "review_policy": load_json(all_raw[fixed["review_policy"]], label=fixed["review_policy"]), + "signature_projection_registry": signature_projection_registry, + "disposition_policy": disposition_policy, + "special_legal_assets": special_legal_assets, + "party_rules_eligible": party_rules.get("registry_status") == "APPROVED", + "signature_projection_eligible": signature_projection_registry.get("registry_status") == "APPROVED", + "disposition_policy_eligible": disposition_policy.get("policy_status") == "APPROVED", + "authority_admitted": load_json(all_raw[fixed["authority_release"]], label=fixed["authority_release"]).get("status") == "ADMITTED", + "asset_hashes": { + "case_registry": digest(all_raw[fixed["case_registry"]]), + "rule_registry": digest(all_raw[fixed["rule_registry"]]), + "signature_projection": digest(all_raw[fixed["signature_projection"]]), + "corpus_release": digest(all_raw[fixed["corpus_release"]]), + "relief_rules": digest(all_raw[fixed["relief_rules"]]), + "party_rules": digest(all_raw[fixed["party_rules"]]), + "review_policy": digest(all_raw[fixed["review_policy"]]), + "corpus_scope": digest(all_raw[fixed["corpus_scope"]]), + **{ + key: digest(all_raw[fixed[key]]) + for key in sorted(special_legal_assets) + }, + }, + "calculator_descriptor_hashes": { + calculator_id: digest(all_raw[path]) for calculator_id, path in sorted(descriptor_paths.items()) + }, + "calculator_descriptors": { + calculator_id: load_json(all_raw[path], label=path) + for calculator_id, path in sorted(descriptor_paths.items()) + }, + "law_value_tables": [ + load_json(all_raw[fixed["general_law_values"]], label=fixed["general_law_values"]), + load_json(all_raw[fixed["court_fee_values"]], label=fixed["court_fee_values"]), + ], + "renderer_descriptors": { + renderer_id: load_json( + all_raw[fixed[f"renderer_{renderer_id}"]], + label=fixed[f"renderer_{renderer_id}"], + ) + for renderer_id in ("R01", "R02", "R03", "R04", "R05", "R06") + }, + "renderer_hashes": { + renderer_id: digest(all_raw[fixed[f"renderer_{renderer_id}"]]) + for renderer_id in ("R01", "R02", "R03", "R04", "R05", "R06") + }, + "release_selected_legal_bytes": { + path.removeprefix(f"{ASSET_ROOT}/"): all_raw[path] + for path in sorted(release_selected_legal_paths) + }, + "schema_documents": schema_documents, + } + inline_receipt = _require_mapping(load_json(all_raw[fixed["inline_receipt"]], label=fixed["inline_receipt"]), code="INLINE_RECEIPT_SHAPE", label="inline receipt") + canonical_code = inline_receipt.get("canonical_code", {}) + code_sha256 = canonical_code.get("code_sha256") if isinstance(canonical_code, dict) else None + assets["s2_20_code_sha256"] = require_hex(code_sha256, code="INLINE_CODE_HASH_INVALID") + return inputs, assets, snapshot_digest + + +def _artifact_ref(path: str, schema_ref: str, payload: bytes) -> dict[str, str]: + return {"path": path, "schema_ref": schema_ref, "sha256": digest(payload)} + + +def artifact_payloads( + result: Mapping[str, Any], request: Mapping[str, Any] +) -> dict[str, tuple[bytes, str, str]]: + plan = result["plan"] + groups = result["groups"] + claim_groups_base = { + "schema_version": "stage2_claim_groups.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_groups": groups, + "group_dependencies": plan["group_dependencies"], + } + claim_groups = {**claim_groups_base, "document_digest": digest(claim_groups_base)} + bindings = result["bindings"] + binding_base = { + "schema_version": "stage2_case_type_bindings.v1", + "run_binding_digest": request["run_binding_digest"], + "case_type_registry_sha256": result["asset_hashes"]["case_registry"], + "case_type_rule_registry_sha256": result["asset_hashes"]["rule_registry"], + "binding_signature_projection_sha256": result["asset_hashes"]["signature_projection"], + "bindings": bindings, + "bound_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if row["case_type_binding_status"] == "BOUND" and row["sub_rule_binding_status"] == "BOUND"), + "unresolved_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if "UNRESOLVED" in {row["case_type_binding_status"], row["sub_rule_binding_status"]}), + "conflict_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if "CONFLICT" in {row["case_type_binding_status"], row["sub_rule_binding_status"]}), + } + binding_document = {**binding_base, "document_digest": digest(binding_base)} + calculation_state_counts = { + state: sum(row["activation_status"] == state for row in result["calculations"]) + for state in ("ACTIVATED", "NOT_APPLICABLE", "ACTIVATION_GAP", "ACTIVATION_CONFLICT") + } + calculation_report_base = { + "schema_version": "stage2.calculation_report.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_ids": sorted({row["atomic_claim_id"] for row in result["calculations"]}), + "receipt_refs": sorted(f"calculation_receipts/{row['calculation_receipt_id']}.json" for row in result["calculations"]), + "receipt_sha256s": sorted(digest(canonical_bytes(row)) for row in result["calculations"]), + "state_counts": calculation_state_counts, + "missing_operand_ids": sorted({item for row in result["calculations"] for item in row["missing_operands"]}), + "issue_codes": sorted({item for row in result["calculations"] for item in row["issue_codes"]}), + } + calculation_report = {**calculation_report_base, "report_sha256": digest(calculation_report_base)} + filing_metrics_base = { + "schema_version": "stage2.filing_metrics.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": [], + "filing_decision_status": "REVIEW_REQUIRED", + "source_ce13_receipt_ids": [], + } + filing_metrics = {**filing_metrics_base, "report_sha256": digest(filing_metrics_base)} + payloads: dict[str, tuple[bytes, str, str]] = { + "canonical_relief_plan.json": (canonical_bytes(plan), "schemas/relief_plan.schema.json#/$defs/canonical_relief_plan", "C35"), + "claim_groups.json": (canonical_bytes(claim_groups), "schemas/relief_plan.schema.json#/$defs/claim_groups_document", "C30"), + "case_type_bindings.json": (canonical_bytes(binding_document), "schemas/binding_retrieval.schema.json#/$defs/case_type_bindings_document", "C26"), + "issue_ledger.plan.json": (canonical_bytes(result["issues"]), "schemas/relief_plan.schema.json#/$defs/issue_ledger_plan", "C30"), + "calculation_report.json": (canonical_bytes(calculation_report), "schemas/calculation.schema.json#/$defs/calculation_report", "C22"), + "filing_metrics.json": (canonical_bytes(filing_metrics), "schemas/calculation.schema.json#/$defs/filing_metrics", "C22"), + "exhibit_register.json": (canonical_bytes(result["exhibit_register"]), "schemas/relief_plan.schema.json#/$defs/exhibit_register", "C30"), + "option_conservation_report.json": (canonical_bytes(result["option_report"]), "schemas/relief_plan.schema.json#/$defs/option_conservation_report", "C20"), + "party_completeness_report.json": (canonical_bytes(result["party_report"]), "schemas/relief_plan.schema.json#/$defs/party_completeness_report", "C30"), + } + for receipt in result["calculations"]: + payloads[f"calculation_receipts/{receipt['calculation_receipt_id']}.json"] = (canonical_bytes(receipt), "schemas/calculation.schema.json#/$defs/calculation_receipt", "C22") + result_by_atomic = {row["atomic_claim_id"]: row for row in result["retrieval"]["results"]} + receipt_by_atomic = {row["atomic_claim_id"]: row for row in result["retrieval"]["receipts"]} + for group in groups: + group_id = group["claim_group_id"] + group_bindings = [row for row in bindings if row["atomic_claim_id"] in group["atomic_claim_ids"]] + group_rule_refs = [ + result["rule_asset_refs"].get(atomic_id, {}) + for atomic_id in group["atomic_claim_ids"] + ] + def merged_rule_refs(key: str) -> list[dict[str, str]]: + indexed = { + (str(ref["path"]), str(ref["sha256"]), str(ref["json_pointer"])): ref + for row in group_rule_refs + for ref in row.get(key, []) + if isinstance(ref, dict) + } + return [indexed[index] for index in sorted(indexed)] + rule_base = { + "schema_version": "stage2_rule_context_pack.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "binding_rows": group_bindings, + "structured_relief_rule_refs": merged_rule_refs("structured_relief_rule_refs"), + "markdown_source_refs": merged_rule_refs("markdown_source_refs"), + "renderer_refs": merged_rule_refs("renderer_refs"), + "party_set_rule_refs": merged_rule_refs("party_set_rule_refs"), + "ce13_branch_refs": merged_rule_refs("ce13_branch_refs"), + "review_policy_refs": merged_rule_refs("review_policy_refs"), + "authority_class": "RELIEF_ONLY", + } + rule_pack = {**rule_base, "pack_sha256": digest(rule_base)} + group_receipts = [receipt_by_atomic[item] for item in group["atomic_claim_ids"] if item in receipt_by_atomic] + fact_base = { + "schema_version": "stage2_requirement_fact_pack.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "atomic_claim_ids": group["atomic_claim_ids"], + "corpus_release_sha256": result["asset_hashes"]["corpus_release"], + "snapshot_id": str(result["corpus_release"].get("snapshot_id") or "UNAVAILABLE"), + "snapshot_sha256": str(result["corpus_release"].get("snapshot_sha256") or ("0" * 64)), + "retrieval_receipt_refs": [ + {"path": f"retrieval_receipts/{row['atomic_claim_id']}.json", "sha256": digest(canonical_bytes(row)), "json_pointer": ""} + for row in group_receipts + ], + "items": sorted( + [ + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["pack_items"].get(atomic_id, []) + ], + key=lambda row: (row["corpus_element_id"], row["chunk_id"]), + ), + "unmapped_chunk_ids": sorted( + { + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["unmapped_by_atomic"].get(atomic_id, []) + } + ), + "conflicting_chunk_ids": sorted( + { + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["conflicting_by_atomic"].get(atomic_id, []) + } + ), + "authority_class": "CAUSE_AND_ELEMENT_CHECK", + "issue_codes": sorted({code for item in group["atomic_claim_ids"] for code in result_by_atomic[item]["issue_codes"]}), + } + fact_pack = {**fact_base, "pack_sha256": digest(fact_base)} + gap_rows = [{ + "corpus_element_id": "UNRESOLVED", + "doctrine_role": "element", + "stage1_slot_refs": [], + "fact_refs": [], + "evidence_refs": [], + "coverage_status": "GAP", + "issue_refs": group["issue_refs"], + }] + gap_document = { + "schema_version": "stage2_element_evidence_gap.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "rows": gap_rows, + "gap_count": 1, + "conflict_count": 0, + } + rule_path = f"rule_context_packs/{group_id}.json" + fact_path = f"requirement_fact_packs/{group_id}.json" + gap_path = f"element_evidence_gap/{group_id}.json" + rule_raw = canonical_bytes(rule_pack) + fact_raw = canonical_bytes(fact_pack) + gap_raw = canonical_bytes(gap_document) + payloads[rule_path] = (rule_raw, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", "C26") + payloads[fact_path] = (fact_raw, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", "C29") + payloads[gap_path] = (gap_raw, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", "C30") + calculation_refs = [ + _artifact_ref( + f"calculation_receipts/{row['calculation_receipt_id']}.json", + "schemas/calculation.schema.json#/$defs/calculation_receipt", + canonical_bytes(row), + ) + for row in result["calculations"] if row["atomic_claim_id"] in group["atomic_claim_ids"] + ] + group_slice_base = { + "schema_version": "stage2_s2_20_group_slice.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "frozen_plan_sha256": digest(canonical_bytes(plan)), + "claim_group": group, + "rule_context_pack_ref": _artifact_ref(rule_path, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", rule_raw), + "requirement_fact_pack_ref": _artifact_ref(fact_path, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", fact_raw), + "element_evidence_gap_ref": _artifact_ref(gap_path, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", gap_raw), + "calculation_receipt_refs": calculation_refs, + "issue_refs": group["issue_refs"], + "drafting_permission": group["drafting_permission"], + } + group_slice = {**group_slice_base, "group_slice_sha256": digest(group_slice_base)} + group_path = f"group_slices/{group_id}.json" + group_raw = canonical_bytes(group_slice) + payloads[group_path] = (group_raw, "schemas/relief_plan.schema.json#/$defs/group_slice", "C35") + for segment_id, authority_class, refs in ( + ("P31_rule_and_pack", "RELIEF_ONLY_AND_CAUSE_CHECK", [ + _artifact_ref(rule_path, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", rule_raw), + _artifact_ref(fact_path, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", fact_raw), + _artifact_ref(gap_path, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", gap_raw), + ]), + ("P32_common_authority", "COMMON_AUTHORITY", []), + ): + segment_base = { + "schema_version": "stage2_s2_20_prompt_segment.v1", + "segment_id": segment_id, + "claim_group_id": group_id, + "authority_class": authority_class, + "ordered_asset_refs": refs, + "source_group_slice_sha256": digest(group_raw), + } + segment = {**segment_base, "segment_payload_sha256": digest(segment_base)} + payloads[f"prompt_segments/{group_id}/{segment_id}.json"] = (canonical_bytes(segment), "schemas/relief_plan.schema.json#/$defs/prompt_segment", "C35") + for row in result["retrieval"]["queries"]: + payloads[f"query_plans/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/query_plan", "C27") + for row in result["retrieval"]["results"]: + payloads[f"retrieval_results/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/retrieval_result", "C28") + for row in result["retrieval"]["receipts"]: + payloads[f"retrieval_receipts/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/retrieval_receipt", "C28") + return payloads + + +def build_schema_documents( + all_raw: Mapping[str, bytes], fixed: Mapping[str, str] +) -> dict[str, Mapping[str, Any]]: + """Load release-bound schemas and close their declared external $ref families. + + ``binding_retrieval.schema.json`` deliberately reuses the sealed + Weaviate collection schema. The inline validator has no filesystem + resolver, so this function merges the external definitions into a + private in-memory validation documents and rewrites only declared + relative references. Conflicting shared definitions fail closed. + """ + + relief = _require_mapping( + load_json(all_raw[fixed["schema_relief_plan"]], label=fixed["schema_relief_plan"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="relief plan schema", + ) + binding = _require_mapping( + load_json(all_raw[fixed["schema_binding_retrieval"]], label=fixed["schema_binding_retrieval"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="binding/retrieval schema", + ) + calculation = _require_mapping( + load_json(all_raw[fixed["schema_calculation"]], label=fixed["schema_calculation"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="calculation schema", + ) + corpus = _require_mapping( + load_json(all_raw[fixed["schema_corpus_collection"]], label=fixed["schema_corpus_collection"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="corpus collection schema", + ) + ingress = _require_mapping( + load_json(all_raw[fixed["schema_ingress"]], label=fixed["schema_ingress"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="ingress schema", + ) + context = _require_mapping( + load_json(all_raw[fixed["schema_context"]], label=fixed["schema_context"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="context schema", + ) + s2_10 = _require_mapping( + load_json(all_raw[fixed["schema_s2_10"]], label=fixed["schema_s2_10"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="S2_10 schema", + ) + review = _require_mapping( + load_json(all_raw[fixed["schema_review_status"]], label=fixed["schema_review_status"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="review/status schema", + ) + deployment = _require_mapping( + load_json(all_raw[fixed["schema_deployment"]], label=fixed["schema_deployment"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="deployment schema", + ) + merged_binding = load_json(canonical_bytes(binding), label="binding schema canonical copy") + merged_defs = merged_binding.get("$defs") + corpus_defs = corpus.get("$defs") + if not isinstance(merged_defs, dict) or not isinstance(corpus_defs, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", "binding and corpus schemas require $defs") + for name, definition in corpus_defs.items(): + if name in merged_defs and merged_defs[name] != definition: + raise S220Error( + "ARTIFACT_SCHEMA_EXTERNAL_DEF_CONFLICT", + f"shared schema definition differs: {name}", + ) + merged_defs.setdefault(name, definition) + + external_prefix = "../corpus/weaviate_collection.schema.json#" + + def rewrite_external_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith(external_prefix): + value["$ref"] = "#" + ref[len(external_prefix):] + for child in value.values(): + rewrite_external_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_external_refs(child) + + rewrite_external_refs(merged_binding) + + def merge_namespaced_external_defs( + target: Mapping[str, Any], + source: Mapping[str, Any], + *, + external_prefix: str, + namespace: str, + ) -> dict[str, Any]: + merged = load_json(canonical_bytes(target), label=f"{namespace} target schema copy") + source_copy = load_json(canonical_bytes(source), label=f"{namespace} source schema copy") + merged_defs = merged.get("$defs") + source_defs = source_copy.get("$defs") + if not isinstance(merged_defs, dict) or not isinstance(source_defs, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", "merged schemas require $defs") + + def rewrite_source_local_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith("#/$defs/"): + value["$ref"] = f"#/$defs/{namespace}__{ref[len('#/$defs/'):]}" + for child in value.values(): + rewrite_source_local_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_source_local_refs(child) + + for name, definition in source_defs.items(): + copied = load_json(canonical_bytes(definition), label=f"{namespace} definition copy") + rewrite_source_local_refs(copied) + target_name = f"{namespace}__{name}" + if target_name in merged_defs and merged_defs[target_name] != copied: + raise S220Error( + "ARTIFACT_SCHEMA_EXTERNAL_DEF_CONFLICT", + f"namespaced schema definition differs: {target_name}", + ) + merged_defs[target_name] = copied + + def rewrite_target_external_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith(external_prefix): + fragment = ref[len(external_prefix):] + if not fragment.startswith("#/$defs/"): + raise S220Error( + "ARTIFACT_SCHEMA_REF_UNSUPPORTED", + f"external schema reference is not a $defs pointer: {ref}", + ) + value["$ref"] = f"#/$defs/{namespace}__{fragment[len('#/$defs/'):]}" + for child in value.values(): + rewrite_target_external_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_target_external_refs(child) + + rewrite_target_external_refs(merged) + return merged + + merged_context = merge_namespaced_external_defs( + context, + review, + external_prefix="review_status.schema.json", + namespace="review_status", + ) + merged_review = merge_namespaced_external_defs( + review, + relief, + external_prefix="relief_plan.schema.json", + namespace="relief_plan", + ) + return { + "schemas/relief_plan.schema.json": relief, + "schemas/binding_retrieval.schema.json": merged_binding, + "schemas/calculation.schema.json": calculation, + "schemas/ingress.schema.json": ingress, + "schemas/context.schema.json": merged_context, + "schemas/s2_10.schema.json": s2_10, + "schemas/review_status.schema.json": merged_review, + "schemas/deployment.schema.json": deployment, + } + + +def _schema_pointer(document: Any, fragment: str) -> Any: + if fragment in {"", "#"}: + return document + pointer = fragment[1:] if fragment.startswith("#") else fragment + if not pointer.startswith("/"): + raise S220Error("ARTIFACT_SCHEMA_REF_UNSUPPORTED", f"non-pointer schema fragment: {fragment}") + current = document + for raw_token in pointer[1:].split("/"): + token = raw_token.replace("~1", "/").replace("~0", "~") + try: + current = current[int(token)] if isinstance(current, list) else current[token] + except (KeyError, IndexError, TypeError, ValueError) as exc: + raise S220Error("ARTIFACT_SCHEMA_REF_UNRESOLVED", f"schema pointer is unresolved: {fragment}") from exc + return current + + +def _schema_type_matches(instance: Any, expected: str) -> bool: + if expected == "null": + return instance is None + if expected == "boolean": + return isinstance(instance, bool) + if expected == "object": + return isinstance(instance, dict) + if expected == "array": + return isinstance(instance, list) + if expected == "string": + return isinstance(instance, str) + if expected == "integer": + return isinstance(instance, int) and not isinstance(instance, bool) + if expected == "number": + return isinstance(instance, (int, float)) and not isinstance(instance, bool) + raise S220Error("ARTIFACT_SCHEMA_KEYWORD_UNSUPPORTED", f"unsupported JSON Schema type: {expected}") + + +def _schema_identity(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def _schema_errors( + instance: Any, + schema: Any, + root_schema: Mapping[str, Any], + instance_path: str, + stack: set[tuple[int, int]], +) -> list[str]: + if schema is True: + return [] + if schema is False: + return [f"{instance_path}: boolean schema false"] + if not isinstance(schema, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"schema is not an object/boolean at {instance_path}") + marker = (id(schema), id(instance)) + if marker in stack: + return [] + nested_stack = set(stack) + nested_stack.add(marker) + errors: list[str] = [] + + if "$ref" in schema: + ref = schema["$ref"] + if not isinstance(ref, str) or not ref.startswith("#"): + raise S220Error("ARTIFACT_SCHEMA_REF_UNSUPPORTED", f"only local schema refs are allowed: {ref!r}") + errors.extend( + _schema_errors( + instance, + _schema_pointer(root_schema, ref), + root_schema, + instance_path, + nested_stack, + ) + ) + + for branch in schema.get("allOf", []): + errors.extend(_schema_errors(instance, branch, root_schema, instance_path, nested_stack)) + if "anyOf" in schema: + branches = [ + _schema_errors(instance, branch, root_schema, instance_path, nested_stack) + for branch in schema["anyOf"] + ] + if not any(not branch for branch in branches): + errors.append(f"{instance_path}: anyOf matched no branch") + if "oneOf" in schema: + branches = [ + _schema_errors(instance, branch, root_schema, instance_path, nested_stack) + for branch in schema["oneOf"] + ] + matched = sum(not branch for branch in branches) + if matched != 1: + errors.append(f"{instance_path}: oneOf expected one match, got {matched}") + if "not" in schema and not _schema_errors( + instance, schema["not"], root_schema, instance_path, nested_stack + ): + errors.append(f"{instance_path}: not subschema matched") + if "if" in schema: + condition_matches = not _schema_errors( + instance, schema["if"], root_schema, instance_path, nested_stack + ) + selected = "then" if condition_matches else "else" + if selected in schema: + errors.extend( + _schema_errors(instance, schema[selected], root_schema, instance_path, nested_stack) + ) + + expected_type = schema.get("type") + if expected_type is not None: + expected_types = [expected_type] if isinstance(expected_type, str) else expected_type + if not isinstance(expected_types, list) or not all(isinstance(item, str) for item in expected_types): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"invalid type declaration at {instance_path}") + if not any(_schema_type_matches(instance, item) for item in expected_types): + errors.append(f"{instance_path}: type mismatch; expected {expected_types}") + return errors + + if "const" in schema and instance != schema["const"]: + errors.append(f"{instance_path}: const mismatch") + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{instance_path}: value is outside enum") + + if isinstance(instance, str): + if "pattern" in schema: + try: + pattern_matches = re.search(str(schema["pattern"]), instance) is not None + except re.error as exc: + raise S220Error("ARTIFACT_SCHEMA_PATTERN_INVALID", f"invalid schema pattern at {instance_path}") from exc + if not pattern_matches: + errors.append(f"{instance_path}: pattern mismatch") + if len(instance) < int(schema.get("minLength", 0)): + errors.append(f"{instance_path}: shorter than minLength") + if schema.get("format") == "date": + try: + parsed_date = date.fromisoformat(instance) + except ValueError: + parsed_date = None + if parsed_date is None or parsed_date.isoformat() != instance: + errors.append(f"{instance_path}: invalid RFC 3339 full-date") + elif "format" in schema: + raise S220Error( + "ARTIFACT_SCHEMA_KEYWORD_UNSUPPORTED", + f"unsupported string format at {instance_path}: {schema['format']}", + ) + + if isinstance(instance, (int, float)) and not isinstance(instance, bool): + if "minimum" in schema and instance < schema["minimum"]: + errors.append(f"{instance_path}: below minimum") + if "maximum" in schema and instance > schema["maximum"]: + errors.append(f"{instance_path}: above maximum") + + if isinstance(instance, dict): + required = schema.get("required", []) + if not isinstance(required, list): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"required must be an array at {instance_path}") + for key in required: + if key not in instance: + errors.append(f"{instance_path}: missing required property {key!r}") + properties = schema.get("properties", {}) + if not isinstance(properties, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"properties must be an object at {instance_path}") + additional = schema.get("additionalProperties", True) + for key, value in instance.items(): + child_path = f"{instance_path}.{key}" + if key in properties: + errors.extend( + _schema_errors(value, properties[key], root_schema, child_path, nested_stack) + ) + elif additional is False: + errors.append(f"{child_path}: additional property is forbidden") + elif isinstance(additional, dict): + errors.extend( + _schema_errors(value, additional, root_schema, child_path, nested_stack) + ) + if len(instance) < int(schema.get("minProperties", 0)): + errors.append(f"{instance_path}: fewer than minProperties") + + if isinstance(instance, list): + if len(instance) < int(schema.get("minItems", 0)): + errors.append(f"{instance_path}: fewer than minItems") + if "maxItems" in schema and len(instance) > int(schema["maxItems"]): + errors.append(f"{instance_path}: more than maxItems") + if schema.get("uniqueItems") is True: + identities = [_schema_identity(item) for item in instance] + if len(identities) != len(set(identities)): + errors.append(f"{instance_path}: array items are not unique") + prefix_items = schema.get("prefixItems", []) + if not isinstance(prefix_items, list): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"prefixItems must be an array at {instance_path}") + for index, child_schema in enumerate(prefix_items[: len(instance)]): + errors.extend( + _schema_errors( + instance[index], child_schema, root_schema, f"{instance_path}[{index}]", nested_stack + ) + ) + if "items" in schema: + for index in range(len(prefix_items), len(instance)): + errors.extend( + _schema_errors( + instance[index], schema["items"], root_schema, f"{instance_path}[{index}]", nested_stack + ) + ) + if "contains" in schema: + matched = sum( + not _schema_errors(item, schema["contains"], root_schema, f"{instance_path}[{index}]", nested_stack) + for index, item in enumerate(instance) + ) + if matched < 1: + errors.append(f"{instance_path}: contains matched no item") + return errors + + +def validate_artifact_payload( + payload: bytes, + schema_ref: str, + path: str, + schema_documents: Mapping[str, Any], +) -> None: + value = load_json(payload, label=path) + schema_path, separator, fragment = schema_ref.partition("#") + root_schema = schema_documents.get(schema_path) + if not isinstance(root_schema, dict): + raise S220Error("ARTIFACT_SCHEMA_REF_UNRESOLVED", f"release-bound schema is unavailable: {schema_path}") + schema = _schema_pointer(root_schema, f"#{fragment}" if separator else "") + errors = _schema_errors(value, schema, root_schema, "$", set()) + if errors: + raise S220Error( + "ARTIFACT_SCHEMA_VALIDATION_FAILED", + f"artifact violates release-bound schema: {path}", + details=errors[:20], + ) + + +def publish(localdocs: McpClient, request: Mapping[str, Any], result: Mapping[str, Any]) -> dict[str, Any]: + root = request["plan_output_root_ref"] + payloads = artifact_payloads(result, request) + for relative, (payload, schema_ref, _) in sorted(payloads.items()): + validate_artifact_payload(payload, schema_ref, relative, result["schema_documents"]) + rows: list[dict[str, Any]] = [] + for relative, (payload, schema_ref, producer) in sorted(payloads.items()): + try: + localdocs.write_immutable(join_path(root, relative), payload) + except Exception as exc: + raise S220Error( + "PUBLISH_PARTIAL_WRITE", + f"artifact publication stopped before barrier: {relative}", + details={ + "written_paths": [row["path"] for row in rows], + "written_artifact_rows": [dict(row) for row in rows], + "failed_path": f"plan/{relative}", + "cause": str(exc), + }, + ) from exc + rows.append({"path": f"plan/{relative}", "schema_ref": schema_ref, "raw_sha256": digest(payload), "byte_size": len(payload), "producer_component": producer}) + groups = result["groups"] + barrier_without_hash = { + "schema_version": "stage2_plan_publish_status.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": result["plan"]["input_snapshot_digest"], + "parent_stage2_release_sha256": EXPECTED_STAGE2_RELEASE_SHA256, + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "s2_20_agent_sha256": request["expected_s2_20_agent_sha256"], + "s2_20_code_sha256": result["s2_20_code_sha256"], + "plan_status": "PLAN_COMPLETE_WITH_REVIEW" if any(row["drafting_permission"] != "CLEAN_DRAFT" for row in groups) else "PLAN_COMPLETE", + "route": "TO_S2_30", + "consumer_authorized": True, + "artifact_rows": rows, + "artifact_set_digest": digest(rows), + "group_ids": [row["claim_group_id"] for row in groups], + "clean_draft_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "CLEAN_DRAFT"], + "review_draft_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "REVIEW_DRAFT"], + "worknote_only_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "WORKNOTE_ONLY"], + "issue_summary": result["issue_summary"], + } + barrier = {**barrier_without_hash, "barrier_sha256": digest(barrier_without_hash)} + barrier_raw = canonical_bytes(barrier) + try: + localdocs.write_immutable(join_path(root, "plan_publish_status.json"), barrier_raw) + except Exception as exc: + raise S220Error( + "PUBLISH_PARTIAL_WRITE", + "all artifacts were written but the final consumer barrier failed", + details={ + "written_paths": [row["path"] for row in rows], + "written_artifact_rows": [dict(row) for row in rows], + "failed_path": "plan/plan_publish_status.json", + "cause": str(exc), + }, + ) from exc + return {"barrier_sha256": digest(barrier_raw), "artifact_set_digest": barrier["artifact_set_digest"], "route": barrier["route"], "plan_status": barrier["plan_status"]} + + +def publish_diagnostic(localdocs: McpClient, request: Mapping[str, Any], error: Mapping[str, Any]) -> dict[str, Any]: + root = request["plan_output_root_ref"] + details = error.get("details") if isinstance(error.get("details"), dict) else {} + partial_rows_raw = details.get("written_artifact_rows", []) if isinstance(details, dict) else [] + partial_rows: list[dict[str, Any]] = [] + if isinstance(partial_rows_raw, list): + for row in partial_rows_raw: + if not isinstance(row, dict) or set(row) != { + "path", "schema_ref", "raw_sha256", "byte_size", "producer_component" + }: + raise S220Error("DIAGNOSTIC_PARTIAL_ROW", "partial artifact row has an open shape") + safe_path(str(row["path"])) + require_hex(row["raw_sha256"], code="DIAGNOSTIC_PARTIAL_ROW_HASH") + if not isinstance(row["byte_size"], int) or isinstance(row["byte_size"], bool) or row["byte_size"] < 1: + raise S220Error("DIAGNOSTIC_PARTIAL_ROW_SIZE", "partial artifact byte size is invalid") + partial_rows.append(dict(row)) + source_refs = sorted(str(row["path"]) for row in partial_rows) + diagnostic_base = { + "schema_version": "stage2_s2_20_technical_diagnostic.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "attempt_id": request["attempt_id"], + "failure_component": "PUBLISH" if error.get("code") == "PUBLISH_PARTIAL_WRITE" else "PREFLIGHT", + "failure_code": str(error.get("code", "INLINE_RUNTIME_ERROR")), + "failure_scope": "RUN_WIDE", + "message": str(error.get("message", "S2_20 failed")), + "input_snapshot_digest": None, + "source_refs": source_refs, + "retryable": str(error.get("code", "")).startswith(("MCP_", "LOCALDOCS_")), + "operator_action_required": True, + } + diagnostic = {**diagnostic_base, "diagnostic_sha256": digest(diagnostic_base)} + diagnostic_raw = canonical_bytes(diagnostic) + localdocs.write_immutable(join_path(root, "technical_diagnostic.json"), diagnostic_raw) + rows = partial_rows + [{ + "path": "plan/technical_diagnostic.json", + "schema_ref": "schemas/relief_plan.schema.json#/$defs/technical_diagnostic", + "raw_sha256": digest(diagnostic_raw), + "byte_size": len(diagnostic_raw), + "producer_component": "S2_20_DIAGNOSTIC", + }] + rows = sorted(rows, key=lambda row: str(row["path"])) + barrier_base = { + "schema_version": "stage2_plan_publish_status.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": "0" * 64, + "parent_stage2_release_sha256": EXPECTED_STAGE2_RELEASE_SHA256, + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "s2_20_agent_sha256": request["expected_s2_20_agent_sha256"], + "s2_20_code_sha256": digest(b"UNAVAILABLE_BEFORE_PREFLIGHT"), + "plan_status": "TECHNICAL_INCOMPLETE", + "route": "STOP_TECHNICAL_INCOMPLETE", + "consumer_authorized": False, + "artifact_rows": rows, + "artifact_set_digest": digest(rows), + "group_ids": [], + "clean_draft_group_ids": [], + "review_draft_group_ids": [], + "worknote_only_group_ids": [], + "issue_summary": {"total": 1, "legal": 0, "binding": 0, "corpus": 0, "calculation": 0, "technical": 1}, + } + barrier = {**barrier_base, "barrier_sha256": digest(barrier_base)} + barrier_raw = canonical_bytes(barrier) + localdocs.write_immutable(join_path(root, "plan_publish_status.json"), barrier_raw) + return {"barrier_sha256": digest(barrier_raw), "artifact_set_digest": digest(rows), "route": "STOP_TECHNICAL_INCOMPLETE", "plan_status": "TECHNICAL_INCOMPLETE"} + + +def run() -> int: + localdocs: McpClient | None = None + request: dict[str, Any] | None = None + publication_guard = {"external_trust_verified": False} + receipt: dict[str, Any] + exit_code = 0 + try: + with contextlib.redirect_stdout(io.StringIO()): + if HEX64.fullmatch(INLINE_USER_HASH) is None or HEX64.fullmatch(INLINE_WORKSPACE_HASH) is None: + raise S220Error("INLINE_CONTEXT_UNBOUND", "AgentBackend user/workspace substitutions are required") + localdocs = McpClient(LOCALDOCS_URL, "localdocs") + localdocs.initialize() + request = validate_request(localdocs.read_binary(REQUEST_PATH)) + inputs, assets, snapshot_digest = hydrate(localdocs, request, publication_guard) + result = execute_core(inputs, assets, request, snapshot_digest) + publication = publish(localdocs, request, result) + receipt = { + "schema_version": "stage2_s2_20_execution_receipt.v1", + "ok": True, + "workflow_id": WORKFLOW_ID, + "request_id": request["request_id"], + "run_binding_digest": request["run_binding_digest"], + "plan_status": publication["plan_status"], + "route": publication["route"], + "artifact_set_digest": publication["artifact_set_digest"], + "plan_publish_status_path": "plan/plan_publish_status.json", + "plan_publish_status_sha256": publication["barrier_sha256"], + "error": None, + } + except Exception as exc: + error = exc.as_dict() if isinstance(exc, S220Error) else {"code": "INLINE_RUNTIME_ERROR", "message": str(exc)} + diagnostic_publication: dict[str, Any] | None = None + if ( + localdocs is not None + and request is not None + and publication_guard["external_trust_verified"] + ): + try: + diagnostic_publication = publish_diagnostic(localdocs, request, error) + except Exception as diagnostic_exc: + error = {**error, "diagnostic_publish_error": str(diagnostic_exc)} + receipt = { + "schema_version": "stage2_s2_20_execution_receipt.v1", + "ok": False, + "workflow_id": WORKFLOW_ID, + "request_id": request["request_id"] if request is not None else "UNBOUND", + "run_binding_digest": request["run_binding_digest"] if request is not None else "0" * 64, + "plan_status": diagnostic_publication["plan_status"] if diagnostic_publication is not None else "TECHNICAL_INCOMPLETE", + "route": diagnostic_publication["route"] if diagnostic_publication is not None else "STOP_TECHNICAL_INCOMPLETE", + "artifact_set_digest": diagnostic_publication["artifact_set_digest"] if diagnostic_publication is not None else None, + "plan_publish_status_path": "plan/plan_publish_status.json", + "plan_publish_status_sha256": diagnostic_publication["barrier_sha256"] if diagnostic_publication is not None else None, + "error": error, + } + exit_code = 2 + finally: + if localdocs is not None: + localdocs.close() + sys.stdout.buffer.write(canonical_bytes(receipt)) + return exit_code + + +if __name__ == "__main__": + raise SystemExit(run()) \ No newline at end of file diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.txt new file mode 100644 index 00000000..96a9c44e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.txt @@ -0,0 +1,3917 @@ +#!/usr/bin/env python3 +"""Release-bound deterministic S2_20 ReliefPlan executor. + +The code is self-contained. It does not call an LLM, import workspace +Python, execute shell commands, install packages at runtime, discover +files by directory scan, or mutate S2_10 legal judgments. +""" + +from __future__ import annotations + +import base64 +import binascii +from concurrent.futures import ThreadPoolExecutor +import contextlib +from datetime import date +from decimal import Decimal, InvalidOperation, ROUND_DOWN, ROUND_HALF_UP, ROUND_UP +import hashlib +import io +import itertools +import json +import re +import sys +import unicodedata +from pathlib import PurePosixPath +from typing import Any, Iterable, Mapping, Sequence + + +WORKFLOW_ID = "S2_20" +ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0" +EXPECTED_STAGE2_RELEASE_SHA256 = "70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92" +LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp" +WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp" +MCP_PROTOCOL_VERSION = "2025-03-26" +INLINE_USER_HASH = "{{__user_hash__}}" +INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}" +REQUEST_PATH = "stage2_control/s2_20_request.json" +ASSET_ROOT = "Default_Agent/Stage_2_Clean" +MAX_FILE_BYTES = 32 * 1024 * 1024 +MAX_TOTAL_BYTES = 256 * 1024 * 1024 +HEX64 = re.compile(r"^[a-f0-9]{64}$") +CLUSTER_ID = re.compile(r"^CL-[a-f0-9]{24}$") +IDENT = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$") +RELIEF_RULE_PATH = re.compile(r"^rules/relief/CT-[0-9]{3}\.md$") +LEGAL_REVIEW_PATH = re.compile(r"^legal_review/[A-Za-z0-9][A-Za-z0-9._-]{0,191}\.json$") +DISPOSITIONS = ("selected", "alternative", "excluded", "deferred") +TARGET_SIGNATURE_FIELDS = ( + "claim_family", "legal_effect", "performance_kind", "object_kind", + "party_role_pattern", "source_obligation_kind", "remedy_mode", + "registry_action", "procedural_posture", "special_statute_tags", +) + + +class S220Error(Exception): + def __init__(self, code: str, message: str, *, details: Any | None = None) -> None: + super().__init__(message) + self.code = code + self.message = message + self.details = details + + def as_dict(self) -> dict[str, Any]: + row: dict[str, Any] = {"code": self.code, "message": self.message} + if self.details is not None: + row["details"] = self.details + return row + + +def nfc(value: str) -> str: + return unicodedata.normalize("NFC", value) + + +def no_duplicates(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise S220Error("DUPLICATE_JSON_KEY", f"duplicate JSON key: {key}") + result[key] = value + return result + + +def load_json(raw: bytes, *, label: str) -> Any: + if len(raw) > MAX_FILE_BYTES: + raise S220Error("SOURCE_SIZE_LIMIT", f"file exceeds limit: {label}") + try: + text = raw.decode("utf-8", errors="strict") + value = json.loads( + text, + object_pairs_hook=no_duplicates, + parse_constant=lambda token: (_ for _ in ()).throw( + S220Error("NON_FINITE_NUMBER", f"non-finite number: {token}") + ), + ) + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise S220Error("JSON_PARSE_ERROR", f"strict JSON parse failed: {label}") from exc + return value + + +def canonical_bytes(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) + + "\n" + ).encode("utf-8") + + +def digest(value: Any) -> str: + payload = value if isinstance(value, bytes) else canonical_bytes(value) + return hashlib.sha256(payload).hexdigest() + + +def safe_path(value: Any, *, code: str = "PATH_INVALID") -> str: + if not isinstance(value, str) or not value or "\x00" in value: + raise S220Error(code, "path must be a non-empty string") + if value.startswith("/") or "\\" in value: + raise S220Error(code, "absolute or backslash path is forbidden") + normalized = PurePosixPath(value) + if any(part in {"", ".", ".."} for part in normalized.parts): + raise S220Error(code, "path traversal or ambiguous component") + return normalized.as_posix() + + +def join_path(*parts: str) -> str: + return safe_path("/".join(part.strip("/") for part in parts if part)) + + +def require_hex(value: Any, *, code: str) -> str: + if not isinstance(value, str) or HEX64.fullmatch(value) is None: + raise S220Error(code, "expected lower-case SHA-256") + return value + + +def stable_id(prefix: str, *parts: Any) -> str: + body = digest([nfc(str(part)) for part in parts])[:24] + return f"{prefix}-{body}" + + +def _parse_mcp_payload(raw: bytes, expected_id: int) -> Mapping[str, Any]: + content_type_json = raw.lstrip().startswith(b"{") + candidates: list[Any] = [] + if content_type_json: + candidates = [load_json(raw, label="mcp-response")] + else: + data_lines: list[str] = [] + events: list[bytes] = [] + for line in raw.decode("utf-8", errors="strict").splitlines(): + if not line: + if data_lines: + events.append("\n".join(data_lines).encode("utf-8")) + data_lines = [] + continue + if line.startswith((":", "event:", "id:", "retry:")): + continue + if not line.startswith("data:"): + raise S220Error("MCP_SSE_SHAPE", "unexpected SSE line") + data_lines.append(line[5:].lstrip()) + if data_lines: + events.append("\n".join(data_lines).encode("utf-8")) + candidates = [load_json(event, label="mcp-sse-event") for event in events] + matches = [row for row in candidates if isinstance(row, dict) and row.get("id") == expected_id] + if len(matches) != 1: + raise S220Error("MCP_RESPONSE_ID", "exactly one matching JSON-RPC result required") + row = matches[0] + if row.get("jsonrpc") != "2.0" or row.get("error") is not None: + raise S220Error("MCP_JSONRPC_ERROR", "MCP returned an invalid/error response") + if not isinstance(row.get("result"), dict): + raise S220Error("MCP_RESULT_SHAPE", "MCP result must be an object") + return row + + +def _tool_text(result: Mapping[str, Any], tool: str) -> str: + if result.get("isError") is True: + rendered = str(result.get("content", "")) + code = "LOCALDOCS_NOT_FOUND" if "not found" in rendered.lower() else "MCP_TOOL_ERROR" + raise S220Error(code, f"{tool} returned isError=true") + content = result.get("content") + if not isinstance(content, list) or len(content) != 1: + raise S220Error("MCP_CONTENT_CARDINALITY", "one content block required") + block = content[0] + if not isinstance(block, dict) or block.get("type") != "text" or not isinstance(block.get("text"), str): + raise S220Error("MCP_CONTENT_SHAPE", "one text block required") + return block["text"] + + +def _binary_from_text(text: str, path: str) -> bytes: + value = load_json(text.encode("utf-8"), label=path) + if isinstance(value, dict) and isinstance(value.get("results"), list): + rows = value["results"] + if len(rows) != 1 or not isinstance(rows[0], dict): + raise S220Error("LOCALDOCS_RESULT_CARDINALITY", "one binary result required") + value = rows[0].get("content", rows[0].get("text")) + if isinstance(value, str): + value = load_json(value.encode("utf-8"), label=path) + if not isinstance(value, dict) or not isinstance(value.get("content_base64"), str): + raise S220Error("LOCALDOCS_BINARY_ENVELOPE", "content_base64 is required") + try: + raw = base64.b64decode(value["content_base64"].encode("ascii"), validate=True) + except (UnicodeEncodeError, binascii.Error, ValueError) as exc: + raise S220Error("LOCALDOCS_BASE64", "strict base64 required") from exc + if value.get("byte_length", value.get("size", len(raw))) != len(raw): + raise S220Error("LOCALDOCS_LENGTH", f"byte length mismatch: {path}") + if value.get("sha256", digest(raw)) != digest(raw): + raise S220Error("LOCALDOCS_HASH", f"raw hash mismatch: {path}") + return raw + + +class McpClient: + def __init__(self, endpoint: str, name: str, *, client: Any | None = None) -> None: + try: + import httpx # type: ignore + except ImportError as exc: + raise S220Error("HTTPX_UNAVAILABLE", "httpx==0.28.1 is required") from exc + self.endpoint = endpoint + self.name = name + self.client = client or httpx.Client(timeout=60) + self.headers = {"Content-Type": "application/json", "Accept": "application/json, text/event-stream"} + self.ids = itertools.count(10) + self.initialized = False + + def close(self) -> None: + close = getattr(self.client, "close", None) + if callable(close): + close() + + def _post(self, body: Mapping[str, Any], expected_id: int | None) -> Mapping[str, Any] | None: + try: + response = self.client.post(self.endpoint, json=dict(body), headers=dict(self.headers)) + response.raise_for_status() + except Exception as exc: + raise S220Error("MCP_TRANSPORT", f"{self.name} transport failed") from exc + session = response.headers.get("mcp-session-id") + if session: + self.headers["mcp-session-id"] = session + if expected_id is None: + return None + return _parse_mcp_payload(bytes(response.content), expected_id) + + def initialize(self) -> None: + row = self._post( + { + "jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": { + "protocolVersion": MCP_PROTOCOL_VERSION, + "capabilities": {}, + "clientInfo": { + "name": "liti-s2-20-inline", "version": "1.0.0", + "user_id": INLINE_USER_HASH, "workspace_id": INLINE_WORKSPACE_HASH, + }, + }, + }, + 1, + ) + if row is None or row["result"].get("protocolVersion") != MCP_PROTOCOL_VERSION: + raise S220Error("MCP_PROTOCOL", "MCP protocol negotiation failed") + self._post({"jsonrpc": "2.0", "method": "notifications/initialized"}, None) + self.initialized = True + + def call(self, tool: str, arguments: Mapping[str, Any]) -> Mapping[str, Any]: + if not self.initialized: + raise S220Error("MCP_NOT_INITIALIZED", "initialize before tools/call") + message_id = next(self.ids) + row = self._post( + {"jsonrpc": "2.0", "id": message_id, "method": "tools/call", "params": {"name": tool, "arguments": dict(arguments)}}, + message_id, + ) + if row is None: + raise S220Error("MCP_EMPTY", f"empty response: {tool}") + return row["result"] + + def read_binary(self, path: str) -> bytes: + safe = safe_path(path) + return _binary_from_text(_tool_text(self.call("read_binary_doc", {"doc_name": safe}), "read_binary_doc"), safe) + + def read_optional(self, path: str) -> bytes | None: + try: + return self.read_binary(path) + except S220Error as exc: + if exc.code == "LOCALDOCS_NOT_FOUND": + return None + raise + + def write_immutable(self, path: str, payload: bytes) -> str: + safe = safe_path(path) + existing = self.read_optional(safe) + if existing is not None: + if existing != payload: + raise S220Error("IMMUTABLE_CONFLICT", f"existing bytes differ: {safe}") + return digest(existing) + encoded = base64.b64encode(payload).decode("ascii") + _tool_text( + self.call("write_binary_file", {"path": safe, "content_base64": encoded, "overwrite": False}), + "write_binary_file", + ) + observed = self.read_binary(safe) + if observed != payload: + raise S220Error("WRITE_READBACK_MISMATCH", f"read-back differs: {safe}") + return digest(observed) + + +REQUEST_KEYS = { + "schema_version", "request_id", "attempt_id", "run_binding_digest", + "user_identity_hash", "workspace_identity_hash", "stage2_run_root_ref", + "plan_output_root_ref", "host_cas_receipt_ref", "host_cas_receipt_sha256", + "detached_admission_receipt_ref", "detached_admission_receipt_sha256", + "outer_execution_receipt_target_ref", "expected_s2_10_publish_status_sha256", + "expected_parent_stage2_release_sha256", "expected_s2_10_release_sha256", + "expected_s2_20_agent_sha256", "expected_s2_20_executor_binding_sha256", +} + + +def validate_request(raw: bytes) -> dict[str, Any]: + value = load_json(raw, label=REQUEST_PATH) + if not isinstance(value, dict) or set(value) != REQUEST_KEYS: + keys = set(value) if isinstance(value, dict) else set() + raise S220Error("REQUEST_CLOSED_SHAPE", "request keys differ", details={"missing": sorted(REQUEST_KEYS - keys), "extra": sorted(keys - REQUEST_KEYS)}) + if value["schema_version"] != "stage2_s2_20_request.v1": + raise S220Error("REQUEST_VERSION", "unsupported request schema") + for key in ("request_id", "attempt_id"): + if not isinstance(value[key], str) or IDENT.fullmatch(value[key]) is None: + raise S220Error("REQUEST_ID_INVALID", f"invalid {key}") + for key in ( + "run_binding_digest", "user_identity_hash", "workspace_identity_hash", + "host_cas_receipt_sha256", "expected_s2_10_publish_status_sha256", + "expected_parent_stage2_release_sha256", "expected_s2_10_release_sha256", + "expected_s2_20_agent_sha256", "expected_s2_20_executor_binding_sha256", + ): + require_hex(value[key], code=f"{key.upper()}_INVALID") + require_hex(value["detached_admission_receipt_sha256"], code="ADMISSION_HASH_INVALID") + run = value["run_binding_digest"] + expected_root = f"stage2_runs/by-binding/{run}" + if value["stage2_run_root_ref"] != expected_root or value["plan_output_root_ref"] != f"{expected_root}/plan": + raise S220Error("REQUEST_ROOT_BINDING", "request/output roots must derive from run binding") + if value["user_identity_hash"] != INLINE_USER_HASH or value["workspace_identity_hash"] != INLINE_WORKSPACE_HASH: + raise S220Error("WORKSPACE_IDENTITY_MISMATCH", "request identity differs from Agent substitution") + exact_paths = { + "host_cas_receipt_ref": f"stage2_control/host_cas/{run}/S2_20.json", + "detached_admission_receipt_ref": f"{ASSET_ROOT}/manifest/stage2_deterministic_admission_receipt.json", + "outer_execution_receipt_target_ref": f"{expected_root}/control/s2_20_outer_execution_receipt.json", + } + for key, expected in exact_paths.items(): + safe_path(value[key]) + if value[key] != expected: + raise S220Error("REQUEST_TRUST_ANCHOR_PATH", f"{key} must equal its canonical run-bound path") + return value + + +def read_stable(localdocs: McpClient, paths: Iterable[str]) -> tuple[dict[str, bytes], str]: + ordered = sorted(set(safe_path(path) for path in paths)) + first: dict[str, bytes] = {} + total = 0 + for path in ordered: + raw = localdocs.read_binary(path) + total += len(raw) + if total > MAX_TOTAL_BYTES: + raise S220Error("TOTAL_READ_LIMIT", "input hydration exceeds total limit") + first[path] = raw + second = {path: localdocs.read_binary(path) for path in ordered} + if any(first[path] != second[path] for path in ordered): + raise S220Error("INPUT_TOCTOU", "input changed between bounded reads") + rows = [[path, digest(first[path]), len(first[path])] for path in ordered] + return first, digest(rows) + + +def option_disposition( + option: Mapping[str, Any], policy: Mapping[str, Any] +) -> tuple[str, list[str]]: + decision = option.get("decision") + client = option.get("client_disposition") + relation = option.get("relation") + if policy.get("policy_status") == "APPROVED": + observed = { + "legal_decision": decision, + "client_instruction": "NO_SUE" if client == "DEFERRED_BY_CLIENT_INSTRUCTION" else "UNSPECIFIED", + "precondition_status": "UNRESOLVED" if option.get("precondition_refs") or relation == "precondition_candidate" else "NOT_APPLICABLE", + "compatibility_status": "INCOMPATIBLE" if option.get("incompatible_with") else "COMPATIBLE", + } + matches = [ + row for row in policy.get("rules", []) + if isinstance(row, dict) + and all(row.get(key) == value for key, value in observed.items()) + ] + if len(matches) != 1: + raise S220Error( + "DISPOSITION_POLICY_CONFLICT" if matches else "DISPOSITION_POLICY_GAP", + "approved option-disposition policy must match exactly one row", + ) + result = matches[0].get("result_disposition") + if result not in {"SELECTED", "ALTERNATIVE", "EXCLUDED", "DEFERRED"}: + raise S220Error("DISPOSITION_POLICY_GAP", "policy row cannot authorize a disposition") + return str(result).lower(), [str(matches[0].get("reason_code"))] + reason_codes = ["DISPOSITION_POLICY_NOT_ADMITTED"] + if decision == "EXCLUDED": + return "excluded", reason_codes + if client == "DEFERRED_BY_CLIENT_INSTRUCTION": + return "deferred", reason_codes + if relation in {"alternative", "precondition_candidate", "accessory"}: + return "alternative", reason_codes + if decision in {"SUPPORTED", "CONDITIONAL", "UNRESOLVED"}: + return "selected", reason_codes + raise S220Error("DISPOSITION_POLICY_GAP", "claim option has no closed disposition") + + +def project_signature( + source: Mapping[str, Any], + c20_projection: Mapping[str, Any], + c21_projection: Mapping[str, Any], + remedy_context: Mapping[str, Any], + projection_registry: Mapping[str, Any], +) -> dict[str, Any]: + """Closed S2_10+C20+C21+remedy projection used by C25. + + No label, free text, filename, retrieval result, or model output may + supplement these explicit typed inputs. A missing/ambiguous value + remains UNKNOWN and therefore cannot produce an exact case binding. + """ + expected_source_fields = { + "vocabulary_version", "legal_basis_family", "right_holder_refs", + "obligor_refs", "performance_kind", "object_refs", "legal_effect", + "source_occurrence_refs", + } + if set(source) != expected_source_fields: + raise S220Error( + "BINDING_SIGNATURE_SOURCE_SHAPE", + "normalized claim signature must use the closed S2_10 vocabulary", + details={ + "missing": sorted(expected_source_fields - set(source)), + "extra": sorted(set(source) - expected_source_fields), + }, + ) + if source.get("vocabulary_version") != "stage2.claim_signature_vocab.v1": + raise S220Error("BINDING_SIGNATURE_SOURCE_VERSION", "unsupported source vocabulary") + source_values: dict[str, Any] = dict(source) + source_values.update( + { + "c20_remedy_projection": remedy_context.get("remedy_mode"), + "c20_dependency_projection": c20_projection.get("dependency_kind"), + "c21_party_projection": c21_projection.get("party_role_pattern"), + "c21_object_projection": c21_projection.get("object_kind"), + } + ) + projected: dict[str, Any] = { + key: ([] if key == "special_statute_tags" else "UNKNOWN") + for key in TARGET_SIGNATURE_FIELDS + } + rules = projection_registry.get("projection_rules", []) + rule_ids: list[str] = [] + provenance: list[dict[str, Any]] = [] + if projection_registry.get("registry_status") == "APPROVED" and isinstance(rules, list): + by_target: dict[str, Mapping[str, Any]] = {} + for rule in rules: + if not isinstance(rule, dict) or rule.get("target_field") not in TARGET_SIGNATURE_FIELDS: + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_INVALID", "closed projection row required") + target_field = str(rule["target_field"]) + if target_field in by_target: + raise S220Error("BINDING_SIGNATURE_PROJECTION_MULTI_RULE", target_field) + by_target[target_field] = rule + if set(by_target) != set(TARGET_SIGNATURE_FIELDS): + raise S220Error("BINDING_SIGNATURE_PROJECTION_COVERAGE", "all target fields require one rule") + for target_field in TARGET_SIGNATURE_FIELDS: + rule = by_target[target_field] + rule_id = str(rule.get("projection_rule_id", "")) + source_fields = rule.get("source_fields", []) + allowed = rule.get("allowed_output_values", []) + operator = rule.get("derivation_operator") + if not rule_id or not isinstance(source_fields, list) or not isinstance(allowed, list): + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_INVALID", target_field) + if rule.get("implementation_status") != "IMPLEMENTED" or rule.get("semantic_review_status") != "APPROVED": + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_NOT_ADMITTED", target_field) + candidates: list[str] = [] + for field in source_fields: + value = source_values.get(str(field)) + values = value if isinstance(value, list) else [value] + candidates.extend(nfc(item) for item in values if isinstance(item, str) and item) + permitted = sorted({item for item in candidates if item in allowed}) + if operator in {"DIRECT_COPY", "CLOSED_LOOKUP"} and len(permitted) == 1: + projected[target_field] = permitted[0] + elif operator == "SET_UNION" and target_field == "special_statute_tags": + projected[target_field] = permitted + elif operator == "SET_INTERSECTION" and target_field == "special_statute_tags": + projected[target_field] = permitted if permitted and len(permitted) == len(set(candidates)) else [] + rule_ids.append(rule_id) + provenance.append( + { + "target_field": target_field, + "value": projected[target_field], + "source_paths": sorted(str(item) for item in source_fields), + "status": "DERIVED" if projected[target_field] not in ("UNKNOWN", []) else "UNKNOWN", + } + ) + if not rule_ids: + rule_ids = ["PENDING_BINDING_SIGNATURE_PROJECTION"] + provenance = [ + { + "target_field": target_field, + "value": projected[target_field], + "source_paths": [], + "status": "UNKNOWN", + } + for target_field in TARGET_SIGNATURE_FIELDS + ] + return { + "schema_version": "stage2.binding_target_signature.v1", + "source_vocabulary_version": source["vocabulary_version"], + "target_vocabulary_version": projection_registry.get("target_vocabulary_version", "stage2.case_type_signature_vocab.v1"), + "projection_rule_ids": sorted(rule_ids), + "source_signature_refs": sorted(str(item) for item in source.get("source_occurrence_refs", []) if isinstance(item, str)), + "target_signature": projected, + "target_signature_sha256": digest(projected), + "signature": projected, + "signature_sha256": digest(projected), + "provenance": provenance, + } + + +def eval_predicate(signature: Mapping[str, Any], predicate: Mapping[str, Any]) -> bool: + operator = predicate.get("operator") + field = predicate.get("field") + if operator not in {"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"} or field not in TARGET_SIGNATURE_FIELDS: + raise S220Error("PREDICATE_GRAMMAR", "unsupported predicate operator/field") + actual = signature.get(str(field), "UNKNOWN") + expected = predicate.get("value") + if operator == "EXISTS": + if not isinstance(expected, bool): + raise S220Error("PREDICATE_GRAMMAR", "EXISTS requires a boolean value") + return (actual not in (None, "UNKNOWN", [])) is expected + if operator == "EQ": + return actual == expected + if operator == "IN": + return actual in (expected if isinstance(expected, list) else []) + actual_set = set(actual if isinstance(actual, list) else [actual]) + expected_set = set(expected if isinstance(expected, list) else []) + return expected_set <= actual_set if operator == "ALL_OF" else actual_set.isdisjoint(expected_set) + + +def eval_closed_predicate( + signature: Mapping[str, Any], predicate: Mapping[str, Any] +) -> bool: + if predicate.get("legal_review_status") != "APPROVED": + return False + clauses = predicate.get("clauses") + if not isinstance(clauses, list) or not clauses or any(not isinstance(item, dict) for item in clauses): + raise S220Error("PREDICATE_GRAMMAR", "approved predicate requires non-empty clauses") + return all(eval_predicate(signature, item) for item in clauses) + + +def bind_case( + signature: Mapping[str, Any], registry: Mapping[str, Any] +) -> tuple[str | None, str, list[str], list[str]]: + rows = registry.get("rows", registry.get("case_types", [])) + matches: list[tuple[str, str]] = [] + for row in rows if isinstance(rows, list) else []: + if not isinstance(row, dict): + continue + if row.get("row_kind") not in {"CLAIM", "GENERIC"}: + continue + if row.get("claim_capable_disposition") != "CLAIM_CAPABLE" or row.get("legal_classification_status") != "APPROVED": + continue + predicates = row.get("case_type_predicates", []) + if ( + isinstance(predicates, list) + and len(predicates) == 1 + and isinstance(predicates[0], dict) + and eval_closed_predicate(signature, predicates[0]) + ): + matches.append((str(row.get("case_type_id")), str(predicates[0].get("predicate_id")))) + matches = sorted(set(matches)) + if len(matches) == 1: + return matches[0][0], "BOUND", [], [matches[0][1]] + if not matches: + return None, "UNRESOLVED", ["CASE_TYPE_BINDING_ZERO_MATCH"], [] + return None, "CONFLICT", ["CASE_TYPE_BINDING_MULTI_MATCH"], [] + + +def bind_rule( + case_type_id: str | None, + signature: Mapping[str, Any], + rule_registry: Mapping[str, Any], +) -> tuple[dict[str, Any] | None, list[str], list[str]]: + if case_type_id is None: + return None, ["SUB_RULE_BINDING_ZERO_MATCH"], [] + registry_rows = rule_registry.get("rows", rule_registry.get("case_type_rule_rows", rule_registry.get("rule_bindings", []))) + rows = [row for row in registry_rows if isinstance(row, dict) and row.get("case_type_id") == case_type_id] + branches: list[dict[str, Any]] = [] + for row in rows: + row_branches = row.get("rule_branches", row.get("sub_rule_bindings", [])) + for branch in row_branches if isinstance(row_branches, list) else []: + if not isinstance(branch, dict) or branch.get("legal_content_status") != "APPROVED": + continue + predicates = branch.get("sub_rule_predicates", []) + if ( + isinstance(predicates, list) + and len(predicates) == 1 + and isinstance(predicates[0], dict) + and eval_closed_predicate(signature, predicates[0]) + ): + branches.append(branch) + if len(branches) == 1: + branch = branches[0] + predicate_ids = [str(item.get("predicate_id")) for item in branch.get("sub_rule_predicates", []) if isinstance(item, dict) and item.get("predicate_id")] + return branch, [], sorted(predicate_ids or [str(branch.get("predicate_id", "UNKNOWN"))]) + if len(branches) > 1: + return None, ["SUB_RULE_BINDING_MULTI_MATCH"], [] + return None, ["RULE_BRANCH_MISSING"], [] + + +def _source_ref(path: str, sha256: str, pointer: str = "") -> dict[str, str]: + return {"path": path, "sha256": sha256, "json_pointer": pointer} + + +def validate_rule_foreign_keys( + case_type_id: str, + branch: Mapping[str, Any], + assets: Mapping[str, Any], +) -> tuple[list[str], dict[str, list[dict[str, str]]]]: + """C26 exact FK/hash/status closure for an otherwise matched branch.""" + codes: list[str] = [] + refs: dict[str, list[dict[str, str]]] = { + "structured_relief_rule_refs": [], + "markdown_source_refs": [], + "renderer_refs": [], + "party_set_rule_refs": [], + "ce13_branch_refs": [], + "review_policy_refs": [], + } + legal_bytes = assets.get("release_selected_legal_bytes", {}) + markdown_path = branch.get("markdown_source_path") + markdown_hash = branch.get("markdown_source_sha256") + if ( + not isinstance(markdown_path, str) + or not isinstance(markdown_hash, str) + or not isinstance(legal_bytes, dict) + or markdown_path not in legal_bytes + or digest(legal_bytes[markdown_path]) != markdown_hash + ): + codes.append("RELIEF_RULE_SOURCE_HASH_MISMATCH") + else: + refs["markdown_source_refs"].append(_source_ref(markdown_path, markdown_hash)) + receipt_path = branch.get("legal_review_receipt_ref") + receipt_hash = branch.get("legal_review_receipt_sha256") + if ( + not isinstance(receipt_path, str) + or not isinstance(receipt_hash, str) + or receipt_path not in legal_bytes + or digest(legal_bytes[receipt_path]) != receipt_hash + ): + codes.append("RULE_BRANCH_LEGAL_RECEIPT_HASH_MISMATCH") + + relief_rules = assets.get("relief_rules", {}) + relief_rows = relief_rules.get("rule_branches", []) if isinstance(relief_rules, dict) else [] + relief_matches = [ + row for row in relief_rows + if isinstance(row, dict) + and row.get("case_type_id") == case_type_id + and row.get("sub_rule_id") == branch.get("sub_rule_id") + and row.get("rule_branch_key", f"{case_type_id}::{row.get('sub_rule_id')}") == branch.get("rule_branch_key") + ] + if ( + not isinstance(relief_rules, dict) + or relief_rules.get("execution_eligible") is not True + or relief_rules.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(relief_matches) != 1 + ): + codes.append("STRUCTURED_RELIEF_RULE_FK_UNRESOLVED") + else: + refs["structured_relief_rule_refs"].append( + _source_ref( + "registry/drafting/case_type_relief_rules.yml", + str(assets["asset_hashes"]["relief_rules"]), + f"/rule_branches/{relief_rows.index(relief_matches[0])}", + ) + ) + + renderer_descriptors = assets.get("renderer_descriptors", {}) + renderer_hashes = assets.get("renderer_hashes", {}) + for renderer_id in branch.get("renderer_ids", []): + descriptor = renderer_descriptors.get(renderer_id) if isinstance(renderer_descriptors, dict) else None + renderer_hash = renderer_hashes.get(renderer_id) if isinstance(renderer_hashes, dict) else None + if ( + not isinstance(descriptor, dict) + or descriptor.get("renderer_id") != renderer_id + or descriptor.get("execution_eligible") is not True + or descriptor.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or not isinstance(renderer_hash, str) + ): + codes.append("RENDERER_FK_UNRESOLVED") + else: + renderer_paths = { + "R01": "renderers/R01_money_payment.yml", + "R02": "renderers/R02_delivery_possession.yml", + "R03": "renderers/R03_declaration_registry.yml", + "R04": "renderers/R04_special_nonmoney_performance.yml", + "R05": "renderers/R05_declaratory.yml", + "R06": "renderers/R06_constitutive_judgment_challenge.yml", + } + refs["renderer_refs"].append(_source_ref(renderer_paths[renderer_id], renderer_hash)) + + party_registry = assets.get("party_rules", {}) + party_rows = party_registry.get("rules", []) if isinstance(party_registry, dict) else [] + party_matches = [ + row for row in party_rows + if isinstance(row, dict) + and row.get("party_set_rule_id") == branch.get("party_set_rule_id") + and row.get("legal_content_status") == "APPROVED" + ] + if party_registry.get("registry_status") != "APPROVED" or len(party_matches) != 1: + codes.append("PARTY_SET_RULE_FK_UNRESOLVED") + else: + refs["party_set_rule_refs"].append( + _source_ref( + "registry/party/party_set_rules.yml", + str(assets["asset_hashes"]["party_rules"]), + f"/rules/{party_rows.index(party_matches[0])}", + ) + ) + + scope_registry = assets.get("corpus_scope", {}) + scope_rows = scope_registry.get("rows", []) if isinstance(scope_registry, dict) else [] + scope_matches = [ + row for row in scope_rows + if isinstance(row, dict) + and row.get("corpus_scope_id") == branch.get("corpus_scope_id") + and row.get("case_type_id") == case_type_id + and branch.get("sub_rule_id") in row.get("sub_rule_ids", []) + and row.get("element_set_id") == branch.get("element_set_id") + and row.get("approval_status") == "APPROVED" + ] + if scope_registry.get("registry_status") != "APPROVED" or len(scope_matches) != 1: + codes.append("CORPUS_SCOPE_FK_UNRESOLVED") + + ce13 = assets.get("calculator_descriptors", {}).get("CE-13") + ce13_rows = ce13.get("formula_rows", []) if isinstance(ce13, dict) else [] + ce13_matches = [ + row for row in ce13_rows + if isinstance(row, dict) + and row.get("formula_id") == branch.get("ce13_branch_id") + ] + if ( + not isinstance(ce13, dict) + or ce13.get("execution_eligible") is not True + or ce13.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(ce13_matches) != 1 + ): + codes.append("CE13_BRANCH_FK_UNRESOLVED") + else: + refs["ce13_branch_refs"].append( + _source_ref( + "registry/calculations/CE-13_court_value_cost.yml", + str(assets["calculator_descriptor_hashes"]["CE-13"]), + f"/formula_rows/{ce13_rows.index(ce13_matches[0])}", + ) + ) + + review_registry = assets.get("review_policy", {}) + policy_rows = review_registry.get("policy_rows", []) if isinstance(review_registry, dict) else [] + policy_matches = [ + row for row in policy_rows + if isinstance(row, dict) and row.get("review_policy_id", row.get("policy_id")) == branch.get("review_policy_id") + ] + if ( + not isinstance(review_registry, dict) + or review_registry.get("execution_eligible") is not True + or review_registry.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(policy_matches) != 1 + ): + codes.append("REVIEW_POLICY_FK_UNRESOLVED") + else: + refs["review_policy_refs"].append( + _source_ref( + "registry/review/review_policy_registry.yml", + str(assets["asset_hashes"]["review_policy"]), + f"/policy_rows/{policy_rows.index(policy_matches[0])}", + ) + ) + return sorted(set(codes)), { + key: sorted(value, key=lambda row: (row["path"], row["json_pointer"])) + for key, value in refs.items() + } + + +def build_portfolio( + verdicts: Sequence[Mapping[str, Any]], + verdict_sources: Mapping[str, Mapping[str, str]], + disposition_policy: Mapping[str, Any], +) -> tuple[list[dict[str, Any]], dict[str, list[str]], list[dict[str, Any]], list[dict[str, Any]]]: + atomic: list[dict[str, Any]] = [] + partitions = {name: [] for name in DISPOSITIONS} + dispositions: list[dict[str, Any]] = [] + option_to_group: dict[str, str] = {} + relation_rows: list[tuple[str, str, str]] = [] + for verdict in verdicts: + cluster_id = str(verdict.get("cluster_id", "UNKNOWN")) + source = verdict_sources.get(cluster_id, {}) + options = verdict.get("claim_options", []) + for option_index, option in enumerate(options if isinstance(options, list) else []): + if not isinstance(option, dict): + continue + option_id = str(option.get("claim_option_id", "")) + if not option_id: + raise S220Error("CLAIM_OPTION_ID_MISSING", "canonical option ID is required") + disposition, disposition_reason_codes = option_disposition(option, disposition_policy) + partitions[disposition].append(option_id) + active = disposition in {"selected", "alternative"} + atomic_id = stable_id("AC", option_id) if active else None + group_id = stable_id("CG", option.get("same_underlying_recovery_key") or option_id) if active else None + if active and atomic_id is not None and group_id is not None: + option_to_group[option_id] = group_id + signature = option.get("normalized_claim_signature", {}) + source_path = source.get("path", f"map_s2_10/domain_verdicts/{cluster_id}.json") + source_sha = source.get("sha256", digest(verdict)) + atomic.append( + { + "atomic_claim_id": atomic_id, + "claim_option_id": option_id, + "claim_group_id": group_id, + "cluster_id": cluster_id, + "disposition": disposition.upper(), + "legal_decision": option.get("decision"), + "relation": option.get("relation"), + "client_disposition": option.get("client_disposition"), + "client_instruction_refs": sorted(str(item) for item in option.get("client_instruction_refs", []) if isinstance(item, str)), + "right_holder_refs": sorted(str(item) for item in signature.get("right_holder_refs", []) if isinstance(item, str)), + "obligor_refs": sorted(str(item) for item in signature.get("obligor_refs", []) if isinstance(item, str)), + "object_refs": sorted(str(item) for item in signature.get("object_refs", []) if isinstance(item, str)), + "same_underlying_recovery_key": option.get("same_underlying_recovery_key"), + "incompatible_with": sorted(str(item) for item in option.get("incompatible_with", []) if isinstance(item, str)), + "precondition_refs": sorted(str(item) for item in option.get("precondition_refs", []) if isinstance(item, str)), + "element_statuses": option.get("element_statuses", []), + "defense_statuses": option.get("defense_statuses", []), + "remedy_candidates": option.get("remedy_candidates", []), + "normalized_claim_signature": signature, + "c20_projection": { + "dependency_kind": option.get("relation", "UNKNOWN"), + "incompatible_option_ids": sorted(str(item) for item in option.get("incompatible_with", []) if isinstance(item, str)), + "precondition_option_ids": sorted(str(item) for item in option.get("precondition_refs", []) if isinstance(item, str)), + }, + "normalized_signature_sha256": digest(signature), + "source_refs": [{ + "source_artifact_path": source_path, + "source_artifact_sha256": source_sha, + "json_pointer": f"/claim_options/{option_index}", + }], + "typed_provenance": sorted( + [item for item in option.get("typed_provenance", []) if isinstance(item, dict)], + key=digest, + ), + "source_option_sha256": digest(option), + } + ) + relation_rows.extend((option_id, target, "INCOMPATIBLE") for target in option.get("incompatible_with", []) if isinstance(target, str)) + relation_rows.extend((option_id, target, "PRECONDITION") for target in option.get("precondition_refs", []) if isinstance(target, str)) + dispositions.append( + { + "schema_version": "stage2_option_disposition.v1", + "claim_option_id": option_id, + "source_cluster_id": cluster_id, + "source_domain_verdict_sha256": source.get("sha256", digest(verdict)), + "legal_decision": option.get("decision"), + "client_instruction": "NO_SUE" if option.get("client_disposition") == "DEFERRED_BY_CLIENT_INSTRUCTION" else "UNSPECIFIED", + "disposition": disposition.upper(), + "reason_codes": sorted(set(disposition_reason_codes + [f"S2_10_{str(option.get('decision', 'UNKNOWN'))}", f"RELATION_{str(option.get('relation', 'UNKNOWN')).upper()}"])), + "atomic_claim_ids": [atomic_id] if atomic_id is not None else [], + "claim_group_id": group_id, + "issue_refs": [], + } + ) + for values in partitions.values(): + values.sort() + seen = [item for values in partitions.values() for item in values] + if len(seen) != len(set(seen)) or set(seen) != {row["claim_option_id"] for row in dispositions}: + raise S220Error("OPTION_CONSERVATION", "disposition partition is not exact") + dependencies: list[dict[str, Any]] = [] + for source_option, target_option, relation in sorted(set(relation_rows)): + source_group = option_to_group.get(source_option) + target_group = option_to_group.get(target_option) + if source_group is None or target_group is None or source_group == target_group: + continue + dependencies.append( + { + "source_group_id": source_group, + "target_group_id": target_group, + "relation": relation, + "source_option_relation_refs": [f"{source_option}->{target_option}"], + } + ) + return ( + sorted(atomic, key=lambda row: row["atomic_claim_id"]), + partitions, + sorted(dispositions, key=lambda row: row["claim_option_id"]), + sorted(dependencies, key=digest), + ) + + +def build_party_projection( + party_context: Mapping[str, Any], object_registry: Mapping[str, Any] +) -> dict[str, Any]: + """C21 facts-only projection; no required-party legal conclusion.""" + parties = party_context.get("parties", []) + party_refs = sorted( + str(row.get("party_identity", {}).get("party_context_id")) + for row in parties + if isinstance(row, dict) + and isinstance(row.get("party_identity"), dict) + and row["party_identity"].get("party_context_id") + ) + identity_issues = sorted( + str(row.get("party_identity", {}).get("party_context_id")) + for row in parties + if isinstance(row, dict) + and isinstance(row.get("party_identity"), dict) + and row["party_identity"].get("identity_disposition") == "IDENTITY_UNRESOLVED" + ) + objects = object_registry.get("objects", []) + object_refs = sorted( + str(row.get("object_context_id")) + for row in objects + if isinstance(row, dict) and row.get("object_context_id") + ) + object_identity_issues = sorted( + str(row.get("object_context_id")) + for row in objects + if isinstance(row, dict) + and row.get("object_context_id") + and row.get("identity_disposition") == "IDENTITY_UNRESOLVED" + ) + present_role_tokens = sorted( + { + nfc(str(row[key])) + for row in parties + if isinstance(row, dict) + for key in ("party_title", "defendant_role") + if isinstance(row.get(key), str) and row.get(key) + } + ) + return { + "schema_version": "stage2_party_projection.v1", + "party_refs": party_refs, + "object_refs": object_refs, + "title_chain_refs": [], + "identity_issue_refs": sorted(set(identity_issues + object_identity_issues)), + "present_role_tokens": present_role_tokens, + "legal_required_party_determination_made": False, + } + + +def claim_c21_projection( + claim: Mapping[str, Any], party_projection: Mapping[str, Any] +) -> dict[str, str]: + known_parties = set(str(item) for item in party_projection.get("party_refs", [])) + known_objects = set(str(item) for item in party_projection.get("object_refs", [])) + holders = set(str(item) for item in claim.get("right_holder_refs", [])) + obligors = set(str(item) for item in claim.get("obligor_refs", [])) + objects = set(str(item) for item in claim.get("object_refs", [])) + if holders and obligors and holders | obligors <= known_parties: + party_role_pattern = f"RH_{len(holders)}_OB_{len(obligors)}" + else: + party_role_pattern = "UNKNOWN" + if not objects: + object_kind = "NO_OBJECT_REF" + elif objects <= known_objects: + object_kind = "IDENTIFIED_OBJECT_SET" + else: + object_kind = "UNKNOWN" + return { + "party_role_pattern": party_role_pattern, + "object_kind": object_kind, + } + + +def calculate_all( + atomic: Sequence[Mapping[str, Any]], + bindings: Sequence[Mapping[str, Any]], + activation_registry: Mapping[str, Any], + descriptor_hashes: Mapping[str, str], + descriptor_objects: Mapping[str, Mapping[str, Any]], + law_value_tables: Sequence[Mapping[str, Any]], + request: Mapping[str, Any], +) -> list[dict[str, Any]]: + """C22 closed dispatcher and Decimal formula interpreter. + + The present distribution contains no admitted legal formula rows. + This implementation nevertheless closes the admitted branch: an exact + activation row and release-selected descriptor are required, every + absent operand/law value remains explicit, and no numeric fallback is + permitted. + """ + descriptors = activation_registry.get("calculator_descriptors", []) + calculator_ids = sorted( + str(row["calculator_id"]) + for row in descriptors + if isinstance(row, dict) and isinstance(row.get("calculator_id"), str) + ) + descriptor_refs = { + str(row["calculator_id"]): row + for row in descriptors + if isinstance(row, dict) and isinstance(row.get("calculator_id"), str) + } + activation_rows = activation_registry.get("activation_rows", []) + if not isinstance(activation_rows, list): + raise S220Error("CALCULATION_ACTIVATION_ROWS_INVALID", "activation_rows must be an array") + registry_ready = ( + activation_registry.get("execution_eligible") is True + and activation_registry.get("status") == "LEGAL_CONTENT_ADMITTED" + ) + binding_by_atomic = { + str(row["atomic_claim_id"]): row + for row in bindings + if isinstance(row, dict) and isinstance(row.get("atomic_claim_id"), str) + } + law_values: dict[str, Mapping[str, Any]] = {} + for table in law_value_tables: + if not isinstance(table, Mapping) or table.get("release_eligible") is not True: + continue + for token in table.get("tokens", []): + if not isinstance(token, Mapping) or token.get("review_status") != "VERIFIED": + continue + law_value_id = token.get("law_value_id") + if isinstance(law_value_id, str) and law_value_id: + if law_value_id in law_values: + raise S220Error("LAW_VALUE_DUPLICATE", f"duplicate law value: {law_value_id}") + law_values[law_value_id] = token + + rounding_modes = { + "ROUND_HALF_UP": ROUND_HALF_UP, + "ROUND_DOWN": ROUND_DOWN, + "ROUND_UP": ROUND_UP, + "TRUNCATE": ROUND_DOWN, + } + + def decimal_value(value: Any) -> Decimal: + try: + return Decimal(str(value)) + except (InvalidOperation, ValueError) as exc: + raise S220Error("INVALID_DECIMAL_OPERAND", "operand is not an exact decimal") from exc + + def claim_metrics(claim: Mapping[str, Any]) -> list[str]: + values: set[str] = set() + for remedy in claim.get("remedy_candidates", []): + if not isinstance(remedy, Mapping): + continue + if isinstance(remedy.get("remedy_kind"), str): + values.add(str(remedy["remedy_kind"])) + values.update( + str(item) + for item in remedy.get("condition_codes", []) + if isinstance(item, str) and item + ) + return sorted(values) + + def row_matches( + activation_row: Mapping[str, Any], + claim: Mapping[str, Any], + binding: Mapping[str, Any], + requested_metrics: Sequence[str], + ) -> bool: + row_metrics = activation_row.get("requested_metrics", []) + equals = activation_row.get("equals", {}) + if not isinstance(row_metrics, list) or not isinstance(equals, Mapping): + raise S220Error("INVALID_ACTIVATION_ROW", "activation row shape is invalid") + if not set(str(item) for item in row_metrics) & set(requested_metrics): + return False + actual = { + "normalized_claim_signature": str(claim.get("normalized_signature_sha256", "")), + "case_type_id": str(binding.get("case_type_id", "")), + "rule_branch_key": str(binding.get("rule_branch_key", "")), + } + for key, expected in equals.items(): + if key == "requested_metric": + if str(expected) not in requested_metrics: + return False + elif key not in actual or actual[key] != str(expected): + return False + return True + + def empty_receipt( + claim: Mapping[str, Any], calculator_id: str, status: str, issues: Sequence[str] + ) -> dict[str, Any]: + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": stable_id("CR", claim["atomic_claim_id"], calculator_id), + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": claim["atomic_claim_id"], + "claim_group_id": claim["claim_group_id"], + "calculator_id": calculator_id, + "activation_status": status, + "calculation_status": status, + "activation_row_id": None, + "descriptor_sha256": descriptor_hashes.get(calculator_id), + "formula_id": None, + "formula_sha256": None, + "operand_rows": [], + "operand_refs": [], + "units": [], + "law_value_uses": [], + "effective_interval": None, + "rounding_rule": None, + "period_rows": [], + "missing_operands": [], + "result": None, + "result_sha256": None, + "issue_codes": sorted(set(str(item) for item in issues)), + } + return {**core, "receipt_sha256": digest(core)} + + def operand_row(operand_id: str, unit_id: str) -> dict[str, Any]: + return { + "operand_id": operand_id, + "source_ref": f"UNRESOLVED_STAGE1_CALCULATION_REQUEST:{operand_id}", + "status": "MISSING", + "value_type": "DECIMAL", + "value": None, + "unit_id": unit_id, + "currency": "KRW" if unit_id == "KRW" else None, + "value_sha256": None, + } + + receipts: list[dict[str, Any]] = [] + for row in sorted(atomic, key=lambda item: str(item["atomic_claim_id"])): + binding = binding_by_atomic.get(str(row["atomic_claim_id"]), {}) + requested_metrics = claim_metrics(row) + for calculator_id in calculator_ids: + if not registry_ready: + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_ACTIVATION_REGISTRY_NOT_ADMITTED"])) + continue + candidate_rows = [ + item for item in activation_rows + if isinstance(item, Mapping) and item.get("calculator_id") == calculator_id + ] + matches = [ + item for item in candidate_rows + if row_matches(item, row, binding, requested_metrics) + ] + if len(matches) > 1: + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_CONFLICT", ["CALCULATION_ACTIVATION_CONFLICT"])) + continue + if not matches: + relevant_metrics = { + str(metric) + for item in candidate_rows + for metric in item.get("requested_metrics", []) + if isinstance(metric, str) + } + if relevant_metrics & set(requested_metrics): + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_ACTIVATION_GAP"])) + else: + receipts.append(empty_receipt(row, calculator_id, "NOT_APPLICABLE", [])) + continue + activation_row = matches[0] + activation_row_id = activation_row.get("activation_row_id") + descriptor_ref = descriptor_refs.get(calculator_id, {}) + descriptor = descriptor_objects.get(calculator_id, {}) + if ( + not isinstance(activation_row_id, str) + or descriptor_ref.get("execution_eligible") is not True + or descriptor_ref.get("status") != "LEGAL_CONTENT_ADMITTED" + or descriptor.get("execution_eligible") is not True + or descriptor.get("status") != "LEGAL_CONTENT_ADMITTED" + or descriptor_ref.get("descriptor_sha256") != descriptor_hashes.get(calculator_id) + or descriptor.get("descriptor_sha256") != descriptor_hashes.get(calculator_id) + ): + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_DESCRIPTOR_NOT_ADMITTED_OR_HASH_MISMATCH"])) + continue + formulas = [item for item in descriptor.get("formula_rows", []) if isinstance(item, Mapping)] + formula_matches = [item for item in formulas if item.get("output_metric_id") in requested_metrics] + if len(formula_matches) != 1: + status = "ACTIVATION_CONFLICT" if len(formula_matches) > 1 else "ACTIVATION_GAP" + receipts.append(empty_receipt(row, calculator_id, status, ["CALCULATION_FORMULA_SELECTION_NOT_EXACT"])) + continue + formula = formula_matches[0] + required_operands = sorted(str(item) for item in formula.get("input_operand_ids", []) if isinstance(item, str)) + output_unit = str(formula.get("output_unit_id", "")) + operand_rows = [operand_row(item, output_unit) for item in required_operands] + missing_operands = list(required_operands) + law_value_uses: list[dict[str, Any]] = [] + missing_law_values: list[str] = [] + for law_value_id in formula.get("law_value_ids", []): + source = law_values.get(str(law_value_id)) + if not isinstance(source, Mapping): + missing_law_values.append(str(law_value_id)) + continue + law_value_uses.append( + { + "law_value_id": str(law_value_id), + "authority_id": str(source["authority_id"]), + "authority_release_sha256": require_hex(source["authority_release_sha256"], code="AUTHORITY_RELEASE_SHA256_INVALID"), + "law_value_release_sha256": require_hex(source["law_value_release_sha256"], code="LAW_VALUE_RELEASE_SHA256_INVALID"), + "effective_interval": source["effective_interval"], + "value": format(decimal_value(source["value"]), "f"), + "unit_id": str(source.get("unit_id", source.get("unit"))), + "approval_status": "APPROVED", + } + ) + calculation_status = "MISSING_OPERAND" if missing_operands else ("LAW_VALUE_GAP" if missing_law_values else "CALCULATED") + result: dict[str, Any] | None = None + result_sha256: str | None = None + if calculation_status == "CALCULATED": + # The current S2_00 handoff deliberately preserves only + # calculation-request references, not numeric values. This + # branch becomes reachable only after exact PRESENT operand + # rows are supplied by a compatible handoff revision. + values = [decimal_value(item["value"]) for item in operand_rows] + operator = formula.get("operator") + if operator == "ADD": + computed = sum(values, Decimal("0")) + elif operator == "SUBTRACT": + computed = values[0] - sum(values[1:], Decimal("0")) + elif operator == "MULTIPLY": + computed = Decimal("1") + for value in values: + computed *= value + elif operator == "DIVIDE": + if len(values) != 2 or values[1] == 0: + raise S220Error("CALCULATION_INVALID_DIVISION", "division requires two operands and non-zero divisor") + computed = values[0] / values[1] + elif operator == "MIN": + computed = min(values) + elif operator == "MAX": + computed = max(values) + else: + raise S220Error("CALCULATION_OPERATOR_UNSUPPORTED", "formula operator is outside the closed set") + rounding = formula.get("rounding_rule", {}) + if not isinstance(rounding, Mapping) or rounding.get("mode") not in rounding_modes: + raise S220Error("CALCULATION_ROUNDING_INVALID", "rounding rule is invalid") + rounded = computed.quantize(decimal_value(rounding["quantum"]), rounding=rounding_modes[str(rounding["mode"])]) + result = { + "metric_id": str(formula["output_metric_id"]), + "value": format(rounded, "f"), + "unit_id": output_unit, + "currency": "KRW" if output_unit == "KRW" else None, + } + result_sha256 = digest(result) + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": stable_id("CR", row["atomic_claim_id"], calculator_id), + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": row["atomic_claim_id"], + "claim_group_id": row["claim_group_id"], + "calculator_id": calculator_id, + "activation_status": "ACTIVATED", + "calculation_status": calculation_status, + "activation_row_id": activation_row_id, + "descriptor_sha256": descriptor_hashes[calculator_id], + "formula_id": str(formula["formula_id"]), + "formula_sha256": require_hex(formula["formula_sha256"], code="FORMULA_SHA256_INVALID"), + "operand_rows": operand_rows, + "operand_refs": required_operands, + "units": sorted({item["unit_id"] for item in operand_rows if item["unit_id"]}), + "law_value_uses": sorted(law_value_uses, key=lambda item: item["law_value_id"]), + "effective_interval": formula["effective_interval"], + "rounding_rule": formula["rounding_rule"], + "period_rows": [], + "missing_operands": missing_operands, + "result": result, + "result_sha256": result_sha256, + "issue_codes": sorted( + (["CALCULATION_OPERAND_MISSING"] if missing_operands else []) + + [f"CALCULATION_LAW_VALUE_UNRESOLVED:{item}" for item in missing_law_values] + ), + } + receipts.append({**core, "receipt_sha256": digest(core)}) + return sorted(receipts, key=lambda row: row["calculation_receipt_id"]) + + +def call_with_transient_retry( + client: McpClient, + tool: str, + arguments: Mapping[str, Any], + *, + max_attempts: int = 3, +) -> tuple[Mapping[str, Any], int]: + if not isinstance(max_attempts, int) or isinstance(max_attempts, bool) or not 1 <= max_attempts <= 3: + raise S220Error("RETRIEVAL_RETRY_POLICY_INVALID", "max_attempts must be an integer in 1..3") + for attempt_count in range(1, max_attempts + 1): + try: + return client.call(tool, arguments), attempt_count + except S220Error as exc: + if exc.code != "MCP_TRANSPORT" or attempt_count == max_attempts: + raise + raise S220Error("RETRIEVAL_RETRY_EXHAUSTED", "unreachable retry state") + + +def canonicalize_retrieval_hits( + candidates: Sequence[Mapping[str, Any]], +) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + ordered = sorted( + (dict(row) for row in candidates), + key=lambda row: ( + int(row.get("post_rerank_rank", 2**31 - 1)), + -float(row.get("reranker_score") or row.get("hybrid_score") or 0), + str(row.get("chunk_id", "")), + ), + ) + unique: dict[str, dict[str, Any]] = {} + dropped: list[dict[str, Any]] = [] + for candidate in ordered: + chunk_id = str(candidate.get("chunk_id", "")) + chunk_sha256 = str(candidate.get("chunk_sha256", "")) + if not chunk_id or HEX64.fullmatch(chunk_sha256) is None: + raise S220Error("RETRIEVAL_HIT_IDENTITY_INVALID", "canonical hit requires chunk ID and hash") + if chunk_id in unique: + dropped.append( + { + "chunk_id": chunk_id, + "chunk_sha256": chunk_sha256, + "drop_reason_code": "DUPLICATE_CHUNK", + } + ) + else: + unique[chunk_id] = candidate + return list(unique.values()), dropped + + +def build_retrieval_branch( + atomic: Sequence[Mapping[str, Any]], + bindings: Sequence[Mapping[str, Any]], + corpus_release: Mapping[str, Any], + corpus_scope: Mapping[str, Any], + request: Mapping[str, Any], +) -> dict[str, Any]: + """C27->C28->C29 closed branch; pending corpus is explicit, never widened.""" + by_atomic = {str(row["atomic_claim_id"]): row for row in bindings} + queries: list[dict[str, Any]] = [] + results: list[dict[str, Any]] = [] + receipts: list[dict[str, Any]] = [] + pack_items: dict[str, list[dict[str, Any]]] = {} + unmapped_by_atomic: dict[str, list[str]] = {} + conflicting_by_atomic: dict[str, list[str]] = {} + corpus_admitted = ( + corpus_release.get("status") == "ADMITTED" + and corpus_release.get("available") is True + and corpus_release.get("sealed") is True + and corpus_scope.get("registry_status") == "APPROVED" + and isinstance(corpus_scope.get("rows"), list) + and bool(corpus_scope.get("rows")) + ) + snapshot_id = str(corpus_release.get("snapshot_id") or "UNAVAILABLE") + snapshot_sha256 = str(corpus_release.get("snapshot_sha256") or ("0" * 64)) + scope_rows = [row for row in corpus_scope.get("rows", []) if isinstance(row, dict)] + ready_query_rows: list[dict[str, Any]] = [] + for claim in atomic: + atomic_id = str(claim["atomic_claim_id"]) + binding = by_atomic[atomic_id] + pack_items[atomic_id] = [] + unmapped_by_atomic[atomic_id] = [] + conflicting_by_atomic[atomic_id] = [] + binding_ready = ( + binding.get("case_type_binding_status") == "BOUND" + and binding.get("sub_rule_binding_status") == "BOUND" + ) + matched_scopes = [ + row for row in scope_rows + if row.get("corpus_scope_id") == binding.get("corpus_scope_id") + and row.get("case_type_id") == binding.get("case_type_id") + and binding.get("sub_rule_id") in row.get("sub_rule_ids", []) + and row.get("element_set_id") == binding.get("element_set_id") + and row.get("approval_status") == "APPROVED" + ] + query_rows: list[dict[str, Any]] = [] + if corpus_admitted and binding_ready and len(matched_scopes) == 1: + scope = matched_scopes[0] + crosswalk = [row for row in scope.get("slot_crosswalk_rows", []) if isinstance(row, dict)] + for doctrine_role in sorted({str(row.get("doctrine_role")) for row in crosswalk if row.get("doctrine_role")}): + expected_ids = sorted( + str(row["corpus_element_id"]) + for row in crosswalk + if row.get("doctrine_role") == doctrine_role and row.get("corpus_element_id") + ) + search_filter = { + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "corpus_scope_id": binding["corpus_scope_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "jurisdiction": "KR", + "snapshot_id": snapshot_id, + "approval_status": "APPROVED", + "effective_on": str(corpus_release.get("effective_on") or corpus_release.get("as_of") or ""), + } + if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", search_filter["effective_on"]): + raise S220Error("CORPUS_EFFECTIVE_DATE_UNBOUND", "admitted corpus requires effective_on") + query_projection = { + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "corpus_element_ids": expected_ids, + } + query_text = json.dumps(query_projection, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + row = { + "query_id": stable_id("Q", atomic_id, doctrine_role), + "atomic_claim_id": atomic_id, + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "corpus_scope_id": binding["corpus_scope_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "query_status": "READY", + "query_text": query_text, + "query_sha256": digest(query_text.encode("utf-8")), + "filter": search_filter, + "alpha": corpus_release.get("alpha"), + "top_k": corpus_release.get("top_k"), + "embedding_configuration_digest": corpus_release.get("embedding_configuration_digest"), + "reranker_configuration_digest": corpus_release.get("reranker_configuration_digest"), + "index_configuration_digest": corpus_release.get("index_configuration_digest"), + "expected_corpus_element_ids": expected_ids, + "issue_codes": [], + } + for key in ("embedding_configuration_digest", "reranker_configuration_digest", "index_configuration_digest"): + require_hex(row[key], code=f"CORPUS_{key.upper()}_UNBOUND") + if not isinstance(row["alpha"], (int, float)) or isinstance(row["alpha"], bool) or not 0 <= row["alpha"] <= 1: + raise S220Error("CORPUS_ALPHA_UNBOUND", "admitted corpus requires alpha in [0,1]") + if not isinstance(row["top_k"], int) or isinstance(row["top_k"], bool) or not 1 <= row["top_k"] <= 100: + raise S220Error("CORPUS_TOP_K_UNBOUND", "admitted corpus requires top_k 1..100") + query_rows.append(row) + ready_query_rows.append(row) + query_base = { + "schema_version": "stage2_requirement_fact_query_plan.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "rows": sorted(query_rows, key=lambda row: row["query_id"]), + } + query = {**query_base, "query_plan_sha256": digest(query_base)} + queries.append(query) + if not query_rows: + issue_codes = ["QUERY_NOT_RUN_UNRESOLVED_BINDING"] + if binding_ready and not corpus_admitted: + issue_codes.append("CORPUS_NOT_ADMITTED") + elif binding_ready and len(matched_scopes) != 1: + issue_codes.append("CORPUS_SCOPE_FK_UNRESOLVED") + result_base = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": query["query_plan_sha256"], + "snapshot_id": snapshot_id, + "snapshot_sha256": snapshot_sha256, + "result_status": "NOT_RUN_UNRESOLVED_BINDING", + "selected_hits": [], + "dropped_hits": [], + "issue_codes": sorted(set(issue_codes)), + } + results.append({**result_base, "result_sha256": digest(result_base)}) + + if ready_query_rows: + collection = corpus_release.get("collection") + tenant = corpus_release.get("tenant") + credential_ref = corpus_release.get("credential_ref") + if not all(isinstance(item, str) and item for item in (collection, tenant, credential_ref)): + raise S220Error("CORPUS_TRANSPORT_BINDING_UNBOUND", "collection, tenant and credential_ref are required") + weaviate = McpClient(WEAVIATE_MCP_URL, "weaviate") + try: + weaviate.initialize() + by_plan = {row["atomic_claim_id"]: row for row in queries} + selected_by_atomic: dict[str, list[dict[str, Any]]] = {} + dropped_by_atomic: dict[str, list[dict[str, Any]]] = {} + requests_by_atomic: dict[str, list[dict[str, Any]]] = {} + responses_by_atomic: dict[str, list[Any]] = {} + filters_by_atomic: dict[str, list[dict[str, Any]]] = {} + attempts_by_atomic: dict[str, list[int]] = {} + for query_row in sorted(ready_query_rows, key=lambda row: row["query_id"]): + atomic_id = str(query_row["atomic_claim_id"]) + arguments = { + "collection_name": collection, + "tenant": tenant, + "query": query_row["query_text"], + "filters": query_row["filter"], + "alpha": query_row["alpha"], + "limit": query_row["top_k"], + } + tool_result, attempt_count = call_with_transient_retry( + weaviate, "search_hybrid", arguments, max_attempts=3 + ) + text = _tool_text(tool_result, "search_hybrid") + response = load_json(text.encode("utf-8"), label="weaviate-search-response") + if isinstance(response, dict): + raw_hits = response.get("hits", response.get("objects", response.get("results"))) + else: + raw_hits = response + if not isinstance(raw_hits, list): + raise S220Error("WEAVIATE_RESPONSE_SHAPE", "search response must expose one hit array") + accepted: list[dict[str, Any]] = [] + dropped: list[dict[str, Any]] = [] + for rank, raw_hit in enumerate(raw_hits, 1): + if not isinstance(raw_hit, dict) or not isinstance(raw_hit.get("metadata"), dict): + raise S220Error("WEAVIATE_HIT_SHAPE", "hit and metadata objects are required") + metadata = raw_hit["metadata"] + chunk_id = str(raw_hit.get("chunk_id", metadata.get("chunk_id", ""))) + chunk_sha = str(raw_hit.get("chunk_sha256", metadata.get("chunk_sha256", ""))) + reason: str | None = None + if metadata.get("approval_status") != "APPROVED": + reason = "UNAPPROVED_CHUNK" + elif metadata.get("chunk_sha256") != chunk_sha or HEX64.fullmatch(chunk_sha) is None: + reason = "HASH_MISMATCH" + elif not ( + query_row["filter"]["case_type_id"] in metadata.get("case_type_ids", []) + and query_row["filter"]["sub_rule_id"] in metadata.get("sub_rule_ids", []) + and query_row["filter"]["rule_branch_key"] in metadata.get("rule_branch_keys", []) + and query_row["filter"]["corpus_scope_id"] in metadata.get("corpus_scope_ids", []) + and query_row["filter"]["element_set_id"] in metadata.get("element_set_ids", []) + and query_row["doctrine_role"] in metadata.get("doctrine_roles", []) + and metadata.get("jurisdiction") == "KR" + ): + reason = "FILTER_MISMATCH" + if reason: + dropped.append({"chunk_id": chunk_id or "UNBOUND", "chunk_sha256": chunk_sha if HEX64.fullmatch(chunk_sha) else "0" * 64, "drop_reason_code": reason}) + continue + accepted.append( + { + "chunk_id": chunk_id, + "chunk_sha256": chunk_sha, + "source_document_id": str(raw_hit.get("source_document_id", metadata.get("document_id"))), + "source_document_sha256": str(raw_hit.get("source_document_sha256", metadata.get("document_sha256"))), + "source_locator": str(raw_hit.get("source_locator", metadata.get("source_locator"))), + "pre_rerank_rank": int(raw_hit.get("pre_rerank_rank", rank)), + "hybrid_score": raw_hit.get("hybrid_score", raw_hit.get("score", 0)), + "post_rerank_rank": int(raw_hit.get("post_rerank_rank", rank)), + "reranker_score": raw_hit.get("reranker_score"), + "metadata": metadata, + } + ) + selected_by_atomic.setdefault(atomic_id, []).extend(accepted) + dropped_by_atomic.setdefault(atomic_id, []).extend(dropped) + requests_by_atomic.setdefault(atomic_id, []).append(arguments) + responses_by_atomic.setdefault(atomic_id, []).append(response) + filters_by_atomic.setdefault(atomic_id, []).append(query_row["filter"]) + attempts_by_atomic.setdefault(atomic_id, []).append(attempt_count) + for atomic_id in sorted(requests_by_atomic): + plan = by_plan[atomic_id] + hits, duplicate_drops = canonicalize_retrieval_hits( + selected_by_atomic.get(atomic_id, []) + ) + dropped_by_atomic.setdefault(atomic_id, []).extend(duplicate_drops) + dropped = sorted(dropped_by_atomic.get(atomic_id, []), key=lambda row: (row["chunk_id"], row["drop_reason_code"])) + result_base = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": plan["query_plan_sha256"], + "snapshot_id": snapshot_id, + "snapshot_sha256": snapshot_sha256, + "result_status": "COMPLETE" if hits else "NO_HIT", + "selected_hits": hits, + "dropped_hits": dropped, + "issue_codes": [] if hits else ["RETRIEVAL_NO_HIT"], + } + results.append({**result_base, "result_sha256": digest(result_base)}) + receipt_base = { + "schema_version": "stage2_requirement_fact_retrieval_receipt.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": plan["query_plan_sha256"], + "request_sha256": digest(requests_by_atomic.get(atomic_id, [])), + "response_sha256": digest(responses_by_atomic.get(atomic_id, [])), + "transport": "MCP_OVER_HTTP_JSON_RPC", + "endpoint": WEAVIATE_MCP_URL, + "tool_name": "search_hybrid", + "collection_name": collection, + "tenant": tenant, + "snapshot_sha256": snapshot_sha256, + "filter_sha256": digest(filters_by_atomic.get(atomic_id, [])), + "configuration_digest": digest( + { + "collection_name": collection, + "tenant": tenant, + "query_count": len(requests_by_atomic.get(atomic_id, [])), + } + ), + "attempt_count": max(attempts_by_atomic.get(atomic_id, [1])), + "replay_status": "NOT_REQUESTED", + "read_only_verified": True, + "credential_ref": credential_ref, + "credential_material_persisted": False, + } + receipts.append({**receipt_base, "receipt_sha256": digest(receipt_base)}) + binding = by_atomic[atomic_id] + scope = next(row for row in scope_rows if row.get("corpus_scope_id") == binding.get("corpus_scope_id")) + crosswalk_by_element = { + str(row["corpus_element_id"]): row + for row in scope.get("slot_crosswalk_rows", []) + if isinstance(row, dict) and row.get("corpus_element_id") + } + for hit in hits: + matched_element = False + for element_id in sorted(str(item) for item in hit["metadata"].get("corpus_element_ids", [])): + crosswalk = crosswalk_by_element.get(element_id) + if crosswalk is None: + unmapped_by_atomic[atomic_id].append(hit["chunk_id"]) + continue + matched_element = True + if crosswalk.get("mapping_status") == "CONFLICT": + conflicting_by_atomic[atomic_id].append(hit["chunk_id"]) + pack_items[atomic_id].append( + { + "corpus_element_id": element_id, + "doctrine_role": crosswalk["doctrine_role"], + "chunk_id": hit["chunk_id"], + "chunk_sha256": hit["chunk_sha256"], + "source_document_id": hit["source_document_id"], + "source_document_sha256": hit["source_document_sha256"], + "source_locator": hit["source_locator"], + "authority_ids": sorted(str(item) for item in hit["metadata"].get("authority_ids", [])), + "proposition_ids": sorted(str(item) for item in hit["metadata"].get("proposition_ids", [])), + "stage1_slot_refs": sorted(str(item) for item in crosswalk.get("stage1_slot_refs", [])), + "mapping_status": crosswalk.get("mapping_status", "UNMAPPED"), + "fact_creation_allowed": False, + "legal_decision_override_allowed": False, + } + ) + if not matched_element: + unmapped_by_atomic[atomic_id].append(hit["chunk_id"]) + finally: + weaviate.close() + key = lambda row: str(row["atomic_claim_id"]) + return { + "queries": sorted(queries, key=key), + "results": sorted(results, key=key), + "receipts": sorted(receipts, key=lambda row: str(row["atomic_claim_id"])), + "pack_items": { + atomic_id: sorted(rows, key=lambda row: (row["corpus_element_id"], row["chunk_id"])) + for atomic_id, rows in sorted(pack_items.items()) + }, + "unmapped_by_atomic": {key: sorted(set(value)) for key, value in sorted(unmapped_by_atomic.items())}, + "conflicting_by_atomic": {key: sorted(set(value)) for key, value in sorted(conflicting_by_atomic.items())}, + } + + +def execute_core(inputs: Mapping[str, Any], assets: Mapping[str, Any], request: Mapping[str, Any], snapshot_digest: str) -> dict[str, Any]: + verdicts = inputs["verdicts"] + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="s2-20-c20-c21") as pool: + c20_future = pool.submit( + build_portfolio, + verdicts, + inputs["verdict_sources"], + assets["disposition_policy"], + ) + c21_future = pool.submit( + build_party_projection, + inputs["party_context"], + inputs.get("object_registry", {}), + ) + atomic_work, partitions, disposition_rows, group_dependencies = c20_future.result() + party_projection = c21_future.result() + for row in atomic_work: + remedy_kinds = sorted( + { + str(item.get("remedy_kind")) + for item in row.get("remedy_candidates", []) + if isinstance(item, dict) and item.get("remedy_kind") + } + ) + row["signature_projection"] = project_signature( + row["normalized_claim_signature"], + row["c20_projection"], + claim_c21_projection(row, party_projection), + {"remedy_mode": remedy_kinds[0] if len(remedy_kinds) == 1 else "UNKNOWN"}, + assets["signature_projection_registry"], + ) + case_registry = assets["case_registry"] + rule_registry = assets["rule_registry"] + bindings: list[dict[str, Any]] = [] + rule_asset_refs: dict[str, dict[str, list[dict[str, str]]]] = {} + issues: list[dict[str, Any]] = [] + for row in atomic_work: + signature = row["signature_projection"]["target_signature"] + case_type_id, status, codes, case_predicate_ids = bind_case(signature, case_registry) + rule, rule_codes, rule_predicate_ids = bind_rule(case_type_id, signature, rule_registry) + fk_codes: list[str] = [] + refs = { + "structured_relief_rule_refs": [], "markdown_source_refs": [], + "renderer_refs": [], "party_set_rule_refs": [], + "ce13_branch_refs": [], "review_policy_refs": [], + } + if rule is not None and case_type_id is not None: + fk_codes, refs = validate_rule_foreign_keys(case_type_id, rule, assets) + all_codes = sorted(set(codes + rule_codes + fk_codes)) + sub_status = ( + "BOUND" + if rule is not None and not fk_codes + else ("CONFLICT" if any("MULTI" in code for code in rule_codes) else "UNRESOLVED") + ) + rule_asset_refs[row["atomic_claim_id"]] = refs + binding = { + "atomic_claim_id": row["atomic_claim_id"], + "target_signature": signature, + "target_signature_sha256": row["signature_projection"]["target_signature_sha256"], + "projection_rule_ids": row["signature_projection"]["projection_rule_ids"], + "case_type_binding_status": status, + "matched_case_type_predicate_ids": case_predicate_ids, + "case_type_id": case_type_id, + "sub_rule_binding_status": sub_status, + "matched_sub_rule_predicate_ids": rule_predicate_ids, + "sub_rule_id": rule.get("sub_rule_id") if rule else None, + "rule_branch_key": rule.get("rule_branch_key") if rule else None, + "renderer_ids": sorted(str(item) for item in (rule.get("renderer_ids", []) if rule else []) if isinstance(item, str)), + "corpus_scope_id": rule.get("corpus_scope_id") if rule else None, + "element_set_id": rule.get("element_set_id") if rule else None, + "party_set_rule_id": rule.get("party_set_rule_id") if rule else None, + "ce13_branch_id": rule.get("ce13_branch_id") if rule else None, + "legal_content_status": "APPROVED" if rule is not None and not fk_codes else ("PENDING_LEGAL_CLASSIFICATION" if status != "BOUND" else "PENDING_LEGAL_CONTENT"), + "binding_issue_codes": all_codes, + } + bindings.append(binding) + for code in all_codes: + issues.append({"issue_id": stable_id("ISS", row["atomic_claim_id"], code), "code": code, "scope_ref": row["atomic_claim_id"], "status": "OPEN"}) + + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="s2-20-c22-c27") as pool: + c22_future = pool.submit( + calculate_all, + atomic_work, + bindings, + assets["activation_registry"], + assets["calculator_descriptor_hashes"], + assets.get("calculator_descriptors", {}), + assets.get("law_value_tables", []), + request, + ) + c27_future = pool.submit( + build_retrieval_branch, + atomic_work, + bindings, + assets["corpus_release"], + assets["corpus_scope"], + request, + ) + calculation_receipts = c22_future.result() + retrieval = c27_future.result() + + groups: dict[str, dict[str, Any]] = {} + for row in atomic_work: + group = groups.setdefault( + row["claim_group_id"], + { + "schema_version": "stage2_claim_group.v1", + "claim_group_id": row["claim_group_id"], + "atomic_claim_ids": [], + "party_refs": [], + "object_refs": [], + "case_type_ids": [], + "rule_branch_keys": [], + "renderer_ids": [], + "party_set_rule_ids": [], + "calculation_receipt_ids": [], + "corpus_scope_ids": [], + "amount_slots": [], + "period_slots": [], + "counter_performance_refs": [], + "procedural_declaration_refs": [], + "dependencies": [], + "drafting_permission": "WORKNOTE_ONLY", + "issue_refs": [], + }, + ) + group["atomic_claim_ids"].append(row["atomic_claim_id"]) + bound_by_id = {row["atomic_claim_id"]: row for row in bindings} + calc_by_atomic: dict[str, list[dict[str, Any]]] = {} + for receipt in calculation_receipts: + calc_by_atomic.setdefault(str(receipt["atomic_claim_id"]), []).append(receipt) + retrieval_by_atomic = {row["atomic_claim_id"]: row for row in retrieval["results"]} + atom_work_by_id = {row["atomic_claim_id"]: row for row in atomic_work} + for group in groups.values(): + group["atomic_claim_ids"].sort() + group_bindings = [bound_by_id[item] for item in group["atomic_claim_ids"]] + group_calculations = [receipt for item in group["atomic_claim_ids"] for receipt in calc_by_atomic.get(item, [])] + group["party_refs"] = sorted({ref for item in group["atomic_claim_ids"] for ref in atom_work_by_id[item]["right_holder_refs"] + atom_work_by_id[item]["obligor_refs"]}) + group["object_refs"] = sorted({ref for item in group["atomic_claim_ids"] for ref in atom_work_by_id[item]["object_refs"]}) + group["case_type_ids"] = sorted({str(row["case_type_id"]) for row in group_bindings if row["case_type_id"]}) + group["rule_branch_keys"] = sorted({str(row["rule_branch_key"]) for row in group_bindings if row["rule_branch_key"]}) + group["renderer_ids"] = sorted({item for row in group_bindings for item in row["renderer_ids"]}) + group["party_set_rule_ids"] = sorted({str(row["party_set_rule_id"]) for row in group_bindings if row["party_set_rule_id"]}) + group["calculation_receipt_ids"] = sorted(str(row["calculation_receipt_id"]) for row in group_calculations) + group["corpus_scope_ids"] = sorted({str(row["corpus_scope_id"]) for row in group_bindings if row["corpus_scope_id"]}) + group["dependencies"] = [row for row in group_dependencies if row["source_group_id"] == group["claim_group_id"]] + permission_codes: list[str] = [] + if any(row["case_type_binding_status"] != "BOUND" or row["sub_rule_binding_status"] != "BOUND" for row in group_bindings): + permission_codes.append("BINDING_NOT_EXACT") + if any(row["activation_status"] in {"ACTIVATION_GAP", "ACTIVATION_CONFLICT"} for row in group_calculations): + permission_codes.append("CALCULATION_NOT_CLOSED") + if any(retrieval_by_atomic[item]["result_status"] != "COMPLETE" for item in group["atomic_claim_ids"]): + permission_codes.append("REQUIREMENT_FACT_PACK_NOT_ADMITTED") + if assets["party_rules_eligible"] is not True: + permission_codes.append("PARTY_SET_RULES_NOT_ADMITTED") + if assets["signature_projection_eligible"] is not True: + permission_codes.append("SIGNATURE_PROJECTION_NOT_ADMITTED") + if assets["disposition_policy_eligible"] is not True: + permission_codes.append("DISPOSITION_POLICY_NOT_ADMITTED") + if assets["authority_admitted"] is not True: + permission_codes.append("AUTHORITY_RELEASE_NOT_ADMITTED") + group_issue_refs: list[str] = [] + for code in sorted(set(permission_codes)): + issue_id = stable_id("ISS", group["claim_group_id"], code) + group_issue_refs.append(issue_id) + issues.append({"issue_id": issue_id, "code": code, "scope_ref": group["claim_group_id"], "status": "OPEN"}) + group["issue_refs"] = group_issue_refs + legal_decisions = {str(atom_work_by_id[item]["legal_decision"]) for item in group["atomic_claim_ids"]} + group["drafting_permission"] = ( + "CLEAN_DRAFT" + if not permission_codes + else ("WORKNOTE_ONLY" if "UNRESOLVED" in legal_decisions else "REVIEW_DRAFT") + ) + + group_rows = sorted(groups.values(), key=lambda row: row["claim_group_id"]) + permission_by_group = {row["claim_group_id"]: row["drafting_permission"] for row in group_rows} + issue_refs_by_scope: dict[str, list[str]] = {} + for issue in issues: + issue_refs_by_scope.setdefault(str(issue["scope_ref"]), []).append(str(issue["issue_id"])) + atomic_rows: list[dict[str, Any]] = [] + for row in atomic_work: + binding = bound_by_id[row["atomic_claim_id"]] + atomic_rows.append( + { + "schema_version": "stage2_atomic_claim.v1", + "atomic_claim_id": row["atomic_claim_id"], + "source_claim_option_ids": [row["claim_option_id"]], + "normalized_signature_sha256": row["normalized_signature_sha256"], + "right_holder_refs": row["right_holder_refs"], + "obligor_refs": row["obligor_refs"], + "object_refs": row["object_refs"], + "remedy_mode": str(row["signature_projection"]["target_signature"].get("remedy_mode") or "UNKNOWN"), + "case_type_binding_status": binding["case_type_binding_status"], + "case_type_id": binding["case_type_id"], + "sub_rule_binding_status": binding["sub_rule_binding_status"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "claim_group_id": row["claim_group_id"], + "drafting_permission": permission_by_group[row["claim_group_id"]], + "source_refs": row["source_refs"], + "issue_refs": sorted(issue_refs_by_scope.get(row["atomic_claim_id"], [])), + } + ) + + issue_base = inputs["issue_ledger"] + base_rows = issue_base.get("issues", []) if isinstance(issue_base, dict) else [] + issue_plan = { + "schema_version": "stage2_issue_ledger_plan.v1", + "base_ledger_sha256": digest(inputs["issue_ledger_raw"]), + "preserved_base_issue_ids": sorted(str(row.get("issue_id")) for row in base_rows if isinstance(row, dict) and row.get("issue_id")), + "plan_issues": sorted(issues, key=lambda row: row["issue_id"]), + } + option_report = { + "schema_version": "stage2_option_conservation_report.v1", + "run_binding_digest": request["run_binding_digest"], + "all_option_ids": sorted(row["claim_option_id"] for row in disposition_rows), + "selected_option_ids": partitions["selected"], + "alternative_option_ids": partitions["alternative"], + "excluded_option_ids": partitions["excluded"], + "deferred_option_ids": partitions["deferred"], + "partition_disjoint": True, + "partition_union_exact": True, + "silent_loss_count": 0, + "duplicate_recovery_issue_refs": [], + "status": "PASS", + } + party_rule_index = { + str(rule.get("party_set_rule_id")): rule + for rule in assets.get("party_rules", {}).get("rules", []) + if isinstance(rule, dict) and rule.get("party_set_rule_id") + } + party_rows: list[dict[str, Any]] = [] + present_role_tokens = set(str(item) for item in party_projection.get("present_role_tokens", [])) + for group in group_rows: + rule_id = group["party_set_rule_ids"][0] if len(group["party_set_rule_ids"]) == 1 else "UNBOUND" + rule = party_rule_index.get(rule_id) + required_roles = sorted( + str(item) for item in (rule.get("required_role_tokens", []) if isinstance(rule, dict) else []) + ) + missing_roles = sorted(set(required_roles) - present_role_tokens) + if rule_id == "UNBOUND" or not isinstance(rule, dict): + missing_roles = sorted(set(missing_roles + ["PARTY_SET_RULE_UNBOUND"])) + identity_conflicts = party_projection["identity_issue_refs"] + party_rows.append( + { + "claim_group_id": group["claim_group_id"], + "party_set_rule_id": rule_id, + "required_role_tokens": required_roles, + "present_party_refs": group["party_refs"], + "missing_role_tokens": missing_roles, + "identity_conflict_refs": identity_conflicts, + "status": "REVIEW_REQUIRED" if missing_roles or identity_conflicts else "COMPLETE", + "issue_refs": group["issue_refs"], + } + ) + party_report = { + "schema_version": "stage2_party_completeness_report.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": party_rows, + "report_status": "REVIEW_REQUIRED" if any(row["status"] != "COMPLETE" for row in party_rows) else "COMPLETE", + } + evidence_inventory = inputs.get("evidence_inventory", {}) + evidence_items = evidence_inventory.get("items", []) if isinstance(evidence_inventory, dict) else [] + if not isinstance(evidence_items, list) or any(not isinstance(item, dict) for item in evidence_items): + raise S220Error("EVIDENCE_INVENTORY_SHAPE", "evidence inventory items must be objects") + evidence_ids = [item.get("evidence_id") for item in evidence_items] + if any(not isinstance(item, str) or not item for item in evidence_ids): + raise S220Error("EVIDENCE_ID_REQUIRED", "every evidence inventory row requires evidence_id") + if len(evidence_ids) != len(set(evidence_ids)): + raise S220Error("EVIDENCE_ID_DUPLICATE", "evidence inventory contains duplicate evidence_id") + exhibit_rows = [ + { + "evidence_ref": evidence_id, + "source_key": evidence_id, + "existing_label": None, + "assigned_label": f"갑 제{ordinal}호증", + "assignment_status": "DETERMINISTICALLY_ASSIGNED", + } + for ordinal, evidence_id in enumerate(sorted(evidence_ids), 1) + ] + exhibit_base = { + "schema_version": "stage2_exhibit_register.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": exhibit_rows, + } + exhibit_register = {**exhibit_base, "register_digest": digest(exhibit_base)} + report_documents = { + "option_conservation_report.json": (option_report, "schemas/relief_plan.schema.json#/$defs/option_conservation_report"), + "party_completeness_report.json": (party_report, "schemas/relief_plan.schema.json#/$defs/party_completeness_report"), + "exhibit_register.json": (exhibit_register, "schemas/relief_plan.schema.json#/$defs/exhibit_register"), + } + issue_raw = canonical_bytes(issue_plan) + plan_base = { + "schema_version": "stage2_canonical_relief_plan.v1", + "producer_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": snapshot_digest, + "parent_stage2_release_sha256": request["expected_parent_stage2_release_sha256"], + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "option_dispositions": disposition_rows, + "atomic_claims": atomic_rows, + "claim_groups": group_rows, + "group_dependencies": group_dependencies, + "selected_option_ids": partitions["selected"], + "alternative_option_ids": partitions["alternative"], + "excluded_option_ids": partitions["excluded"], + "deferred_option_ids": partitions["deferred"], + "report_refs": [ + {"path": path, "schema_ref": schema_ref, "sha256": digest(canonical_bytes(document))} + for path, (document, schema_ref) in sorted(report_documents.items()) + ], + "issue_ledger_ref": { + "path": "issue_ledger.plan.json", + "schema_ref": "schemas/relief_plan.schema.json#/$defs/issue_ledger_plan", + "sha256": digest(issue_raw), + }, + } + plan = {**plan_base, "plan_content_digest": digest(plan_base)} + issue_codes = [str(row["code"]) for row in issue_plan["plan_issues"]] + issue_summary = { + "total": len(issue_codes), + "legal": sum("LEGAL" in code or "AUTHORITY" in code for code in issue_codes), + "binding": sum("BINDING" in code or "RULE" in code for code in issue_codes), + "corpus": sum("CORPUS" in code or "REQUIREMENT_FACT" in code for code in issue_codes), + "calculation": sum("CALCULATION" in code or code.startswith("CE_") for code in issue_codes), + "technical": 0, + } + return { + "plan": plan, + "groups": group_rows, + "atomic": atomic_rows, + "bindings": bindings, + "issues": issue_plan, + "calculations": calculation_receipts, + "retrieval": retrieval, + "rule_asset_refs": rule_asset_refs, + "party_projection": party_projection, + "partitions": partitions, + "option_report": option_report, + "party_report": party_report, + "exhibit_register": exhibit_register, + "asset_hashes": assets["asset_hashes"], + "corpus_release": assets["corpus_release"], + "s2_20_code_sha256": assets["s2_20_code_sha256"], + "schema_documents": assets["schema_documents"], + "issue_summary": issue_summary, + } + + +def _require_mapping(value: Any, *, code: str, label: str) -> Mapping[str, Any]: + if not isinstance(value, dict): + raise S220Error(code, f"object required: {label}") + return value + + +def _verify_binding_text(raw: bytes, request: Mapping[str, Any]) -> None: + try: + text = raw.decode("utf-8", errors="strict") + except UnicodeDecodeError as exc: + raise S220Error("EXECUTOR_BINDING_UTF8", "executor binding must be UTF-8") from exc + marker = "\n - stage_id: S2_20\n" + framed = "\n" + text + if framed.count(marker) != 1: + raise S220Error("EXECUTOR_BINDING_S2_20_ROW", "exactly one S2_20 binding row is required") + section = framed.split(marker, 1)[1] + section = section.split("\n - stage_id:", 1)[0] + section = section.split("\nlegacy_fallbacks:", 1)[0] + + # Parse the closed scalar mapping by indentation instead of treating + # untrusted YAML as a bag of substrings. Sequence values are checked + # separately below; aliases, tags, flow mappings and duplicate scalar + # paths are not accepted in the live trust row. + stack: list[tuple[int, str]] = [] + scalars: dict[Any, str] = {} + for raw_line in section.splitlines(): + if not raw_line.strip(): + continue + if "\t" in raw_line or raw_line.lstrip().startswith(("#", "&", "*", "!", "{", "[")): + raise S220Error("EXECUTOR_BINDING_YAML_PROFILE", "binding uses a forbidden YAML construct") + indent = len(raw_line) - len(raw_line.lstrip(" ")) + stripped = raw_line.strip() + if stripped.startswith("- "): + continue + match = re.fullmatch(r"([A-Za-z0-9_]+):(.*)", stripped) + if match is None: + raise S220Error("EXECUTOR_BINDING_YAML_PROFILE", f"unsupported binding line: {stripped}") + while stack and stack[-1][0] >= indent: + stack.pop() + key, tail = match.group(1), match.group(2).strip() + path = tuple(item[1] for item in stack) + (key,) + if tail: + if path in scalars: + raise S220Error("EXECUTOR_BINDING_DUPLICATE_KEY", ".".join(path)) + if len(tail) >= 2 and tail[0] == tail[-1] and tail[0] in {'"', "'"}: + tail = tail[1:-1] + scalars[path] = tail + else: + stack.append((indent, key)) + + required_values = { + ("binding_id",): "S2-BINDING-S2_20-CODE-EXECUTOR-V1", + ("workflow_id",): "S2_20", + ("execution_class",): "NON-LLM-DETERMINISTIC", + ("active_runtime_authority",): "true", + ("agent_script_ref", "path"): "agent_scripts/Stage_2_S2_20.yml", + ("agent_script_ref", "sha256"): request["expected_s2_20_agent_sha256"], + ("workflow_contract_ref", "path"): "workflows/S2_20_canonical_relief_plan_reduce.yml", + ("inline_code_receipt_ref", "path"): "manifest/s2_20_inline_code_receipt.json", + ("stage2_release_ref", "path"): "manifest/stage2_release.json", + ("stage2_release_ref", "sha256"): request["expected_parent_stage2_release_sha256"], + ("expected_release_sha256",): request["expected_parent_stage2_release_sha256"], + ("agent_script_sha256",): request["expected_s2_20_agent_sha256"], + ("mcp_server_id",): "code-executor", + ("tool_name",): "run_code", + ("language",): "python", + ("network",): "agent-network", + ("timeout_seconds",): "300", + ("runtime_image_status",): "PINNED_VERIFIED", + ("dependency_lock", "requirements"): "httpx==0.28.1", + ("dependency_lock", "lock_status"): "STATIC_PIN_BOUND", + ("localdocs_contract", "fixed_request_path"): "stage2_control/s2_20_request.json", + ("external_mcp_contract", "endpoint"): WEAVIATE_MCP_URL, + ("external_mcp_contract", "operation_mode"): "READ_ONLY_EXACT_FILTER", + ("external_mcp_contract", "admission_status"): "LIVE_VERIFIED", + ("egress_profile_status",): "LIVE_VERIFIED", + ("live_admission_status",): "LIVE_CANARY_ADMITTED", + } + mismatches = sorted( + ".".join(path) + for path, expected in required_values.items() + if scalars.get(path) != expected + ) + for path in (("runtime_image_digest",), ("canonical_code_sha256",)): + if HEX64.fullmatch(scalars.get(path, "")) is None: + mismatches.append(".".join(path)) + for path in (("external_mcp_contract", "collection"), ("external_mcp_contract", "tenant")): + value = scalars.get(path, "") + if not value or value.startswith("PENDING"): + mismatches.append(".".join(path)) + if any(value.startswith("PENDING") for value in scalars.values()): + mismatches.append("pending_scalar") + + allowlist_marker = " read_path_allowlist:\n" + allowlist_tail = section.split(allowlist_marker, 1) + if len(allowlist_tail) != 2: + mismatches.append("localdocs_contract.read_path_allowlist") + allowlist_text = "" + else: + allowlist_text = allowlist_tail[1].split(" write_root_rule:", 1)[0] + required_allowlist_entries = ( + f"{ASSET_ROOT}/schemas/deployment.schema.json", + f"{ASSET_ROOT}/rules/relief/.md", + f"{ASSET_ROOT}/legal_review/.json", + ) + for entry in required_allowlist_entries: + if f" - {entry}\n" not in allowlist_text + "\n": + mismatches.append(f"allowlist:{entry}") + if mismatches: + raise S220Error( + "EXECUTOR_BINDING_NOT_ADMITTED", + "S2_20 binding is incomplete or structurally inconsistent", + details={"mismatches": sorted(set(mismatches))}, + ) + + +def _admission_signed_material(admission: Mapping[str, Any]) -> dict[str, Any]: + return { + key: value + for key, value in admission.items() + if key not in {"signature", "signature_verification_status", "signed_payload_sha256"} + } + + +def _verify_external_trust( + raw_map: Mapping[str, bytes], + fixed: Mapping[str, str], + request: Mapping[str, Any], + schema_documents: Mapping[str, Any], +) -> tuple[Mapping[str, Any], Mapping[str, Any], Mapping[str, Any]]: + if digest(raw_map[fixed["binding"]]) != request["expected_s2_20_executor_binding_sha256"]: + raise S220Error("EXECUTOR_BINDING_HASH_MISMATCH", "executor binding bytes differ") + _verify_binding_text(raw_map[fixed["binding"]], request) + if digest(raw_map[fixed["agent"]]) != request["expected_s2_20_agent_sha256"]: + raise S220Error("S2_20_AGENT_HASH_MISMATCH", "deployment Agent bytes differ") + if digest(raw_map[fixed["s2_10_release"]]) != request["expected_s2_10_release_sha256"]: + raise S220Error("S2_10_RELEASE_HASH_MISMATCH", "S2_10 child release bytes differ") + + # Host CAS is the first semantic write-authority gate. No plan + # artifact, including a diagnostic, may be published unless this + # exact run-bound receipt has passed its release-bound schema. + host_cas = _require_mapping(load_json(raw_map[fixed["host_cas"]], label=fixed["host_cas"]), code="HOST_CAS_SHAPE", label="host CAS") + _validate_loaded_value( + host_cas, + raw_map[fixed["host_cas"]], + "schemas/deployment.schema.json#/$defs/s2_20_host_cas_receipt", + label=fixed["host_cas"], + schema_documents=schema_documents, + ) + expected_scope = f"S2_20:{request['run_binding_digest']}" + if ( + host_cas.get("run_binding_digest") != request["run_binding_digest"] + or host_cas.get("exclusivity_key") != expected_scope + ): + raise S220Error("HOST_CAS_NOT_ACQUIRED", "host CAS receipt does not prove the exact single writer") + + if digest(raw_map[fixed["admission"]]) != request["detached_admission_receipt_sha256"]: + raise S220Error("ADMISSION_RECEIPT_HASH_MISMATCH", "detached admission bytes differ") + admission = _require_mapping(load_json(raw_map[fixed["admission"]], label=fixed["admission"]), code="ADMISSION_SHAPE", label="admission") + _validate_loaded_value( + admission, + raw_map[fixed["admission"]], + "schemas/deployment.schema.json#/$defs/stage2_deterministic_admission_receipt", + label=fixed["admission"], + schema_documents=schema_documents, + ) + if admission.get("admission_status") not in {"CANARY_ADMITTED", "PRODUCTION_ADMITTED"}: + raise S220Error("ADMISSION_PENDING", "direct execution requires canary or production admission") + if admission.get("parent_release_sha256") != request["expected_parent_stage2_release_sha256"]: + raise S220Error("ADMISSION_PARENT_RELEASE", "admission parent release differs") + if admission.get("executor_binding_sha256") != request["expected_s2_20_executor_binding_sha256"]: + raise S220Error("ADMISSION_EXECUTOR_BINDING", "admission executor binding differs") + if "S2_20" not in admission.get("present_deterministic_stage_ids", []): + raise S220Error("ADMISSION_STAGE_SET", "S2_20 is absent from deterministic admission") + signature = admission.get("signature") + if ( + not isinstance(signature, str) + or not signature + or signature.startswith("PENDING") + or admission.get("signature_verification_status") != "BACKEND_VERIFIED" + ): + raise S220Error("ADMISSION_SIGNATURE", "backend-verified external admission signature is required") + signed_payload_sha256 = require_hex( + admission.get("signed_payload_sha256"), code="ADMISSION_SIGNED_PAYLOAD_HASH" + ) + signed_material = _admission_signed_material(admission) + if signed_payload_sha256 != digest(signed_material): + raise S220Error("ADMISSION_SIGNED_PAYLOAD_HASH", "admission signed payload digest differs") + capability_sha256 = require_hex( + admission.get("backend_capability_receipt_sha256"), + code="ADMISSION_BACKEND_CAPABILITY_HASH", + ) + if capability_sha256 != host_cas.get("backend_capability_receipt_sha256"): + raise S220Error( + "ADMISSION_BACKEND_CAPABILITY_MISMATCH", + "admission verification is not bound to the host CAS backend capability", + ) + matching_stage_receipts = [ + row for row in admission.get("stage_receipts", []) + if isinstance(row, dict) and row.get("asset_id") == "RECEIPT-S2_20-INLINE-CODE" + ] + if ( + len(matching_stage_receipts) != 1 + or matching_stage_receipts[0].get("path") != "manifest/s2_20_inline_code_receipt.json" + or matching_stage_receipts[0].get("sha256") != digest(raw_map[fixed["inline_receipt"]]) + ): + raise S220Error("ADMISSION_STAGE_RECEIPT", "admission does not bind the exact S2_20 inline receipt") + child = _require_mapping(load_json(raw_map[fixed["s2_10_release"]], label=fixed["s2_10_release"]), code="S2_10_RELEASE_SHAPE", label="S2_10 release") + parent_ref = child.get("parent_stage2_release_ref", {}) + if not isinstance(parent_ref, dict) or parent_ref.get("sha256") != request["expected_parent_stage2_release_sha256"]: + raise S220Error("S2_10_PARENT_RELEASE_MISMATCH", "S2_10 child release is not bound to the parent") + release = _require_mapping(load_json(raw_map[fixed["release"]], label=fixed["release"]), code="PARENT_RELEASE_SHAPE", label="parent release") + return release, child, admission + + +def _require_object_self_hash( + value: Mapping[str, Any], field: str, *, code: str, label: str +) -> str: + declared = value.get(field) + require_hex(declared, code=code) + material = {key: item for key, item in value.items() if key != field} + if declared != digest(material): + raise S220Error(code, f"object self hash differs: {label}") + return str(declared) + + +def _validate_loaded_value( + value: Any, + raw: bytes, + schema_ref: str, + *, + label: str, + schema_documents: Mapping[str, Any], + require_canonical_bytes: bool = True, +) -> None: + validate_artifact_payload( + canonical_bytes(value), schema_ref, label, schema_documents + ) + if require_canonical_bytes and raw != canonical_bytes(value): + raise S220Error( + "UPSTREAM_CANONICAL_BYTES_MISMATCH", + f"upstream JSON is not canonical bytes: {label}", + ) + + +def _sealed_child_asset_hash(child_release: Mapping[str, Any], path: str) -> str: + rows = child_release.get("sealed_assets", []) + matches = [ + row for row in rows + if isinstance(row, dict) and row.get("path") == path + ] if isinstance(rows, list) else [] + if len(matches) != 1: + raise S220Error( + "S2_10_CHILD_ASSET_BINDING", + f"child release must seal exactly one asset row: {path}", + ) + return require_hex(matches[0].get("sha256"), code="S2_10_CHILD_ASSET_HASH") + + +def _validate_special_legal_asset( + value: Any, + *, + schema_version: str, + identity_field: str, + identity: str, + rows_field: str, + label: str, +) -> Mapping[str, Any]: + row = _require_mapping( + value, code="SPECIAL_LEGAL_ASSET_SHAPE", label=label + ) + if ( + row.get("schema_version") != schema_version + or row.get(identity_field) != identity + or row.get("status") not in {"PENDING_LEGAL_CONTENT", "LEGAL_CONTENT_ADMITTED"} + or not isinstance(row.get(rows_field), list) + ): + raise S220Error( + "SPECIAL_LEGAL_ASSET_CONTRACT", + f"special legal asset envelope differs: {label}", + ) + if row.get("execution_eligible") is True: + if row.get("status") != "LEGAL_CONTENT_ADMITTED" or not row.get(rows_field): + raise S220Error( + "SPECIAL_LEGAL_ASSET_NOT_ADMITTED", + f"executable special asset lacks admitted rows: {label}", + ) + elif row.get("execution_eligible") is not False: + raise S220Error( + "SPECIAL_LEGAL_ASSET_ELIGIBILITY", + f"execution_eligible must be boolean: {label}", + ) + return row + + +def validate_upstream_handoff( + all_raw: Mapping[str, bytes], + fixed: Mapping[str, str], + request: Mapping[str, Any], + root: str, + cluster_ids: Sequence[str], + wave_paths: Sequence[str], + schema_documents: Mapping[str, Any], + child_release: Mapping[str, Any], +) -> dict[str, Any]: + """Validate and preserve the complete S2_00/S2_10 handoff consumed here.""" + + def loaded(path: str, schema_ref: str) -> Any: + value = load_json(all_raw[path], label=path) + _validate_loaded_value( + value, + all_raw[path], + schema_ref, + label=path, + schema_documents=schema_documents, + ) + return value + + ingress = _require_mapping( + loaded(fixed["ingress_status"], "schemas/ingress.schema.json#/$defs/ingress_status"), + code="INGRESS_STATUS_SHAPE", + label="ingress status", + ) + cluster_plan = _require_mapping( + loaded(fixed["cluster_plan"], "schemas/context.schema.json#/$defs/cluster_plan"), + code="CLUSTER_PLAN_SHAPE", + label="cluster plan", + ) + bundle_plan = _require_mapping( + loaded(fixed["bundle_plan"], "schemas/context.schema.json#/$defs/bundle_plan"), + code="BUNDLE_PLAN_SHAPE", + label="bundle plan", + ) + context_values: dict[str, Any] = {} + for key, definition in ( + ("case_context", "case_context"), + ("evidence_inventory", "evidence_inventory"), + ("object_registry", "object_registry"), + ("party_context", "party_and_title_context"), + ("slot_crosswalk", "slot_crosswalk"), + ): + context_values[key] = loaded( + fixed[key], f"schemas/context.schema.json#/$defs/{definition}" + ) + issue_ledger = loaded( + fixed["issue_ledger"], + "schemas/review_status.schema.json#/$defs/issue_ledger_base", + ) + + binding_receipt = _require_mapping( + ingress.get("run_binding_receipt"), + code="INGRESS_RUN_BINDING_SHAPE", + label="ingress run binding", + ) + barrier = _require_mapping( + ingress.get("output_barrier"), + code="INGRESS_BARRIER_SHAPE", + label="ingress output barrier", + ) + expected_root = f"{root}/" + if ( + binding_receipt.get("run_binding_digest") != request["run_binding_digest"] + or binding_receipt.get("canonical_output_root") != expected_root + or barrier.get("canonical_output_root") != expected_root + or barrier.get("branch") != "NORMAL" + or barrier.get("written_last") is not True + or barrier.get("non_status_artifacts_read_back_verified") is not True + or ingress.get("route") not in {"TO_S2_10", "TO_S2_10_WITH_ISSUES"} + ): + raise S220Error( + "INGRESS_BARRIER_BINDING", + "S2_00 ingress status does not prove the exact normal-route binding", + ) + if sorted(ingress.get("executable_cluster_ids", [])) != sorted(cluster_ids): + raise S220Error( + "INGRESS_CLUSTER_SET", + "ingress executable cluster set differs from cluster plan", + ) + artifact_rows = barrier.get("artifacts", []) + if not isinstance(artifact_rows, list) or barrier.get("artifact_set_digest") != digest(artifact_rows): + raise S220Error( + "INGRESS_ARTIFACT_SET_DIGEST", + "ingress output artifact set digest differs", + ) + artifact_by_path: dict[str, Mapping[str, Any]] = {} + for row in artifact_rows: + if not isinstance(row, dict) or not isinstance(row.get("path"), str): + raise S220Error("INGRESS_ARTIFACT_ROW_SHAPE", "ingress artifact row is invalid") + if row["path"] in artifact_by_path: + raise S220Error("INGRESS_ARTIFACT_PATH_DUPLICATE", f"duplicate ingress artifact: {row['path']}") + artifact_by_path[row["path"]] = row + s2_00_consumed_paths = [ + fixed[key] + for key in ( + "case_context", "evidence_inventory", "object_registry", "cluster_plan", + "party_context", "slot_crosswalk", "bundle_plan", "issue_ledger", + ) + ] + [f"{root}/context/cluster_slices/{cluster_id}.json" for cluster_id in cluster_ids] + for path in s2_00_consumed_paths: + relative = path.removeprefix(f"{root}/") + row = artifact_by_path.get(relative) + if not isinstance(row, dict) or row.get("raw_sha256") != digest(all_raw[path]): + raise S220Error( + "INGRESS_ARTIFACT_LINK_MISMATCH", + f"ingress barrier does not bind consumed artifact: {relative}", + ) + + expected_context_schema_hash = digest(all_raw[fixed["schema_context"]]) + expected_run_id = ingress.get("run_id") + expected_input_set = binding_receipt.get("input_set_digest") + expected_release_digest = binding_receipt.get("stage2_release_digest") + for label, value in { + "cluster_plan": cluster_plan, + "bundle_plan": bundle_plan, + **context_values, + }.items(): + header = value.get("artifact_header") if isinstance(value, dict) else None + if ( + not isinstance(header, dict) + or header.get("schema_sha256") != expected_context_schema_hash + or header.get("run_id") != expected_run_id + or header.get("input_set_digest") != expected_input_set + or header.get("stage2_release_digest") != expected_release_digest + ): + raise S220Error( + "CONTEXT_HEADER_BINDING", + f"context artifact header differs: {label}", + ) + + cluster_slices: dict[str, Mapping[str, Any]] = {} + slice_raw_hashes: dict[str, str] = {} + for cluster_id in sorted(cluster_ids): + path = f"{root}/context/cluster_slices/{cluster_id}.json" + slice_value = _require_mapping( + loaded(path, "schemas/context.schema.json#/$defs/cluster_slice"), + code="CLUSTER_SLICE_SHAPE", + label=path, + ) + slice_material = {key: value for key, value in slice_value.items() if key != "slice_digest"} + if ( + slice_value.get("cluster_id") != cluster_id + or slice_value.get("immutable") is not True + or slice_value.get("slice_digest") != digest(slice_material) + ): + raise S220Error( + "CLUSTER_SLICE_BINDING", + f"cluster slice identity/self hash differs: {cluster_id}", + ) + header = slice_value.get("artifact_header") + if ( + not isinstance(header, dict) + or header.get("schema_sha256") != expected_context_schema_hash + or header.get("run_id") != expected_run_id + or header.get("input_set_digest") != expected_input_set + or header.get("stage2_release_digest") != expected_release_digest + ): + raise S220Error("CLUSTER_SLICE_HEADER", f"cluster slice header differs: {cluster_id}") + cluster_slices[cluster_id] = slice_value + slice_raw_hashes[cluster_id] = digest(all_raw[path]) + + child_agent_hash = _sealed_child_asset_hash( + child_release, "agent_scripts/Stage_2_S2_10.yml" + ) + child_binding_hash = _sealed_child_asset_hash( + child_release, "deployment/stage2_s2_10_llm_binding.yml" + ) + cohorts = bundle_plan.get("cohorts", []) + if not isinstance(cohorts, list): + raise S220Error("BUNDLE_COHORT_SHAPE", "bundle cohorts must be an array") + cohort_ids: list[str] = [] + executable_ids: list[str] = [] + non_executable_ids: list[str] = [] + member_wave_by_cluster: dict[str, int] = {} + for cohort in cohorts: + if not isinstance(cohort, dict) or not isinstance(cohort.get("bundle_cohort_id"), str): + raise S220Error("BUNDLE_COHORT_SHAPE", "bundle cohort object/ID required") + cohort_id = str(cohort["bundle_cohort_id"]) + cohort_ids.append(cohort_id) + status = cohort.get("cohort_status") + if status == "EXECUTABLE": + executable_ids.append(cohort_id) + elif status == "NON_EXECUTABLE": + non_executable_ids.append(cohort_id) + member_ids = cohort.get("cohort_member_cluster_ids", []) + member_slices = cohort.get("member_slices", []) + if ( + member_ids != sorted(member_ids) + or not isinstance(member_slices, list) + or member_slices != sorted(member_slices, key=lambda row: str(row.get("cluster_id", ""))) + or member_ids != [row.get("cluster_id") for row in member_slices if isinstance(row, dict)] + ): + raise S220Error("BUNDLE_MEMBER_SET", f"bundle membership differs: {cohort_id}") + selected_refs = cohort.get("selected_context_refs", []) + if ( + not isinstance(selected_refs, list) + or [row.get("order_index") for row in selected_refs if isinstance(row, dict)] != list(range(len(selected_refs))) + or cohort.get("selected_context_ordered_refs_sha256") != digest(selected_refs) + ): + raise S220Error("BUNDLE_SELECTED_CONTEXT", f"selected context order/hash differs: {cohort_id}") + if ( + cohort.get("s2_10_agent_sha256") != child_agent_hash + or cohort.get("s2_10_llm_binding_sha256") != child_binding_hash + ): + raise S220Error("BUNDLE_S2_10_BINDING", f"bundle S2_10 binding differs: {cohort_id}") + membership_hash = digest(member_ids) + member_slice_hash = digest(member_slices) + receipt = cohort.get("context_materialization_receipt") + echoed = { + "bundle_cohort_id": cohort_id, + "s2_10_agent_sha256": child_agent_hash, + "s2_10_llm_binding_sha256": child_binding_hash, + "selected_context_ordered_refs_sha256": digest(selected_refs), + "cohort_membership_sha256": membership_hash, + "member_slice_ref_set_sha256": member_slice_hash, + } + materialization = { + "handoff_contract_version": cohort.get("handoff_contract_version"), + **echoed, + } + if ( + not isinstance(receipt, dict) + or any(receipt.get(key) != value for key, value in echoed.items()) + or receipt.get("materialization_input_sha256") != digest(materialization) + ): + raise S220Error("BUNDLE_MATERIALIZATION_RECEIPT", f"materialization receipt differs: {cohort_id}") + if status == "EXECUTABLE": + if receipt.get("verification_status") != "PASS" or cohort.get("reason_codes") != []: + raise S220Error("BUNDLE_EXECUTABLE_STATUS", f"executable cohort is not PASS: {cohort_id}") + for member in member_slices: + cluster_id = str(member["cluster_id"]) + expected_path = f"context/cluster_slices/{cluster_id}.json" + if ( + cluster_id in member_wave_by_cluster + or cluster_id not in slice_raw_hashes + or member.get("path") != expected_path + or member.get("sha256") != slice_raw_hashes[cluster_id] + or not isinstance(member.get("dependency_wave_ordinal"), int) + ): + raise S220Error("BUNDLE_SLICE_LINK", f"bundle slice link differs: {cluster_id}") + member_wave_by_cluster[cluster_id] = int(member["dependency_wave_ordinal"]) + if len(cohort_ids) != len(set(cohort_ids)): + raise S220Error("BUNDLE_COHORT_DUPLICATE", "bundle cohort IDs must be unique") + if ( + executable_ids != bundle_plan.get("executable_bundle_cohort_ids") + or non_executable_ids != bundle_plan.get("non_executable_bundle_cohort_ids") + or set(member_wave_by_cluster) != set(cluster_ids) + ): + raise S220Error("BUNDLE_EXECUTABLE_COVERAGE", "bundle executable coverage differs") + expected_wave_ordinals = list(range(len(wave_paths))) + if sorted(set(member_wave_by_cluster.values())) != expected_wave_ordinals: + raise S220Error("BUNDLE_WAVE_ORDINALS", "bundle wave ordinals are not contiguous/exact") + + status = _require_mapping( + loaded(fixed["s2_10_status"], "schemas/s2_10.schema.json#/$defs/global_publish_status"), + code="S2_10_STATUS_SHAPE", + label="S2_10 publish status", + ) + if ( + status.get("status") != "COMPLETE" + or status.get("route") != "TO_S2_20" + or status.get("run_binding_digest") != request["run_binding_digest"] + or status.get("parent_stage2_release_sha256") != request["expected_parent_stage2_release_sha256"] + or status.get("s2_10_release_sha256") != request["expected_s2_10_release_sha256"] + or status.get("status_written_last") is not True + or sorted(status.get("expected_executable_cluster_ids", [])) != sorted(cluster_ids) + or sorted(status.get("valid_domain_verdict_cluster_ids", [])) != sorted(cluster_ids) + or status.get("terminal_technical_failure_cluster_ids") != [] + ): + raise S220Error("S2_10_BARRIER", "S2_10 global barrier differs") + _require_object_self_hash( + status, "status_sha256", code="S2_10_STATUS_SELF_DIGEST", label="S2_10 publish status" + ) + producer = child_release.get("producer_contract", {}) + producer_digest = producer.get("s2_10_producer_contract_digest") if isinstance(producer, dict) else None + require_hex(producer_digest, code="S2_10_PRODUCER_CONTRACT") + + verdicts: list[Mapping[str, Any]] = [] + verdict_sources: dict[str, dict[str, str]] = {} + item_receipts: dict[str, Mapping[str, Any]] = {} + issue_parts: dict[str, Mapping[str, Any]] = {} + assumption_parts: dict[str, Mapping[str, Any]] = {} + usage_parts: dict[str, Mapping[str, Any]] = {} + request_ids: set[str] = set() + for cluster_id in sorted(cluster_ids): + slice_path = f"{root}/context/cluster_slices/{cluster_id}.json" + verdict_path = f"{root}/map_s2_10/domain_verdicts/{cluster_id}.json" + item_path = f"{root}/map_s2_10/item_receipts/{cluster_id}.json" + issue_path = f"{root}/map_s2_10/issue_patches/{cluster_id}.json" + assumption_path = f"{root}/map_s2_10/assumption_parts/{cluster_id}.json" + usage_path = f"{root}/map_s2_10/usage_parts/{cluster_id}.json" + verdict = _require_mapping( + loaded(verdict_path, "schemas/s2_10.schema.json#/$defs/canonical_domain_verdict"), + code="DOMAIN_VERDICT_SHAPE", + label=cluster_id, + ) + verdict_hash = _require_object_self_hash( + verdict, "domain_verdict_sha256", code="DOMAIN_VERDICT_SELF_DIGEST", label=cluster_id + ) + if ( + verdict.get("cluster_id") != cluster_id + or verdict.get("run_binding_digest") != request["run_binding_digest"] + or verdict.get("cluster_slice_sha256") != digest(all_raw[slice_path]) + or verdict.get("producer_contract_digest") != producer_digest + ): + raise S220Error("DOMAIN_VERDICT_LINK", f"domain verdict link differs: {cluster_id}") + item = _require_mapping( + loaded(item_path, "schemas/s2_10.schema.json#/$defs/item_receipt"), + code="ITEM_RECEIPT_SHAPE", + label=item_path, + ) + _require_object_self_hash( + item, "receipt_sha256", code="ITEM_RECEIPT_SELF_DIGEST", label=cluster_id + ) + if ( + item.get("cluster_id") != cluster_id + or item.get("run_binding_digest") != request["run_binding_digest"] + or item.get("domain_verdict_path") != f"map_s2_10/domain_verdicts/{cluster_id}.json" + or item.get("domain_verdict_sha256") != verdict_hash + or item.get("read_back_sha256") != verdict_hash + or item.get("validation_status") != "PASS" + or item.get("persistence_status") not in {"PUBLISHED", "IDEMPOTENT_BYTE_IDENTICAL"} + or item.get("request_id") != verdict.get("request_id") + or item.get("wave_ordinal") != verdict.get("wave_ordinal") + or item.get("attempt_no") != verdict.get("attempt_no") + or item.get("raw_model_output_sha256") != verdict.get("raw_model_output_sha256") + ): + raise S220Error("ITEM_RECEIPT_INVALID", f"invalid S2_10 item receipt: {cluster_id}") + issue = _require_mapping( + loaded(issue_path, "schemas/s2_10.schema.json#/$defs/issue_part"), + code="ISSUE_PART_SHAPE", + label=issue_path, + ) + assumption = _require_mapping( + loaded(assumption_path, "schemas/s2_10.schema.json#/$defs/assumption_part"), + code="ASSUMPTION_PART_SHAPE", + label=assumption_path, + ) + usage = _require_mapping( + loaded(usage_path, "schemas/s2_10.schema.json#/$defs/usage_part"), + code="USAGE_PART_SHAPE", + label=usage_path, + ) + for part, expected_hash, label in ( + (issue, item.get("issue_part_sha256"), "issue"), + (assumption, item.get("assumption_part_sha256"), "assumption"), + (usage, item.get("usage_part_sha256"), "usage"), + ): + part_hash = _require_object_self_hash( + part, "part_sha256", code=f"{label.upper()}_PART_SELF_DIGEST", label=cluster_id + ) + header = part.get("header") + if ( + part_hash != expected_hash + or not isinstance(header, dict) + or header.get("producer_contract_digest") != producer_digest + or header.get("request_id") != item.get("request_id") + or header.get("run_binding_digest") != request["run_binding_digest"] + or header.get("wave_ordinal") != item.get("wave_ordinal") + or header.get("attempt_no") != item.get("attempt_no") + or header.get("cluster_id") != cluster_id + or header.get("domain_verdict_sha256") != verdict_hash + ): + raise S220Error(f"{label.upper()}_PART_LINK", f"{label} part link differs: {cluster_id}") + if assumption.get("assumptions") != verdict.get("assumptions"): + raise S220Error("ASSUMPTION_PART_CONTENT", f"assumption part differs from verdict: {cluster_id}") + request_ids.add(str(item.get("request_id"))) + verdicts.append(verdict) + verdict_sources[cluster_id] = { + "path": verdict_path.removeprefix(f"{root}/"), + "sha256": digest(all_raw[verdict_path]), + } + item_receipts[cluster_id] = item + issue_parts[cluster_id] = issue + assumption_parts[cluster_id] = assumption + usage_parts[cluster_id] = usage + if len(request_ids) != 1: + raise S220Error("S2_10_REQUEST_ID_SET", "all S2_10 items must share one request ID") + + wave_receipts: list[Mapping[str, Any]] = [] + wave_cluster_union: set[str] = set() + for ordinal, path in enumerate(wave_paths): + wave = _require_mapping( + loaded(path, "schemas/s2_10.schema.json#/$defs/wave_receipt"), + code="WAVE_RECEIPT_SHAPE", + label=path, + ) + _require_object_self_hash( + wave, "receipt_sha256", code="WAVE_RECEIPT_SELF_DIGEST", label=path + ) + expected_clusters = sorted( + cluster_id for cluster_id, wave_ordinal in member_wave_by_cluster.items() + if wave_ordinal == ordinal + ) + if ( + wave.get("request_id") not in request_ids + or wave.get("run_binding_digest") != request["run_binding_digest"] + or wave.get("wave_ordinal") != ordinal + or wave.get("is_final_wave") != (ordinal == len(wave_paths) - 1) + or sorted(wave.get("expected_wave_cluster_ids", [])) != expected_clusters + or sorted(wave.get("valid_domain_verdict_cluster_ids", [])) != expected_clusters + or wave.get("terminal_technical_failure_cluster_ids") != [] + or wave.get("wave_status") != "WAVE_COMPLETE" + or wave.get("route") != ("TO_S2_20" if ordinal == len(wave_paths) - 1 else "NEXT_WAVE") + or wave.get("written_once") is not True + ): + raise S220Error("WAVE_RECEIPT_INVALID", f"terminal wave receipt differs: {ordinal}") + for cluster_id in expected_clusters: + if item_receipts[cluster_id].get("wave_ordinal") != ordinal: + raise S220Error("ITEM_WAVE_LINK", f"item/wave ordinal differs: {cluster_id}") + wave_cluster_union.update(expected_clusters) + wave_receipts.append(wave) + if wave_cluster_union != set(cluster_ids): + raise S220Error("WAVE_CLUSTER_COVERAGE", "terminal wave coverage differs") + if status.get("terminal_wave_receipt_sha256s") != [ + wave["receipt_sha256"] for wave in wave_receipts + ]: + raise S220Error("WAVE_RECEIPT_HASH_LINK", "global status wave hashes differ") + + return { + "ingress_status": ingress, + "cluster_plan": cluster_plan, + "bundle_plan": bundle_plan, + "cluster_slices": cluster_slices, + "verdicts": verdicts, + "verdict_sources": verdict_sources, + "item_receipts": item_receipts, + "issue_patches": issue_parts, + "assumption_parts": assumption_parts, + "usage_parts": usage_parts, + "wave_receipts": wave_receipts, + "case_context": context_values["case_context"], + "evidence_inventory": context_values["evidence_inventory"], + "object_registry": context_values["object_registry"], + "party_context": context_values["party_context"], + "slot_crosswalk": context_values["slot_crosswalk"], + "issue_ledger": issue_ledger, + } + + +def hydrate( + localdocs: McpClient, + request: Mapping[str, Any], + publication_guard: dict[str, bool], +) -> tuple[dict[str, Any], dict[str, Any], str]: + root = request["stage2_run_root_ref"] + fixed = { + "release": f"{ASSET_ROOT}/manifest/stage2_release.json", + "module_manifest": f"{ASSET_ROOT}/manifest/module_manifest.json", + "s2_10_release": f"{ASSET_ROOT}/manifest/s2_10_release.json", + "authority_release": f"{ASSET_ROOT}/manifest/authority_release.json", + "case_coverage": f"{ASSET_ROOT}/manifest/case_type_coverage.json", + "binding": f"{ASSET_ROOT}/deployment/stage2_code_executor_binding.yml", + "agent": f"{ASSET_ROOT}/agent_scripts/Stage_2_S2_20.yml", + "inline_receipt": f"{ASSET_ROOT}/manifest/s2_20_inline_code_receipt.json", + "admission": request["detached_admission_receipt_ref"], + "host_cas": request["host_cas_receipt_ref"], + "ingress_status": f"{root}/ingress/ingress_status.json", + "case_context": f"{root}/context/case_context.json", + "evidence_inventory": f"{root}/context/evidence_inventory.json", + "object_registry": f"{root}/context/object_registry.json", + "s2_10_status": f"{root}/map_s2_10/s2_10_publish_status.json", + "cluster_plan": f"{root}/context/cluster_plan.json", + "party_context": f"{root}/context/party_and_title_context.json", + "slot_crosswalk": f"{root}/context/slot_crosswalk.json", + "bundle_plan": f"{root}/context/bundle_plan.json", + "issue_ledger": f"{root}/review/issue_ledger.base.json", + "case_registry": f"{ASSET_ROOT}/manifest/case_type_registry.yml", + "rule_registry": f"{ASSET_ROOT}/manifest/case_type_rule_registry.yml", + "signature_projection": f"{ASSET_ROOT}/registry/binding/binding_signature_projection.yml", + "disposition_policy": f"{ASSET_ROOT}/registry/planning/option_disposition_policy.yml", + "party_rules": f"{ASSET_ROOT}/registry/party/party_set_rules.yml", + "relief_rules": f"{ASSET_ROOT}/registry/drafting/case_type_relief_rules.yml", + "review_policy": f"{ASSET_ROOT}/registry/review/review_policy_registry.yml", + "authority_registry": f"{ASSET_ROOT}/registry/authority/authority_registry.yml", + "corpus_scope": f"{ASSET_ROOT}/registry/corpus/requirement_fact_scope.yml", + "activation": f"{ASSET_ROOT}/registry/calculations/calculation_activation_registry.yml", + "general_law_values": f"{ASSET_ROOT}/law_values/general_law_values.yml", + "court_fee_values": f"{ASSET_ROOT}/law_values/court_fee_values.yml", + "corpus_release": f"{ASSET_ROOT}/manifest/corpus_release.json", + "schema_relief_plan": f"{ASSET_ROOT}/schemas/relief_plan.schema.json", + "schema_binding_retrieval": f"{ASSET_ROOT}/schemas/binding_retrieval.schema.json", + "schema_calculation": f"{ASSET_ROOT}/schemas/calculation.schema.json", + "schema_corpus_collection": f"{ASSET_ROOT}/corpus/weaviate_collection.schema.json", + "schema_ingress": f"{ASSET_ROOT}/schemas/ingress.schema.json", + "schema_context": f"{ASSET_ROOT}/schemas/context.schema.json", + "schema_s2_10": f"{ASSET_ROOT}/schemas/s2_10.schema.json", + "schema_review_status": f"{ASSET_ROOT}/schemas/review_status.schema.json", + "schema_deployment": f"{ASSET_ROOT}/schemas/deployment.schema.json", + "inheritance_reserved_share": f"{ASSET_ROOT}/registry/temporal/inheritance_reserved_share.yml", + "forbidden_relief_rules": f"{ASSET_ROOT}/registry/drafting/forbidden_relief_rules.yml", + "counter_performance_rules": f"{ASSET_ROOT}/registry/drafting/counter_performance_rules.yml", + "procedural_declaration_rules": f"{ASSET_ROOT}/registry/drafting/procedural_declaration_rules.yml", + "actio_route_registry": f"{ASSET_ROOT}/routing/actio_pauliana_route_registry.yml", + "actio_mortgage_route": f"{ASSET_ROOT}/routing/actio_pauliana_mortgage_route.yml", + "actio_value_invariants": f"{ASSET_ROOT}/validators/actio_value_compensation_invariants.yml", + "renderer_R01": f"{ASSET_ROOT}/renderers/R01_money_payment.yml", + "renderer_R02": f"{ASSET_ROOT}/renderers/R02_delivery_possession.yml", + "renderer_R03": f"{ASSET_ROOT}/renderers/R03_declaration_registry.yml", + "renderer_R04": f"{ASSET_ROOT}/renderers/R04_special_nonmoney_performance.yml", + "renderer_R05": f"{ASSET_ROOT}/renderers/R05_declaratory.yml", + "renderer_R06": f"{ASSET_ROOT}/renderers/R06_constitutive_judgment_challenge.yml", + } + seed, _ = read_stable(localdocs, fixed.values()) + if digest(seed[fixed["release"]]) != EXPECTED_STAGE2_RELEASE_SHA256 or digest(seed[fixed["release"]]) != request["expected_parent_stage2_release_sha256"]: + raise S220Error("PARENT_RELEASE_HASH_MISMATCH", "parent release bytes differ") + if digest(seed[fixed["s2_10_status"]]) != request["expected_s2_10_publish_status_sha256"]: + raise S220Error("S2_10_STATUS_HASH_MISMATCH", "S2_10 publish status differs") + if digest(seed[fixed["host_cas"]]) != request["host_cas_receipt_sha256"]: + raise S220Error("HOST_CAS_HASH_MISMATCH", "host CAS receipt differs") + seed_schema_documents = build_schema_documents(seed, fixed) + validate_artifact_payload( + canonical_bytes(request), + "schemas/deployment.schema.json#/$defs/s2_20_request", + REQUEST_PATH, + seed_schema_documents, + ) + release, child_release, _ = _verify_external_trust( + seed, fixed, request, seed_schema_documents + ) + publication_guard["external_trust_verified"] = True + release_without_digest = {key: value for key, value in release.items() if key != "release_digest"} + if release.get("release_digest") != digest(release_without_digest): + raise S220Error("PARENT_RELEASE_SELF_DIGEST", "parent release self digest differs") + module_manifest = _require_mapping( + load_json(seed[fixed["module_manifest"]], label=fixed["module_manifest"]), + code="MODULE_MANIFEST_SHAPE", + label="module manifest", + ) + module_ref = release.get("module_manifest_ref", {}) + if not isinstance(module_ref, dict) or module_ref.get("sha256") != digest(seed[fixed["module_manifest"]]): + raise S220Error("MODULE_MANIFEST_RELEASE_BINDING", "parent release does not bind the module manifest") + module_rows = module_manifest.get("modules", module_manifest.get("module_rows", [])) + by_module_path = { + str(row.get("path")): row + for row in module_rows + if isinstance(row, dict) and isinstance(row.get("path"), str) + } + release_bound_keys = ( + "case_registry", "rule_registry", "signature_projection", "disposition_policy", + "party_rules", "relief_rules", "review_policy", "authority_registry", + "corpus_scope", "activation", "authority_release", "corpus_release", "case_coverage", + "general_law_values", "court_fee_values", + "schema_relief_plan", "schema_binding_retrieval", "schema_calculation", "schema_corpus_collection", + "schema_ingress", "schema_context", "schema_s2_10", "schema_review_status", "schema_deployment", + "inheritance_reserved_share", "forbidden_relief_rules", "counter_performance_rules", + "procedural_declaration_rules", "actio_route_registry", "actio_mortgage_route", + "actio_value_invariants", + "renderer_R01", "renderer_R02", "renderer_R03", "renderer_R04", "renderer_R05", "renderer_R06", + ) + for key in release_bound_keys: + relative = fixed[key].removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(seed[fixed[key]]): + raise S220Error("MODULE_MEMBER_HASH_MISMATCH", f"module manifest does not bind: {relative}") + for key in ("schema_context", "schema_s2_10", "schema_review_status"): + relative = fixed[key].removeprefix(f"{ASSET_ROOT}/") + if _sealed_child_asset_hash(child_release, relative) != digest(seed[fixed[key]]): + raise S220Error( + "S2_10_CHILD_SCHEMA_HASH_MISMATCH", + f"S2_10 child release does not bind schema: {relative}", + ) + cluster_plan = load_json(seed[fixed["cluster_plan"]], label=fixed["cluster_plan"]) + _validate_loaded_value( + cluster_plan, + seed[fixed["cluster_plan"]], + "schemas/context.schema.json#/$defs/cluster_plan", + label=fixed["cluster_plan"], + schema_documents=seed_schema_documents, + ) + ingress_status = load_json(seed[fixed["ingress_status"]], label=fixed["ingress_status"]) + _validate_loaded_value( + ingress_status, + seed[fixed["ingress_status"]], + "schemas/ingress.schema.json#/$defs/ingress_status", + label=fixed["ingress_status"], + schema_documents=seed_schema_documents, + ) + bundle_plan = load_json(seed[fixed["bundle_plan"]], label=fixed["bundle_plan"]) + _validate_loaded_value( + bundle_plan, + seed[fixed["bundle_plan"]], + "schemas/context.schema.json#/$defs/bundle_plan", + label=fixed["bundle_plan"], + schema_documents=seed_schema_documents, + ) + status = load_json(seed[fixed["s2_10_status"]], label=fixed["s2_10_status"]) + if not isinstance(status, dict) or status.get("status") != "COMPLETE" or status.get("route") != "TO_S2_20": + raise S220Error("S2_10_BARRIER", "S2_10 must be COMPLETE/TO_S2_20") + cluster_ids = cluster_plan.get("executable_cluster_ids", []) if isinstance(cluster_plan, dict) else [] + if ( + not isinstance(cluster_ids, list) + or any(not isinstance(item, str) or CLUSTER_ID.fullmatch(item) is None for item in cluster_ids) + ): + raise S220Error("CLUSTER_PLAN_SHAPE", "executable_cluster_ids must be canonical cluster IDs") + dynamic_paths: list[str] = [] + seed_activation = _require_mapping(load_json(seed[fixed["activation"]], label=fixed["activation"]), code="ACTIVATION_REGISTRY_SHAPE", label="activation registry") + descriptor_paths: dict[str, str] = {} + for descriptor in seed_activation.get("calculator_descriptors", []): + if not isinstance(descriptor, dict) or not isinstance(descriptor.get("calculator_id"), str) or not isinstance(descriptor.get("descriptor_path"), str): + raise S220Error("CALCULATOR_DESCRIPTOR_SHAPE", "calculator descriptor ID/path required") + path = join_path(ASSET_ROOT, descriptor["descriptor_path"]) + descriptor_paths[descriptor["calculator_id"]] = path + dynamic_paths.append(path) + seed_rule_registry = _require_mapping( + load_json(seed[fixed["rule_registry"]], label=fixed["rule_registry"]), + code="RULE_REGISTRY_SHAPE", + label="rule registry", + ) + release_selected_legal_paths: set[str] = set() + for registry_key, rows_key in ( + ("case_registry", "rows"), + ("party_rules", "rules"), + ("corpus_scope", "rows"), + ): + registry_value = _require_mapping( + load_json(seed[fixed[registry_key]], label=fixed[registry_key]), + code="LEGAL_REGISTRY_SHAPE", + label=registry_key, + ) + for legal_row in registry_value.get(rows_key, []): + if isinstance(legal_row, dict) and isinstance(legal_row.get("legal_review_receipt_ref"), str): + legal_review_ref = safe_path(str(legal_row["legal_review_receipt_ref"])) + if LEGAL_REVIEW_PATH.fullmatch(legal_review_ref) is None: + raise S220Error( + "LEGAL_REVIEW_PATH_FAMILY", + f"legal review receipt is outside the closed family: {legal_review_ref}", + ) + release_selected_legal_paths.add( + join_path(ASSET_ROOT, legal_review_ref) + ) + for rule_row in seed_rule_registry.get("rows", []): + if not isinstance(rule_row, dict): + continue + for branch in rule_row.get("rule_branches", []): + if not isinstance(branch, dict): + continue + for branch_path_key in ("markdown_source_path", "legal_review_receipt_ref"): + if isinstance(branch.get(branch_path_key), str): + branch_ref = safe_path(str(branch[branch_path_key])) + path_family = RELIEF_RULE_PATH if branch_path_key == "markdown_source_path" else LEGAL_REVIEW_PATH + if path_family.fullmatch(branch_ref) is None: + raise S220Error( + "LEGAL_ASSET_PATH_FAMILY", + f"{branch_path_key} is outside the closed family: {branch_ref}", + ) + release_selected_legal_paths.add( + join_path(ASSET_ROOT, branch_ref) + ) + dynamic_paths.extend(sorted(release_selected_legal_paths)) + for cluster_id in sorted(cluster_ids): + dynamic_paths.extend( + [ + f"{root}/context/cluster_slices/{cluster_id}.json", + f"{root}/map_s2_10/domain_verdicts/{cluster_id}.json", + f"{root}/map_s2_10/item_receipts/{cluster_id}.json", + f"{root}/map_s2_10/issue_patches/{cluster_id}.json", + f"{root}/map_s2_10/assumption_parts/{cluster_id}.json", + f"{root}/map_s2_10/usage_parts/{cluster_id}.json", + ] + ) + bundle_wave_ordinals = sorted({ + int(member["dependency_wave_ordinal"]) + for cohort in bundle_plan.get("cohorts", []) + if isinstance(cohort, dict) and cohort.get("cohort_status") == "EXECUTABLE" + for member in cohort.get("member_slices", []) + if isinstance(member, dict) and isinstance(member.get("dependency_wave_ordinal"), int) + }) + if ( + not bundle_wave_ordinals + or bundle_wave_ordinals[-1] >= len(cluster_ids) + or bundle_wave_ordinals != list(range(bundle_wave_ordinals[-1] + 1)) + ): + raise S220Error("BUNDLE_WAVE_ORDINALS", "executable bundle wave ordinals must be contiguous from zero") + wave_paths = [ + f"{root}/map_s2_10/wave_receipts/wave-{ordinal:04d}.json" + for ordinal in bundle_wave_ordinals + ] + dynamic_paths.extend(wave_paths) + all_raw, snapshot_digest = read_stable(localdocs, list(fixed.values()) + dynamic_paths) + schema_documents = build_schema_documents(all_raw, fixed) + upstream = validate_upstream_handoff( + all_raw, + fixed, + request, + root, + cluster_ids, + wave_paths, + schema_documents, + child_release, + ) + activation = load_json(all_raw[fixed["activation"]], label=fixed["activation"]) + case_registry = _require_mapping(load_json(all_raw[fixed["case_registry"]], label=fixed["case_registry"]), code="CASE_REGISTRY_SHAPE", label="case registry") + rule_registry = _require_mapping(load_json(all_raw[fixed["rule_registry"]], label=fixed["rule_registry"]), code="RULE_REGISTRY_SHAPE", label="rule registry") + case_rows = case_registry.get("rows", case_registry.get("case_types", [])) + rule_rows = rule_registry.get("rows", rule_registry.get("case_type_rule_rows", [])) + if not isinstance(case_rows, list) or len(case_rows) != 137 or len({row.get("case_type_id") for row in case_rows if isinstance(row, dict)}) != 137: + raise S220Error("CASE_REGISTRY_137_CLOSURE", "case registry must contain exactly 137 unique case_type_id rows") + if not isinstance(rule_rows, list) or len(rule_rows) != 137 or {row.get("case_type_id") for row in rule_rows if isinstance(row, dict)} != {row.get("case_type_id") for row in case_rows if isinstance(row, dict)}: + raise S220Error("RULE_REGISTRY_137_CLOSURE", "rule registry must cover the exact case registry set") + if not isinstance(activation, dict) or len(activation.get("calculator_descriptors", [])) != 17: + raise S220Error("CALCULATOR_17_CLOSURE", "calculation registry must enumerate CE-01..13 and CE-R1..R4") + for path in descriptor_paths.values(): + relative = path.removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(all_raw[path]): + raise S220Error("CALCULATOR_MODULE_HASH_MISMATCH", f"calculator descriptor is not release-bound: {relative}") + for path in sorted(release_selected_legal_paths): + relative = path.removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(all_raw[path]): + raise S220Error( + "LEGAL_ASSET_MODULE_HASH_MISMATCH", + f"selected legal asset is not release-bound: {relative}", + ) + inputs = { + **upstream, + "issue_ledger_raw": all_raw[fixed["issue_ledger"]], + } + corpus_release = _require_mapping(load_json(all_raw[fixed["corpus_release"]], label=fixed["corpus_release"]), code="CORPUS_RELEASE_SHAPE", label="corpus release") + signature_projection_registry = _require_mapping( + load_json(all_raw[fixed["signature_projection"]], label=fixed["signature_projection"]), + code="SIGNATURE_PROJECTION_REGISTRY_SHAPE", + label="signature projection registry", + ) + disposition_policy = _require_mapping( + load_json(all_raw[fixed["disposition_policy"]], label=fixed["disposition_policy"]), + code="DISPOSITION_POLICY_SHAPE", + label="option disposition policy", + ) + party_rules = _require_mapping( + load_json(all_raw[fixed["party_rules"]], label=fixed["party_rules"]), + code="PARTY_RULES_SHAPE", + label="party-set rules", + ) + corpus_scope = _require_mapping( + load_json(all_raw[fixed["corpus_scope"]], label=fixed["corpus_scope"]), + code="CORPUS_SCOPE_SHAPE", + label="requirement-fact scope registry", + ) + special_asset_contracts = { + "inheritance_reserved_share": ( + "stage2.s2_20.inheritance_reserved_share_temporal.v1", + "registry_id", "S2-20-INHERITANCE-RESERVED-SHARE-TEMPORAL", "branch_rows", + ), + "forbidden_relief_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-FORBIDDEN-RELIEF-RULES", "rule_rows", + ), + "counter_performance_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-COUNTER-PERFORMANCE-RULES", "rule_rows", + ), + "procedural_declaration_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-PROCEDURAL-DECLARATION-RULES", "rule_rows", + ), + "actio_route_registry": ( + "stage2.s2_20.actio_pauliana_route_registry.v1", + "registry_id", "S2-20-ACTIO-PAULIANA-ROUTES", "route_rows", + ), + "actio_mortgage_route": ( + "stage2.s2_20.actio_pauliana_mortgage_route.v1", + "registry_id", "S2-20-ACTIO-MORTGAGE-ROUTES", "route_rows", + ), + "actio_value_invariants": ( + "stage2.s2_20.actio_value_compensation_invariants.v1", + "validator_id", "S2-20-ACTIO-VALUE-COMPENSATION-INVARIANTS", "invariant_rows", + ), + } + special_legal_assets = { + key: _validate_special_legal_asset( + load_json(all_raw[fixed[key]], label=fixed[key]), + schema_version=contract[0], + identity_field=contract[1], + identity=contract[2], + rows_field=contract[3], + label=fixed[key], + ) + for key, contract in special_asset_contracts.items() + } + assets = { + "case_registry": case_registry, + "rule_registry": rule_registry, + "activation_registry": activation, + "corpus_release": corpus_release, + "corpus_scope": corpus_scope, + "party_rules": party_rules, + "relief_rules": load_json(all_raw[fixed["relief_rules"]], label=fixed["relief_rules"]), + "review_policy": load_json(all_raw[fixed["review_policy"]], label=fixed["review_policy"]), + "signature_projection_registry": signature_projection_registry, + "disposition_policy": disposition_policy, + "special_legal_assets": special_legal_assets, + "party_rules_eligible": party_rules.get("registry_status") == "APPROVED", + "signature_projection_eligible": signature_projection_registry.get("registry_status") == "APPROVED", + "disposition_policy_eligible": disposition_policy.get("policy_status") == "APPROVED", + "authority_admitted": load_json(all_raw[fixed["authority_release"]], label=fixed["authority_release"]).get("status") == "ADMITTED", + "asset_hashes": { + "case_registry": digest(all_raw[fixed["case_registry"]]), + "rule_registry": digest(all_raw[fixed["rule_registry"]]), + "signature_projection": digest(all_raw[fixed["signature_projection"]]), + "corpus_release": digest(all_raw[fixed["corpus_release"]]), + "relief_rules": digest(all_raw[fixed["relief_rules"]]), + "party_rules": digest(all_raw[fixed["party_rules"]]), + "review_policy": digest(all_raw[fixed["review_policy"]]), + "corpus_scope": digest(all_raw[fixed["corpus_scope"]]), + **{ + key: digest(all_raw[fixed[key]]) + for key in sorted(special_legal_assets) + }, + }, + "calculator_descriptor_hashes": { + calculator_id: digest(all_raw[path]) for calculator_id, path in sorted(descriptor_paths.items()) + }, + "calculator_descriptors": { + calculator_id: load_json(all_raw[path], label=path) + for calculator_id, path in sorted(descriptor_paths.items()) + }, + "law_value_tables": [ + load_json(all_raw[fixed["general_law_values"]], label=fixed["general_law_values"]), + load_json(all_raw[fixed["court_fee_values"]], label=fixed["court_fee_values"]), + ], + "renderer_descriptors": { + renderer_id: load_json( + all_raw[fixed[f"renderer_{renderer_id}"]], + label=fixed[f"renderer_{renderer_id}"], + ) + for renderer_id in ("R01", "R02", "R03", "R04", "R05", "R06") + }, + "renderer_hashes": { + renderer_id: digest(all_raw[fixed[f"renderer_{renderer_id}"]]) + for renderer_id in ("R01", "R02", "R03", "R04", "R05", "R06") + }, + "release_selected_legal_bytes": { + path.removeprefix(f"{ASSET_ROOT}/"): all_raw[path] + for path in sorted(release_selected_legal_paths) + }, + "schema_documents": schema_documents, + } + inline_receipt = _require_mapping(load_json(all_raw[fixed["inline_receipt"]], label=fixed["inline_receipt"]), code="INLINE_RECEIPT_SHAPE", label="inline receipt") + canonical_code = inline_receipt.get("canonical_code", {}) + code_sha256 = canonical_code.get("code_sha256") if isinstance(canonical_code, dict) else None + assets["s2_20_code_sha256"] = require_hex(code_sha256, code="INLINE_CODE_HASH_INVALID") + return inputs, assets, snapshot_digest + + +def _artifact_ref(path: str, schema_ref: str, payload: bytes) -> dict[str, str]: + return {"path": path, "schema_ref": schema_ref, "sha256": digest(payload)} + + +def artifact_payloads( + result: Mapping[str, Any], request: Mapping[str, Any] +) -> dict[str, tuple[bytes, str, str]]: + plan = result["plan"] + groups = result["groups"] + claim_groups_base = { + "schema_version": "stage2_claim_groups.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_groups": groups, + "group_dependencies": plan["group_dependencies"], + } + claim_groups = {**claim_groups_base, "document_digest": digest(claim_groups_base)} + bindings = result["bindings"] + binding_base = { + "schema_version": "stage2_case_type_bindings.v1", + "run_binding_digest": request["run_binding_digest"], + "case_type_registry_sha256": result["asset_hashes"]["case_registry"], + "case_type_rule_registry_sha256": result["asset_hashes"]["rule_registry"], + "binding_signature_projection_sha256": result["asset_hashes"]["signature_projection"], + "bindings": bindings, + "bound_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if row["case_type_binding_status"] == "BOUND" and row["sub_rule_binding_status"] == "BOUND"), + "unresolved_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if "UNRESOLVED" in {row["case_type_binding_status"], row["sub_rule_binding_status"]}), + "conflict_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if "CONFLICT" in {row["case_type_binding_status"], row["sub_rule_binding_status"]}), + } + binding_document = {**binding_base, "document_digest": digest(binding_base)} + calculation_state_counts = { + state: sum(row["activation_status"] == state for row in result["calculations"]) + for state in ("ACTIVATED", "NOT_APPLICABLE", "ACTIVATION_GAP", "ACTIVATION_CONFLICT") + } + calculation_report_base = { + "schema_version": "stage2.calculation_report.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_ids": sorted({row["atomic_claim_id"] for row in result["calculations"]}), + "receipt_refs": sorted(f"calculation_receipts/{row['calculation_receipt_id']}.json" for row in result["calculations"]), + "receipt_sha256s": sorted(digest(canonical_bytes(row)) for row in result["calculations"]), + "state_counts": calculation_state_counts, + "missing_operand_ids": sorted({item for row in result["calculations"] for item in row["missing_operands"]}), + "issue_codes": sorted({item for row in result["calculations"] for item in row["issue_codes"]}), + } + calculation_report = {**calculation_report_base, "report_sha256": digest(calculation_report_base)} + filing_metrics_base = { + "schema_version": "stage2.filing_metrics.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": [], + "filing_decision_status": "REVIEW_REQUIRED", + "source_ce13_receipt_ids": [], + } + filing_metrics = {**filing_metrics_base, "report_sha256": digest(filing_metrics_base)} + payloads: dict[str, tuple[bytes, str, str]] = { + "canonical_relief_plan.json": (canonical_bytes(plan), "schemas/relief_plan.schema.json#/$defs/canonical_relief_plan", "C35"), + "claim_groups.json": (canonical_bytes(claim_groups), "schemas/relief_plan.schema.json#/$defs/claim_groups_document", "C30"), + "case_type_bindings.json": (canonical_bytes(binding_document), "schemas/binding_retrieval.schema.json#/$defs/case_type_bindings_document", "C26"), + "issue_ledger.plan.json": (canonical_bytes(result["issues"]), "schemas/relief_plan.schema.json#/$defs/issue_ledger_plan", "C30"), + "calculation_report.json": (canonical_bytes(calculation_report), "schemas/calculation.schema.json#/$defs/calculation_report", "C22"), + "filing_metrics.json": (canonical_bytes(filing_metrics), "schemas/calculation.schema.json#/$defs/filing_metrics", "C22"), + "exhibit_register.json": (canonical_bytes(result["exhibit_register"]), "schemas/relief_plan.schema.json#/$defs/exhibit_register", "C30"), + "option_conservation_report.json": (canonical_bytes(result["option_report"]), "schemas/relief_plan.schema.json#/$defs/option_conservation_report", "C20"), + "party_completeness_report.json": (canonical_bytes(result["party_report"]), "schemas/relief_plan.schema.json#/$defs/party_completeness_report", "C30"), + } + for receipt in result["calculations"]: + payloads[f"calculation_receipts/{receipt['calculation_receipt_id']}.json"] = (canonical_bytes(receipt), "schemas/calculation.schema.json#/$defs/calculation_receipt", "C22") + result_by_atomic = {row["atomic_claim_id"]: row for row in result["retrieval"]["results"]} + receipt_by_atomic = {row["atomic_claim_id"]: row for row in result["retrieval"]["receipts"]} + for group in groups: + group_id = group["claim_group_id"] + group_bindings = [row for row in bindings if row["atomic_claim_id"] in group["atomic_claim_ids"]] + group_rule_refs = [ + result["rule_asset_refs"].get(atomic_id, {}) + for atomic_id in group["atomic_claim_ids"] + ] + def merged_rule_refs(key: str) -> list[dict[str, str]]: + indexed = { + (str(ref["path"]), str(ref["sha256"]), str(ref["json_pointer"])): ref + for row in group_rule_refs + for ref in row.get(key, []) + if isinstance(ref, dict) + } + return [indexed[index] for index in sorted(indexed)] + rule_base = { + "schema_version": "stage2_rule_context_pack.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "binding_rows": group_bindings, + "structured_relief_rule_refs": merged_rule_refs("structured_relief_rule_refs"), + "markdown_source_refs": merged_rule_refs("markdown_source_refs"), + "renderer_refs": merged_rule_refs("renderer_refs"), + "party_set_rule_refs": merged_rule_refs("party_set_rule_refs"), + "ce13_branch_refs": merged_rule_refs("ce13_branch_refs"), + "review_policy_refs": merged_rule_refs("review_policy_refs"), + "authority_class": "RELIEF_ONLY", + } + rule_pack = {**rule_base, "pack_sha256": digest(rule_base)} + group_receipts = [receipt_by_atomic[item] for item in group["atomic_claim_ids"] if item in receipt_by_atomic] + fact_base = { + "schema_version": "stage2_requirement_fact_pack.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "atomic_claim_ids": group["atomic_claim_ids"], + "corpus_release_sha256": result["asset_hashes"]["corpus_release"], + "snapshot_id": str(result["corpus_release"].get("snapshot_id") or "UNAVAILABLE"), + "snapshot_sha256": str(result["corpus_release"].get("snapshot_sha256") or ("0" * 64)), + "retrieval_receipt_refs": [ + {"path": f"retrieval_receipts/{row['atomic_claim_id']}.json", "sha256": digest(canonical_bytes(row)), "json_pointer": ""} + for row in group_receipts + ], + "items": sorted( + [ + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["pack_items"].get(atomic_id, []) + ], + key=lambda row: (row["corpus_element_id"], row["chunk_id"]), + ), + "unmapped_chunk_ids": sorted( + { + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["unmapped_by_atomic"].get(atomic_id, []) + } + ), + "conflicting_chunk_ids": sorted( + { + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["conflicting_by_atomic"].get(atomic_id, []) + } + ), + "authority_class": "CAUSE_AND_ELEMENT_CHECK", + "issue_codes": sorted({code for item in group["atomic_claim_ids"] for code in result_by_atomic[item]["issue_codes"]}), + } + fact_pack = {**fact_base, "pack_sha256": digest(fact_base)} + gap_rows = [{ + "corpus_element_id": "UNRESOLVED", + "doctrine_role": "element", + "stage1_slot_refs": [], + "fact_refs": [], + "evidence_refs": [], + "coverage_status": "GAP", + "issue_refs": group["issue_refs"], + }] + gap_document = { + "schema_version": "stage2_element_evidence_gap.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "rows": gap_rows, + "gap_count": 1, + "conflict_count": 0, + } + rule_path = f"rule_context_packs/{group_id}.json" + fact_path = f"requirement_fact_packs/{group_id}.json" + gap_path = f"element_evidence_gap/{group_id}.json" + rule_raw = canonical_bytes(rule_pack) + fact_raw = canonical_bytes(fact_pack) + gap_raw = canonical_bytes(gap_document) + payloads[rule_path] = (rule_raw, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", "C26") + payloads[fact_path] = (fact_raw, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", "C29") + payloads[gap_path] = (gap_raw, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", "C30") + calculation_refs = [ + _artifact_ref( + f"calculation_receipts/{row['calculation_receipt_id']}.json", + "schemas/calculation.schema.json#/$defs/calculation_receipt", + canonical_bytes(row), + ) + for row in result["calculations"] if row["atomic_claim_id"] in group["atomic_claim_ids"] + ] + group_slice_base = { + "schema_version": "stage2_s2_20_group_slice.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "frozen_plan_sha256": digest(canonical_bytes(plan)), + "claim_group": group, + "rule_context_pack_ref": _artifact_ref(rule_path, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", rule_raw), + "requirement_fact_pack_ref": _artifact_ref(fact_path, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", fact_raw), + "element_evidence_gap_ref": _artifact_ref(gap_path, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", gap_raw), + "calculation_receipt_refs": calculation_refs, + "issue_refs": group["issue_refs"], + "drafting_permission": group["drafting_permission"], + } + group_slice = {**group_slice_base, "group_slice_sha256": digest(group_slice_base)} + group_path = f"group_slices/{group_id}.json" + group_raw = canonical_bytes(group_slice) + payloads[group_path] = (group_raw, "schemas/relief_plan.schema.json#/$defs/group_slice", "C35") + for segment_id, authority_class, refs in ( + ("P31_rule_and_pack", "RELIEF_ONLY_AND_CAUSE_CHECK", [ + _artifact_ref(rule_path, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", rule_raw), + _artifact_ref(fact_path, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", fact_raw), + _artifact_ref(gap_path, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", gap_raw), + ]), + ("P32_common_authority", "COMMON_AUTHORITY", []), + ): + segment_base = { + "schema_version": "stage2_s2_20_prompt_segment.v1", + "segment_id": segment_id, + "claim_group_id": group_id, + "authority_class": authority_class, + "ordered_asset_refs": refs, + "source_group_slice_sha256": digest(group_raw), + } + segment = {**segment_base, "segment_payload_sha256": digest(segment_base)} + payloads[f"prompt_segments/{group_id}/{segment_id}.json"] = (canonical_bytes(segment), "schemas/relief_plan.schema.json#/$defs/prompt_segment", "C35") + for row in result["retrieval"]["queries"]: + payloads[f"query_plans/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/query_plan", "C27") + for row in result["retrieval"]["results"]: + payloads[f"retrieval_results/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/retrieval_result", "C28") + for row in result["retrieval"]["receipts"]: + payloads[f"retrieval_receipts/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/retrieval_receipt", "C28") + return payloads + + +def build_schema_documents( + all_raw: Mapping[str, bytes], fixed: Mapping[str, str] +) -> dict[str, Mapping[str, Any]]: + """Load release-bound schemas and close their declared external $ref families. + + ``binding_retrieval.schema.json`` deliberately reuses the sealed + Weaviate collection schema. The inline validator has no filesystem + resolver, so this function merges the external definitions into a + private in-memory validation documents and rewrites only declared + relative references. Conflicting shared definitions fail closed. + """ + + relief = _require_mapping( + load_json(all_raw[fixed["schema_relief_plan"]], label=fixed["schema_relief_plan"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="relief plan schema", + ) + binding = _require_mapping( + load_json(all_raw[fixed["schema_binding_retrieval"]], label=fixed["schema_binding_retrieval"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="binding/retrieval schema", + ) + calculation = _require_mapping( + load_json(all_raw[fixed["schema_calculation"]], label=fixed["schema_calculation"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="calculation schema", + ) + corpus = _require_mapping( + load_json(all_raw[fixed["schema_corpus_collection"]], label=fixed["schema_corpus_collection"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="corpus collection schema", + ) + ingress = _require_mapping( + load_json(all_raw[fixed["schema_ingress"]], label=fixed["schema_ingress"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="ingress schema", + ) + context = _require_mapping( + load_json(all_raw[fixed["schema_context"]], label=fixed["schema_context"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="context schema", + ) + s2_10 = _require_mapping( + load_json(all_raw[fixed["schema_s2_10"]], label=fixed["schema_s2_10"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="S2_10 schema", + ) + review = _require_mapping( + load_json(all_raw[fixed["schema_review_status"]], label=fixed["schema_review_status"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="review/status schema", + ) + deployment = _require_mapping( + load_json(all_raw[fixed["schema_deployment"]], label=fixed["schema_deployment"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="deployment schema", + ) + merged_binding = load_json(canonical_bytes(binding), label="binding schema canonical copy") + merged_defs = merged_binding.get("$defs") + corpus_defs = corpus.get("$defs") + if not isinstance(merged_defs, dict) or not isinstance(corpus_defs, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", "binding and corpus schemas require $defs") + for name, definition in corpus_defs.items(): + if name in merged_defs and merged_defs[name] != definition: + raise S220Error( + "ARTIFACT_SCHEMA_EXTERNAL_DEF_CONFLICT", + f"shared schema definition differs: {name}", + ) + merged_defs.setdefault(name, definition) + + external_prefix = "../corpus/weaviate_collection.schema.json#" + + def rewrite_external_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith(external_prefix): + value["$ref"] = "#" + ref[len(external_prefix):] + for child in value.values(): + rewrite_external_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_external_refs(child) + + rewrite_external_refs(merged_binding) + + def merge_namespaced_external_defs( + target: Mapping[str, Any], + source: Mapping[str, Any], + *, + external_prefix: str, + namespace: str, + ) -> dict[str, Any]: + merged = load_json(canonical_bytes(target), label=f"{namespace} target schema copy") + source_copy = load_json(canonical_bytes(source), label=f"{namespace} source schema copy") + merged_defs = merged.get("$defs") + source_defs = source_copy.get("$defs") + if not isinstance(merged_defs, dict) or not isinstance(source_defs, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", "merged schemas require $defs") + + def rewrite_source_local_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith("#/$defs/"): + value["$ref"] = f"#/$defs/{namespace}__{ref[len('#/$defs/'):]}" + for child in value.values(): + rewrite_source_local_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_source_local_refs(child) + + for name, definition in source_defs.items(): + copied = load_json(canonical_bytes(definition), label=f"{namespace} definition copy") + rewrite_source_local_refs(copied) + target_name = f"{namespace}__{name}" + if target_name in merged_defs and merged_defs[target_name] != copied: + raise S220Error( + "ARTIFACT_SCHEMA_EXTERNAL_DEF_CONFLICT", + f"namespaced schema definition differs: {target_name}", + ) + merged_defs[target_name] = copied + + def rewrite_target_external_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith(external_prefix): + fragment = ref[len(external_prefix):] + if not fragment.startswith("#/$defs/"): + raise S220Error( + "ARTIFACT_SCHEMA_REF_UNSUPPORTED", + f"external schema reference is not a $defs pointer: {ref}", + ) + value["$ref"] = f"#/$defs/{namespace}__{fragment[len('#/$defs/'):]}" + for child in value.values(): + rewrite_target_external_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_target_external_refs(child) + + rewrite_target_external_refs(merged) + return merged + + merged_context = merge_namespaced_external_defs( + context, + review, + external_prefix="review_status.schema.json", + namespace="review_status", + ) + merged_review = merge_namespaced_external_defs( + review, + relief, + external_prefix="relief_plan.schema.json", + namespace="relief_plan", + ) + return { + "schemas/relief_plan.schema.json": relief, + "schemas/binding_retrieval.schema.json": merged_binding, + "schemas/calculation.schema.json": calculation, + "schemas/ingress.schema.json": ingress, + "schemas/context.schema.json": merged_context, + "schemas/s2_10.schema.json": s2_10, + "schemas/review_status.schema.json": merged_review, + "schemas/deployment.schema.json": deployment, + } + + +def _schema_pointer(document: Any, fragment: str) -> Any: + if fragment in {"", "#"}: + return document + pointer = fragment[1:] if fragment.startswith("#") else fragment + if not pointer.startswith("/"): + raise S220Error("ARTIFACT_SCHEMA_REF_UNSUPPORTED", f"non-pointer schema fragment: {fragment}") + current = document + for raw_token in pointer[1:].split("/"): + token = raw_token.replace("~1", "/").replace("~0", "~") + try: + current = current[int(token)] if isinstance(current, list) else current[token] + except (KeyError, IndexError, TypeError, ValueError) as exc: + raise S220Error("ARTIFACT_SCHEMA_REF_UNRESOLVED", f"schema pointer is unresolved: {fragment}") from exc + return current + + +def _schema_type_matches(instance: Any, expected: str) -> bool: + if expected == "null": + return instance is None + if expected == "boolean": + return isinstance(instance, bool) + if expected == "object": + return isinstance(instance, dict) + if expected == "array": + return isinstance(instance, list) + if expected == "string": + return isinstance(instance, str) + if expected == "integer": + return isinstance(instance, int) and not isinstance(instance, bool) + if expected == "number": + return isinstance(instance, (int, float)) and not isinstance(instance, bool) + raise S220Error("ARTIFACT_SCHEMA_KEYWORD_UNSUPPORTED", f"unsupported JSON Schema type: {expected}") + + +def _schema_identity(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def _schema_errors( + instance: Any, + schema: Any, + root_schema: Mapping[str, Any], + instance_path: str, + stack: set[tuple[int, int]], +) -> list[str]: + if schema is True: + return [] + if schema is False: + return [f"{instance_path}: boolean schema false"] + if not isinstance(schema, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"schema is not an object/boolean at {instance_path}") + marker = (id(schema), id(instance)) + if marker in stack: + return [] + nested_stack = set(stack) + nested_stack.add(marker) + errors: list[str] = [] + + if "$ref" in schema: + ref = schema["$ref"] + if not isinstance(ref, str) or not ref.startswith("#"): + raise S220Error("ARTIFACT_SCHEMA_REF_UNSUPPORTED", f"only local schema refs are allowed: {ref!r}") + errors.extend( + _schema_errors( + instance, + _schema_pointer(root_schema, ref), + root_schema, + instance_path, + nested_stack, + ) + ) + + for branch in schema.get("allOf", []): + errors.extend(_schema_errors(instance, branch, root_schema, instance_path, nested_stack)) + if "anyOf" in schema: + branches = [ + _schema_errors(instance, branch, root_schema, instance_path, nested_stack) + for branch in schema["anyOf"] + ] + if not any(not branch for branch in branches): + errors.append(f"{instance_path}: anyOf matched no branch") + if "oneOf" in schema: + branches = [ + _schema_errors(instance, branch, root_schema, instance_path, nested_stack) + for branch in schema["oneOf"] + ] + matched = sum(not branch for branch in branches) + if matched != 1: + errors.append(f"{instance_path}: oneOf expected one match, got {matched}") + if "not" in schema and not _schema_errors( + instance, schema["not"], root_schema, instance_path, nested_stack + ): + errors.append(f"{instance_path}: not subschema matched") + if "if" in schema: + condition_matches = not _schema_errors( + instance, schema["if"], root_schema, instance_path, nested_stack + ) + selected = "then" if condition_matches else "else" + if selected in schema: + errors.extend( + _schema_errors(instance, schema[selected], root_schema, instance_path, nested_stack) + ) + + expected_type = schema.get("type") + if expected_type is not None: + expected_types = [expected_type] if isinstance(expected_type, str) else expected_type + if not isinstance(expected_types, list) or not all(isinstance(item, str) for item in expected_types): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"invalid type declaration at {instance_path}") + if not any(_schema_type_matches(instance, item) for item in expected_types): + errors.append(f"{instance_path}: type mismatch; expected {expected_types}") + return errors + + if "const" in schema and instance != schema["const"]: + errors.append(f"{instance_path}: const mismatch") + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{instance_path}: value is outside enum") + + if isinstance(instance, str): + if "pattern" in schema: + try: + pattern_matches = re.search(str(schema["pattern"]), instance) is not None + except re.error as exc: + raise S220Error("ARTIFACT_SCHEMA_PATTERN_INVALID", f"invalid schema pattern at {instance_path}") from exc + if not pattern_matches: + errors.append(f"{instance_path}: pattern mismatch") + if len(instance) < int(schema.get("minLength", 0)): + errors.append(f"{instance_path}: shorter than minLength") + if schema.get("format") == "date": + try: + parsed_date = date.fromisoformat(instance) + except ValueError: + parsed_date = None + if parsed_date is None or parsed_date.isoformat() != instance: + errors.append(f"{instance_path}: invalid RFC 3339 full-date") + elif "format" in schema: + raise S220Error( + "ARTIFACT_SCHEMA_KEYWORD_UNSUPPORTED", + f"unsupported string format at {instance_path}: {schema['format']}", + ) + + if isinstance(instance, (int, float)) and not isinstance(instance, bool): + if "minimum" in schema and instance < schema["minimum"]: + errors.append(f"{instance_path}: below minimum") + if "maximum" in schema and instance > schema["maximum"]: + errors.append(f"{instance_path}: above maximum") + + if isinstance(instance, dict): + required = schema.get("required", []) + if not isinstance(required, list): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"required must be an array at {instance_path}") + for key in required: + if key not in instance: + errors.append(f"{instance_path}: missing required property {key!r}") + properties = schema.get("properties", {}) + if not isinstance(properties, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"properties must be an object at {instance_path}") + additional = schema.get("additionalProperties", True) + for key, value in instance.items(): + child_path = f"{instance_path}.{key}" + if key in properties: + errors.extend( + _schema_errors(value, properties[key], root_schema, child_path, nested_stack) + ) + elif additional is False: + errors.append(f"{child_path}: additional property is forbidden") + elif isinstance(additional, dict): + errors.extend( + _schema_errors(value, additional, root_schema, child_path, nested_stack) + ) + if len(instance) < int(schema.get("minProperties", 0)): + errors.append(f"{instance_path}: fewer than minProperties") + + if isinstance(instance, list): + if len(instance) < int(schema.get("minItems", 0)): + errors.append(f"{instance_path}: fewer than minItems") + if "maxItems" in schema and len(instance) > int(schema["maxItems"]): + errors.append(f"{instance_path}: more than maxItems") + if schema.get("uniqueItems") is True: + identities = [_schema_identity(item) for item in instance] + if len(identities) != len(set(identities)): + errors.append(f"{instance_path}: array items are not unique") + prefix_items = schema.get("prefixItems", []) + if not isinstance(prefix_items, list): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"prefixItems must be an array at {instance_path}") + for index, child_schema in enumerate(prefix_items[: len(instance)]): + errors.extend( + _schema_errors( + instance[index], child_schema, root_schema, f"{instance_path}[{index}]", nested_stack + ) + ) + if "items" in schema: + for index in range(len(prefix_items), len(instance)): + errors.extend( + _schema_errors( + instance[index], schema["items"], root_schema, f"{instance_path}[{index}]", nested_stack + ) + ) + if "contains" in schema: + matched = sum( + not _schema_errors(item, schema["contains"], root_schema, f"{instance_path}[{index}]", nested_stack) + for index, item in enumerate(instance) + ) + if matched < 1: + errors.append(f"{instance_path}: contains matched no item") + return errors + + +def validate_artifact_payload( + payload: bytes, + schema_ref: str, + path: str, + schema_documents: Mapping[str, Any], +) -> None: + value = load_json(payload, label=path) + schema_path, separator, fragment = schema_ref.partition("#") + root_schema = schema_documents.get(schema_path) + if not isinstance(root_schema, dict): + raise S220Error("ARTIFACT_SCHEMA_REF_UNRESOLVED", f"release-bound schema is unavailable: {schema_path}") + schema = _schema_pointer(root_schema, f"#{fragment}" if separator else "") + errors = _schema_errors(value, schema, root_schema, "$", set()) + if errors: + raise S220Error( + "ARTIFACT_SCHEMA_VALIDATION_FAILED", + f"artifact violates release-bound schema: {path}", + details=errors[:20], + ) + + +def publish(localdocs: McpClient, request: Mapping[str, Any], result: Mapping[str, Any]) -> dict[str, Any]: + root = request["plan_output_root_ref"] + payloads = artifact_payloads(result, request) + for relative, (payload, schema_ref, _) in sorted(payloads.items()): + validate_artifact_payload(payload, schema_ref, relative, result["schema_documents"]) + rows: list[dict[str, Any]] = [] + for relative, (payload, schema_ref, producer) in sorted(payloads.items()): + try: + localdocs.write_immutable(join_path(root, relative), payload) + except Exception as exc: + raise S220Error( + "PUBLISH_PARTIAL_WRITE", + f"artifact publication stopped before barrier: {relative}", + details={ + "written_paths": [row["path"] for row in rows], + "written_artifact_rows": [dict(row) for row in rows], + "failed_path": f"plan/{relative}", + "cause": str(exc), + }, + ) from exc + rows.append({"path": f"plan/{relative}", "schema_ref": schema_ref, "raw_sha256": digest(payload), "byte_size": len(payload), "producer_component": producer}) + groups = result["groups"] + barrier_without_hash = { + "schema_version": "stage2_plan_publish_status.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": result["plan"]["input_snapshot_digest"], + "parent_stage2_release_sha256": EXPECTED_STAGE2_RELEASE_SHA256, + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "s2_20_agent_sha256": request["expected_s2_20_agent_sha256"], + "s2_20_code_sha256": result["s2_20_code_sha256"], + "plan_status": "PLAN_COMPLETE_WITH_REVIEW" if any(row["drafting_permission"] != "CLEAN_DRAFT" for row in groups) else "PLAN_COMPLETE", + "route": "TO_S2_30", + "consumer_authorized": True, + "artifact_rows": rows, + "artifact_set_digest": digest(rows), + "group_ids": [row["claim_group_id"] for row in groups], + "clean_draft_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "CLEAN_DRAFT"], + "review_draft_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "REVIEW_DRAFT"], + "worknote_only_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "WORKNOTE_ONLY"], + "issue_summary": result["issue_summary"], + } + barrier = {**barrier_without_hash, "barrier_sha256": digest(barrier_without_hash)} + barrier_raw = canonical_bytes(barrier) + try: + localdocs.write_immutable(join_path(root, "plan_publish_status.json"), barrier_raw) + except Exception as exc: + raise S220Error( + "PUBLISH_PARTIAL_WRITE", + "all artifacts were written but the final consumer barrier failed", + details={ + "written_paths": [row["path"] for row in rows], + "written_artifact_rows": [dict(row) for row in rows], + "failed_path": "plan/plan_publish_status.json", + "cause": str(exc), + }, + ) from exc + return {"barrier_sha256": digest(barrier_raw), "artifact_set_digest": barrier["artifact_set_digest"], "route": barrier["route"], "plan_status": barrier["plan_status"]} + + +def publish_diagnostic(localdocs: McpClient, request: Mapping[str, Any], error: Mapping[str, Any]) -> dict[str, Any]: + root = request["plan_output_root_ref"] + details = error.get("details") if isinstance(error.get("details"), dict) else {} + partial_rows_raw = details.get("written_artifact_rows", []) if isinstance(details, dict) else [] + partial_rows: list[dict[str, Any]] = [] + if isinstance(partial_rows_raw, list): + for row in partial_rows_raw: + if not isinstance(row, dict) or set(row) != { + "path", "schema_ref", "raw_sha256", "byte_size", "producer_component" + }: + raise S220Error("DIAGNOSTIC_PARTIAL_ROW", "partial artifact row has an open shape") + safe_path(str(row["path"])) + require_hex(row["raw_sha256"], code="DIAGNOSTIC_PARTIAL_ROW_HASH") + if not isinstance(row["byte_size"], int) or isinstance(row["byte_size"], bool) or row["byte_size"] < 1: + raise S220Error("DIAGNOSTIC_PARTIAL_ROW_SIZE", "partial artifact byte size is invalid") + partial_rows.append(dict(row)) + source_refs = sorted(str(row["path"]) for row in partial_rows) + diagnostic_base = { + "schema_version": "stage2_s2_20_technical_diagnostic.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "attempt_id": request["attempt_id"], + "failure_component": "PUBLISH" if error.get("code") == "PUBLISH_PARTIAL_WRITE" else "PREFLIGHT", + "failure_code": str(error.get("code", "INLINE_RUNTIME_ERROR")), + "failure_scope": "RUN_WIDE", + "message": str(error.get("message", "S2_20 failed")), + "input_snapshot_digest": None, + "source_refs": source_refs, + "retryable": str(error.get("code", "")).startswith(("MCP_", "LOCALDOCS_")), + "operator_action_required": True, + } + diagnostic = {**diagnostic_base, "diagnostic_sha256": digest(diagnostic_base)} + diagnostic_raw = canonical_bytes(diagnostic) + localdocs.write_immutable(join_path(root, "technical_diagnostic.json"), diagnostic_raw) + rows = partial_rows + [{ + "path": "plan/technical_diagnostic.json", + "schema_ref": "schemas/relief_plan.schema.json#/$defs/technical_diagnostic", + "raw_sha256": digest(diagnostic_raw), + "byte_size": len(diagnostic_raw), + "producer_component": "S2_20_DIAGNOSTIC", + }] + rows = sorted(rows, key=lambda row: str(row["path"])) + barrier_base = { + "schema_version": "stage2_plan_publish_status.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": "0" * 64, + "parent_stage2_release_sha256": EXPECTED_STAGE2_RELEASE_SHA256, + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "s2_20_agent_sha256": request["expected_s2_20_agent_sha256"], + "s2_20_code_sha256": digest(b"UNAVAILABLE_BEFORE_PREFLIGHT"), + "plan_status": "TECHNICAL_INCOMPLETE", + "route": "STOP_TECHNICAL_INCOMPLETE", + "consumer_authorized": False, + "artifact_rows": rows, + "artifact_set_digest": digest(rows), + "group_ids": [], + "clean_draft_group_ids": [], + "review_draft_group_ids": [], + "worknote_only_group_ids": [], + "issue_summary": {"total": 1, "legal": 0, "binding": 0, "corpus": 0, "calculation": 0, "technical": 1}, + } + barrier = {**barrier_base, "barrier_sha256": digest(barrier_base)} + barrier_raw = canonical_bytes(barrier) + localdocs.write_immutable(join_path(root, "plan_publish_status.json"), barrier_raw) + return {"barrier_sha256": digest(barrier_raw), "artifact_set_digest": digest(rows), "route": "STOP_TECHNICAL_INCOMPLETE", "plan_status": "TECHNICAL_INCOMPLETE"} + + +def run() -> int: + localdocs: McpClient | None = None + request: dict[str, Any] | None = None + publication_guard = {"external_trust_verified": False} + receipt: dict[str, Any] + exit_code = 0 + try: + with contextlib.redirect_stdout(io.StringIO()): + if HEX64.fullmatch(INLINE_USER_HASH) is None or HEX64.fullmatch(INLINE_WORKSPACE_HASH) is None: + raise S220Error("INLINE_CONTEXT_UNBOUND", "AgentBackend user/workspace substitutions are required") + localdocs = McpClient(LOCALDOCS_URL, "localdocs") + localdocs.initialize() + request = validate_request(localdocs.read_binary(REQUEST_PATH)) + inputs, assets, snapshot_digest = hydrate(localdocs, request, publication_guard) + result = execute_core(inputs, assets, request, snapshot_digest) + publication = publish(localdocs, request, result) + receipt = { + "schema_version": "stage2_s2_20_execution_receipt.v1", + "ok": True, + "workflow_id": WORKFLOW_ID, + "request_id": request["request_id"], + "run_binding_digest": request["run_binding_digest"], + "plan_status": publication["plan_status"], + "route": publication["route"], + "artifact_set_digest": publication["artifact_set_digest"], + "plan_publish_status_path": "plan/plan_publish_status.json", + "plan_publish_status_sha256": publication["barrier_sha256"], + "error": None, + } + except Exception as exc: + error = exc.as_dict() if isinstance(exc, S220Error) else {"code": "INLINE_RUNTIME_ERROR", "message": str(exc)} + diagnostic_publication: dict[str, Any] | None = None + if ( + localdocs is not None + and request is not None + and publication_guard["external_trust_verified"] + ): + try: + diagnostic_publication = publish_diagnostic(localdocs, request, error) + except Exception as diagnostic_exc: + error = {**error, "diagnostic_publish_error": str(diagnostic_exc)} + receipt = { + "schema_version": "stage2_s2_20_execution_receipt.v1", + "ok": False, + "workflow_id": WORKFLOW_ID, + "request_id": request["request_id"] if request is not None else "UNBOUND", + "run_binding_digest": request["run_binding_digest"] if request is not None else "0" * 64, + "plan_status": diagnostic_publication["plan_status"] if diagnostic_publication is not None else "TECHNICAL_INCOMPLETE", + "route": diagnostic_publication["route"] if diagnostic_publication is not None else "STOP_TECHNICAL_INCOMPLETE", + "artifact_set_digest": diagnostic_publication["artifact_set_digest"] if diagnostic_publication is not None else None, + "plan_publish_status_path": "plan/plan_publish_status.json", + "plan_publish_status_sha256": diagnostic_publication["barrier_sha256"] if diagnostic_publication is not None else None, + "error": error, + } + exit_code = 2 + finally: + if localdocs is not None: + localdocs.close() + sys.stdout.buffer.write(canonical_bytes(receipt)) + return exit_code + + +if __name__ == "__main__": + raise SystemExit(run()) \ No newline at end of file diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/binding_retrieval.schema.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/binding_retrieval.schema.json new file mode 100644 index 00000000..e5c3cf10 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/binding_retrieval.schema.json @@ -0,0 +1,1219 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.liti-agent.local/stage2/s2_20/binding_retrieval.schema.v1.json", + "title": "S2_20 case-type, relief-rule, party-set, query, retrieval, and requirement-fact pack contracts", + "description": "Closed deterministic contracts. Pending legal classification/content is representable but can never satisfy executable BOUND or CLEAN_DRAFT conditions.", + "schema_version": "stage2_s2_20_binding_retrieval_contract_bundle.v1", + "oneOf": [ + {"$ref": "#/$defs/binding_signature_projection_registry"}, + {"$ref": "#/$defs/option_disposition_policy"}, + {"$ref": "#/$defs/party_set_rules"}, + {"$ref": "#/$defs/case_type_registry"}, + {"$ref": "#/$defs/case_type_rule_registry"}, + {"$ref": "#/$defs/case_type_bindings_document"}, + {"$ref": "#/$defs/requirement_fact_scope_registry"}, + {"$ref": "#/$defs/rule_context_pack"}, + {"$ref": "#/$defs/query_plan"}, + {"$ref": "#/$defs/retrieval_result"}, + {"$ref": "#/$defs/retrieval_receipt"}, + {"$ref": "#/$defs/requirement_fact_pack"} + ], + "$defs": { + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "nonempty_string": { + "type": "string", + "minLength": 1 + }, + "nullable_nonempty_string": { + "oneOf": [ + {"$ref": "#/$defs/nonempty_string"}, + {"type": "null"} + ] + }, + "path_ref": { + "type": "string", + "minLength": 1, + "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*[\\x00-\\x1f]).+$" + }, + "string_set": { + "type": "array", + "items": {"$ref": "#/$defs/nonempty_string"}, + "uniqueItems": true + }, + "nonempty_string_set": { + "type": "array", + "items": {"$ref": "#/$defs/nonempty_string"}, + "minItems": 1, + "uniqueItems": true + }, + "atomic_claim_id": { + "type": "string", + "pattern": "^AC-[a-f0-9]{16,64}$" + }, + "claim_group_id": { + "type": "string", + "pattern": "^CG-[a-f0-9]{16,64}$" + }, + "case_type_id": { + "type": "string", + "pattern": "^CT-[A-Za-z0-9._:-]{1,120}$" + }, + "sub_rule_id": { + "type": "string", + "pattern": "^SR-[A-Za-z0-9._:-]{1,160}$" + }, + "rule_branch_key": { + "type": "string", + "pattern": "^CT-[A-Za-z0-9._:-]{1,120}::SR-[A-Za-z0-9._:-]{1,160}$" + }, + "issue_code_set": { + "type": "array", + "items": {"$ref": "#/$defs/nonempty_string"}, + "uniqueItems": true + }, + "source_ref": { + "type": "object", + "additionalProperties": false, + "required": ["path", "sha256", "json_pointer"], + "properties": { + "path": {"$ref": "#/$defs/path_ref"}, + "sha256": {"$ref": "#/$defs/sha256"}, + "json_pointer": { + "type": "string", + "pattern": "^(?:/(?:[^~/]|~0|~1)*)*$" + } + } + }, + "target_signature": { + "type": "object", + "additionalProperties": false, + "required": [ + "claim_family", + "legal_effect", + "performance_kind", + "object_kind", + "party_role_pattern", + "source_obligation_kind", + "remedy_mode", + "registry_action", + "procedural_posture", + "special_statute_tags" + ], + "properties": { + "claim_family": {"$ref": "#/$defs/nonempty_string"}, + "legal_effect": {"$ref": "#/$defs/nonempty_string"}, + "performance_kind": {"$ref": "#/$defs/nonempty_string"}, + "object_kind": {"$ref": "#/$defs/nonempty_string"}, + "party_role_pattern": {"$ref": "#/$defs/nonempty_string"}, + "source_obligation_kind": {"$ref": "#/$defs/nonempty_string"}, + "remedy_mode": {"$ref": "#/$defs/nonempty_string"}, + "registry_action": {"$ref": "#/$defs/nonempty_string"}, + "procedural_posture": {"$ref": "#/$defs/nonempty_string"}, + "special_statute_tags": {"$ref": "#/$defs/string_set"} + } + }, + "predicate_value": { + "oneOf": [ + {"$ref": "#/$defs/nonempty_string"}, + {"$ref": "#/$defs/nonempty_string_set"}, + {"type": "boolean"} + ] + }, + "predicate_clause": { + "type": "object", + "additionalProperties": false, + "required": ["field", "operator", "value"], + "properties": { + "field": { + "enum": [ + "claim_family", + "legal_effect", + "performance_kind", + "object_kind", + "party_role_pattern", + "source_obligation_kind", + "remedy_mode", + "registry_action", + "procedural_posture", + "special_statute_tags" + ] + }, + "operator": {"enum": ["EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"]}, + "value": {"$ref": "#/$defs/predicate_value"} + } + }, + "closed_predicate": { + "type": "object", + "additionalProperties": false, + "required": ["predicate_id", "clauses", "priority", "legal_review_status", "legal_review_receipt_ref", "legal_review_receipt_sha256"], + "properties": { + "predicate_id": {"$ref": "#/$defs/nonempty_string"}, + "clauses": { + "type": "array", + "items": {"$ref": "#/$defs/predicate_clause"}, + "minItems": 1 + }, + "priority": {"type": "integer", "minimum": 0}, + "legal_review_status": {"const": "APPROVED"}, + "legal_review_receipt_ref": {"$ref": "#/$defs/path_ref"}, + "legal_review_receipt_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "binding_signature_projection": { + "type": "object", + "additionalProperties": false, + "required": [ + "projection_rule_id", + "target_field", + "source_fields", + "derivation_operator", + "allowed_output_values", + "unknown_value", + "implementation_status", + "semantic_review_status" + ], + "properties": { + "projection_rule_id": {"$ref": "#/$defs/nonempty_string"}, + "target_field": { + "enum": [ + "claim_family", + "legal_effect", + "performance_kind", + "object_kind", + "party_role_pattern", + "source_obligation_kind", + "remedy_mode", + "registry_action", + "procedural_posture", + "special_statute_tags" + ] + }, + "source_fields": { + "type": "array", + "items": { + "enum": [ + "legal_basis_family", + "right_holder_refs", + "obligor_refs", + "performance_kind", + "object_refs", + "legal_effect", + "source_occurrence_refs", + "c20_remedy_projection", + "c20_dependency_projection", + "c21_party_projection", + "c21_object_projection" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "derivation_operator": { + "enum": ["DIRECT_COPY", "CLOSED_LOOKUP", "SET_INTERSECTION", "SET_UNION", "BOOLEAN_CLASSIFICATION"] + }, + "allowed_output_values": {"$ref": "#/$defs/string_set"}, + "unknown_value": {"const": "UNKNOWN"}, + "implementation_status": {"enum": ["IMPLEMENTED", "PENDING_IMPLEMENTATION"]}, + "semantic_review_status": {"enum": ["APPROVED", "PENDING_LEGAL_REVIEW"]} + } + }, + "binding_signature_projection_registry": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "registry_id", + "source_schema_ref", + "source_field_allowlist", + "target_field_order", + "projection_rules", + "free_text_fallback_allowed", + "fuzzy_matching_allowed", + "registry_status" + ], + "properties": { + "schema_version": {"const": "stage2_binding_signature_projection_registry.v1"}, + "registry_id": {"const": "S2_20_BINDING_SIGNATURE_PROJECTION_V1"}, + "source_schema_ref": {"const": "schemas/domain_verdict.schema.json#/$defs/normalized_claim_signature"}, + "source_field_allowlist": { + "type": "array", + "prefixItems": [ + {"const": "legal_basis_family"}, + {"const": "right_holder_refs"}, + {"const": "obligor_refs"}, + {"const": "performance_kind"}, + {"const": "object_refs"}, + {"const": "legal_effect"}, + {"const": "source_occurrence_refs"} + ], + "items": false, + "minItems": 7, + "maxItems": 7 + }, + "target_field_order": { + "type": "array", + "prefixItems": [ + {"const": "claim_family"}, + {"const": "legal_effect"}, + {"const": "performance_kind"}, + {"const": "object_kind"}, + {"const": "party_role_pattern"}, + {"const": "source_obligation_kind"}, + {"const": "remedy_mode"}, + {"const": "registry_action"}, + {"const": "procedural_posture"}, + {"const": "special_statute_tags"} + ], + "items": false, + "minItems": 10, + "maxItems": 10 + }, + "projection_rules": { + "type": "array", + "items": {"$ref": "#/$defs/binding_signature_projection"} + }, + "free_text_fallback_allowed": {"const": false}, + "fuzzy_matching_allowed": {"const": false}, + "registry_status": {"enum": ["PENDING_LEGAL_REVIEW", "APPROVED"]} + } + }, + "option_disposition_rule": { + "type": "object", + "additionalProperties": false, + "required": [ + "rule_id", + "legal_decision", + "client_instruction", + "precondition_status", + "compatibility_status", + "result_disposition", + "reason_code" + ], + "properties": { + "rule_id": {"$ref": "#/$defs/nonempty_string"}, + "legal_decision": {"enum": ["SUPPORTED", "CONDITIONAL", "UNRESOLVED", "EXCLUDED"]}, + "client_instruction": {"enum": ["PURSUE", "NO_SUE", "UNSPECIFIED"]}, + "precondition_status": {"enum": ["SATISFIED", "UNRESOLVED", "FAILED", "NOT_APPLICABLE"]}, + "compatibility_status": {"enum": ["COMPATIBLE", "INCOMPATIBLE", "UNRESOLVED"]}, + "result_disposition": {"enum": ["SELECTED", "ALTERNATIVE", "EXCLUDED", "DEFERRED", "POLICY_GAP"]}, + "reason_code": {"$ref": "#/$defs/nonempty_string"} + } + }, + "option_disposition_policy": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "policy_id", + "precedence_order", + "rules", + "exhaustive_cross_product_required", + "exactly_one_match_required", + "hidden_default_allowed", + "policy_status" + ], + "properties": { + "schema_version": {"const": "stage2_option_disposition_policy.v1"}, + "policy_id": {"const": "S2_20_OPTION_DISPOSITION_POLICY_V1"}, + "precedence_order": { + "type": "array", + "prefixItems": [ + {"const": "LEGAL_EXCLUSION"}, + {"const": "CLIENT_NO_SUE"}, + {"const": "PRECONDITION"}, + {"const": "COMPATIBILITY"}, + {"const": "LEGAL_DECISION"} + ], + "items": false, + "minItems": 5, + "maxItems": 5 + }, + "rules": {"type": "array", "items": {"$ref": "#/$defs/option_disposition_rule"}}, + "exhaustive_cross_product_required": {"const": true}, + "exactly_one_match_required": {"const": true}, + "hidden_default_allowed": {"const": false}, + "policy_status": {"enum": ["PENDING_LEGAL_REVIEW", "APPROVED"]} + }, + "allOf": [ + { + "if": {"properties": {"policy_status": {"const": "APPROVED"}}, "required": ["policy_status"]}, + "then": {"properties": {"rules": {"minItems": 1}}} + } + ] + }, + "party_set_rule": { + "type": "object", + "additionalProperties": false, + "required": [ + "party_set_rule_id", + "applicability_predicates", + "required_role_tokens", + "allowed_liability_topologies", + "identity_resolution_required", + "legal_content_status", + "legal_review_receipt_ref", + "legal_review_receipt_sha256" + ], + "properties": { + "party_set_rule_id": {"$ref": "#/$defs/nonempty_string"}, + "applicability_predicates": {"type": "array", "items": {"$ref": "#/$defs/predicate_clause"}}, + "required_role_tokens": {"$ref": "#/$defs/string_set"}, + "allowed_liability_topologies": {"$ref": "#/$defs/string_set"}, + "identity_resolution_required": {"type": "boolean"}, + "legal_content_status": {"enum": ["PENDING_LEGAL_CONTENT", "APPROVED", "REJECTED"]}, + "legal_review_receipt_ref": { + "oneOf": [ + {"$ref": "#/$defs/path_ref"}, + {"type": "null"} + ] + }, + "legal_review_receipt_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + } + }, + "allOf": [ + { + "if": { + "properties": {"legal_content_status": {"const": "PENDING_LEGAL_CONTENT"}}, + "required": ["legal_content_status"] + }, + "then": { + "properties": { + "applicability_predicates": {"maxItems": 0}, + "required_role_tokens": {"maxItems": 0}, + "allowed_liability_topologies": {"maxItems": 0}, + "legal_review_receipt_ref": {"type": "null"}, + "legal_review_receipt_sha256": {"type": "null"} + } + } + }, + { + "if": { + "properties": {"legal_content_status": {"const": "APPROVED"}}, + "required": ["legal_content_status"] + }, + "then": { + "properties": { + "applicability_predicates": {"minItems": 1}, + "required_role_tokens": {"minItems": 1}, + "legal_review_receipt_ref": {"$ref": "#/$defs/path_ref"}, + "legal_review_receipt_sha256": {"$ref": "#/$defs/sha256"} + } + } + } + ] + }, + "party_set_rules": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "registry_id", "rules", "exactly_one_match_required", "fallback_allowed", "registry_status"], + "properties": { + "schema_version": {"const": "stage2_party_set_rules.v1"}, + "registry_id": {"const": "S2_20_PARTY_SET_RULES_V1"}, + "rules": {"type": "array", "items": {"$ref": "#/$defs/party_set_rule"}}, + "exactly_one_match_required": {"const": true}, + "fallback_allowed": {"const": false}, + "registry_status": {"enum": ["PENDING_LEGAL_CONTENT", "APPROVED"]} + }, + "allOf": [ + { + "if": {"properties": {"registry_status": {"const": "APPROVED"}}, "required": ["registry_status"]}, + "then": {"properties": {"rules": {"minItems": 1}}} + } + ] + }, + "case_type_catalog_row": { + "type": "object", + "additionalProperties": false, + "required": [ + "catalog_row_id", + "source_ordinal", + "source_label", + "case_type_id", + "row_kind", + "claim_capable_disposition", + "case_type_predicates", + "companion_case_type_ids", + "legal_classification_status", + "legal_content_status", + "legal_review_receipt_ref", + "legal_review_receipt_sha256" + ], + "properties": { + "catalog_row_id": {"$ref": "#/$defs/nonempty_string"}, + "source_ordinal": {"type": "integer", "minimum": 1, "maximum": 137}, + "source_label": {"$ref": "#/$defs/nonempty_string"}, + "case_type_id": {"$ref": "#/$defs/case_type_id"}, + "row_kind": {"enum": ["CLAIM", "DOCTRINE_TOPIC", "GROUP", "GENERIC", "PENDING_LEGAL_CLASSIFICATION"]}, + "claim_capable_disposition": {"enum": ["CLAIM_CAPABLE", "COMPANION_ONLY", "CLASSIFICATION_ONLY", "PENDING_LEGAL_CONTENT"]}, + "case_type_predicates": {"type": "array", "items": {"$ref": "#/$defs/closed_predicate"}, "maxItems": 1}, + "companion_case_type_ids": {"type": "array", "items": {"$ref": "#/$defs/case_type_id"}, "uniqueItems": true}, + "legal_classification_status": {"enum": ["PENDING_LEGAL_CLASSIFICATION", "APPROVED"]}, + "legal_content_status": {"enum": ["PENDING_LEGAL_CONTENT", "APPROVED", "REJECTED"]}, + "legal_review_receipt_ref": { + "oneOf": [ + {"$ref": "#/$defs/path_ref"}, + {"type": "null"} + ] + }, + "legal_review_receipt_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + } + }, + "allOf": [ + { + "if": { + "anyOf": [ + {"properties": {"row_kind": {"const": "PENDING_LEGAL_CLASSIFICATION"}}, "required": ["row_kind"]}, + {"properties": {"claim_capable_disposition": {"const": "PENDING_LEGAL_CONTENT"}}, "required": ["claim_capable_disposition"]}, + {"properties": {"legal_classification_status": {"const": "PENDING_LEGAL_CLASSIFICATION"}}, "required": ["legal_classification_status"]}, + {"properties": {"legal_content_status": {"const": "PENDING_LEGAL_CONTENT"}}, "required": ["legal_content_status"]} + ] + }, + "then": { + "properties": { + "case_type_predicates": {"maxItems": 0}, + "legal_review_receipt_ref": {"type": "null"}, + "legal_review_receipt_sha256": {"type": "null"} + } + } + }, + { + "if": { + "properties": {"legal_classification_status": {"const": "APPROVED"}}, + "required": ["legal_classification_status"] + }, + "then": { + "properties": {"case_type_predicates": {"minItems": 1, "maxItems": 1}} + } + }, + { + "if": { + "properties": {"claim_capable_disposition": {"const": "CLAIM_CAPABLE"}}, + "required": ["claim_capable_disposition"] + }, + "then": { + "properties": { + "row_kind": {"enum": ["CLAIM", "GENERIC"]}, + "case_type_predicates": {"minItems": 1}, + "legal_classification_status": {"const": "APPROVED"}, + "legal_content_status": {"const": "APPROVED"}, + "legal_review_receipt_ref": {"$ref": "#/$defs/path_ref"}, + "legal_review_receipt_sha256": {"$ref": "#/$defs/sha256"} + } + } + } + ] + }, + "case_type_registry": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "registry_id", + "catalog_source_ref", + "catalog_source_sha256", + "catalog_row_ids", + "rows", + "claim_capable_case_type_ids", + "pending_legal_classification_case_type_ids", + "fuzzy_match_allowed", + "catch_all_fallback_allowed", + "registry_status" + ], + "properties": { + "schema_version": {"const": "stage2_case_type_registry.v1"}, + "registry_id": {"const": "STAGE2_CASE_TYPE_137_V1"}, + "catalog_source_ref": {"$ref": "#/$defs/path_ref"}, + "catalog_source_sha256": {"$ref": "#/$defs/sha256"}, + "catalog_row_ids": {"type": "array", "items": {"$ref": "#/$defs/nonempty_string"}, "minItems": 137, "maxItems": 137, "uniqueItems": true}, + "rows": {"type": "array", "items": {"$ref": "#/$defs/case_type_catalog_row"}, "minItems": 137, "maxItems": 137}, + "claim_capable_case_type_ids": {"type": "array", "items": {"$ref": "#/$defs/case_type_id"}, "uniqueItems": true}, + "pending_legal_classification_case_type_ids": {"type": "array", "items": {"$ref": "#/$defs/case_type_id"}, "uniqueItems": true}, + "fuzzy_match_allowed": {"const": false}, + "catch_all_fallback_allowed": {"const": false}, + "registry_status": {"enum": ["PENDING_LEGAL_CLASSIFICATION", "PARTIALLY_APPROVED", "APPROVED"]} + }, + "x-cross-field-invariants": [ + "catalog_row_ids equals rows.catalog_row_id in source ordinal order", + "source ordinals are exactly 1 through 137", + "claim_capable_case_type_ids contains only approved CLAIM rows or approved GENERIC rows with approved closed predicates", + "pending rows are never executable binding candidates" + ] + }, + "rule_branch": { + "type": "object", + "additionalProperties": false, + "required": [ + "sub_rule_id", + "sub_rule_predicates", + "rule_branch_key", + "structured_relief_rule_id", + "markdown_source_path", + "markdown_source_sha256", + "renderer_ids", + "required_slot_ids", + "corpus_scope_id", + "element_set_id", + "ce13_branch_id", + "review_policy_id", + "party_set_rule_id", + "legal_content_status", + "legal_review_receipt_ref", + "legal_review_receipt_sha256" + ], + "properties": { + "sub_rule_id": {"$ref": "#/$defs/sub_rule_id"}, + "sub_rule_predicates": {"type": "array", "items": {"$ref": "#/$defs/closed_predicate"}, "minItems": 1, "maxItems": 1}, + "rule_branch_key": {"$ref": "#/$defs/rule_branch_key"}, + "structured_relief_rule_id": {"$ref": "#/$defs/nonempty_string"}, + "markdown_source_path": {"$ref": "#/$defs/path_ref"}, + "markdown_source_sha256": {"$ref": "#/$defs/sha256"}, + "renderer_ids": {"$ref": "#/$defs/nonempty_string_set"}, + "required_slot_ids": {"$ref": "#/$defs/nonempty_string_set"}, + "corpus_scope_id": {"$ref": "#/$defs/nonempty_string"}, + "element_set_id": {"$ref": "#/$defs/nonempty_string"}, + "ce13_branch_id": {"$ref": "#/$defs/nonempty_string"}, + "review_policy_id": {"$ref": "#/$defs/nonempty_string"}, + "party_set_rule_id": {"$ref": "#/$defs/nonempty_string"}, + "legal_content_status": {"const": "APPROVED"}, + "legal_review_receipt_ref": {"$ref": "#/$defs/path_ref"}, + "legal_review_receipt_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "case_type_rule_row": { + "type": "object", + "additionalProperties": false, + "required": [ + "case_type_id", + "claim_capable_disposition", + "legal_classification_status", + "legal_content_status", + "rule_branches", + "non_claim_disposition", + "companion_case_type_ids", + "legal_review_receipt_ref", + "legal_review_receipt_sha256" + ], + "properties": { + "case_type_id": {"$ref": "#/$defs/case_type_id"}, + "claim_capable_disposition": {"enum": ["CLAIM_CAPABLE", "COMPANION_ONLY", "CLASSIFICATION_ONLY", "PENDING_LEGAL_CONTENT"]}, + "legal_classification_status": {"enum": ["PENDING_LEGAL_CLASSIFICATION", "APPROVED"]}, + "legal_content_status": {"enum": ["PENDING_LEGAL_CONTENT", "APPROVED", "REJECTED"]}, + "rule_branches": {"type": "array", "items": {"$ref": "#/$defs/rule_branch"}}, + "non_claim_disposition": { + "oneOf": [ + {"enum": ["COMPANION_ONLY", "CLASSIFICATION_ONLY"]}, + {"type": "null"} + ] + }, + "companion_case_type_ids": {"type": "array", "items": {"$ref": "#/$defs/case_type_id"}, "uniqueItems": true}, + "legal_review_receipt_ref": { + "oneOf": [ + {"$ref": "#/$defs/path_ref"}, + {"type": "null"} + ] + }, + "legal_review_receipt_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + } + }, + "allOf": [ + { + "if": { + "anyOf": [ + {"properties": {"legal_classification_status": {"const": "PENDING_LEGAL_CLASSIFICATION"}}, "required": ["legal_classification_status"]}, + {"properties": {"legal_content_status": {"const": "PENDING_LEGAL_CONTENT"}}, "required": ["legal_content_status"]}, + {"properties": {"claim_capable_disposition": {"const": "PENDING_LEGAL_CONTENT"}}, "required": ["claim_capable_disposition"]} + ] + }, + "then": { + "properties": { + "rule_branches": {"maxItems": 0}, + "legal_review_receipt_ref": {"type": "null"}, + "legal_review_receipt_sha256": {"type": "null"} + } + } + }, + { + "if": { + "properties": {"claim_capable_disposition": {"const": "CLAIM_CAPABLE"}}, + "required": ["claim_capable_disposition"] + }, + "then": { + "properties": { + "legal_classification_status": {"const": "APPROVED"}, + "legal_content_status": {"const": "APPROVED"}, + "rule_branches": {"minItems": 1}, + "non_claim_disposition": {"type": "null"}, + "legal_review_receipt_ref": {"$ref": "#/$defs/path_ref"}, + "legal_review_receipt_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "else": { + "if": { + "properties": { + "claim_capable_disposition": {"enum": ["COMPANION_ONLY", "CLASSIFICATION_ONLY"]}, + "legal_content_status": {"const": "APPROVED"} + }, + "required": ["claim_capable_disposition", "legal_content_status"] + }, + "then": { + "properties": { + "rule_branches": {"maxItems": 0}, + "non_claim_disposition": {"enum": ["COMPANION_ONLY", "CLASSIFICATION_ONLY"]}, + "legal_review_receipt_ref": {"$ref": "#/$defs/path_ref"}, + "legal_review_receipt_sha256": {"$ref": "#/$defs/sha256"} + } + } + } + } + ] + }, + "case_type_rule_registry": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "registry_id", + "case_type_registry_ref", + "case_type_registry_sha256", + "rows", + "runtime_branch_selector", + "fuzzy_fallback_allowed", + "nearest_rule_fallback_allowed", + "registry_status" + ], + "properties": { + "schema_version": {"const": "stage2_case_type_rule_registry.v1"}, + "registry_id": {"const": "STAGE2_CASE_TYPE_RULE_REGISTRY_V1"}, + "case_type_registry_ref": {"const": "manifest/case_type_registry.yml"}, + "case_type_registry_sha256": {"$ref": "#/$defs/sha256"}, + "rows": {"type": "array", "items": {"$ref": "#/$defs/case_type_rule_row"}, "minItems": 137, "maxItems": 137}, + "runtime_branch_selector": {"const": "EXACT_TARGET_SIGNATURE_TO_CASE_TYPE_ID_TO_SUB_RULE_ID"}, + "fuzzy_fallback_allowed": {"const": false}, + "nearest_rule_fallback_allowed": {"const": false}, + "registry_status": {"enum": ["PENDING_LEGAL_CONTENT", "PARTIALLY_APPROVED", "APPROVED"]} + } + }, + "case_type_binding": { + "type": "object", + "additionalProperties": false, + "required": [ + "atomic_claim_id", + "target_signature", + "target_signature_sha256", + "projection_rule_ids", + "case_type_binding_status", + "matched_case_type_predicate_ids", + "case_type_id", + "sub_rule_binding_status", + "matched_sub_rule_predicate_ids", + "sub_rule_id", + "rule_branch_key", + "renderer_ids", + "corpus_scope_id", + "element_set_id", + "party_set_rule_id", + "ce13_branch_id", + "legal_content_status", + "binding_issue_codes" + ], + "properties": { + "atomic_claim_id": {"$ref": "#/$defs/atomic_claim_id"}, + "target_signature": {"$ref": "#/$defs/target_signature"}, + "target_signature_sha256": {"$ref": "#/$defs/sha256"}, + "projection_rule_ids": {"$ref": "#/$defs/nonempty_string_set"}, + "case_type_binding_status": {"enum": ["BOUND", "UNRESOLVED", "CONFLICT"]}, + "matched_case_type_predicate_ids": {"$ref": "#/$defs/string_set"}, + "case_type_id": { + "oneOf": [ + {"$ref": "#/$defs/case_type_id"}, + {"type": "null"} + ] + }, + "sub_rule_binding_status": {"enum": ["BOUND", "UNRESOLVED", "CONFLICT", "NOT_RUN"]}, + "matched_sub_rule_predicate_ids": {"$ref": "#/$defs/string_set"}, + "sub_rule_id": { + "oneOf": [ + {"$ref": "#/$defs/sub_rule_id"}, + {"type": "null"} + ] + }, + "rule_branch_key": { + "oneOf": [ + {"$ref": "#/$defs/rule_branch_key"}, + {"type": "null"} + ] + }, + "renderer_ids": {"$ref": "#/$defs/string_set"}, + "corpus_scope_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "element_set_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "party_set_rule_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "ce13_branch_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "legal_content_status": {"enum": ["APPROVED", "PENDING_LEGAL_CLASSIFICATION", "PENDING_LEGAL_CONTENT", "NOT_APPLICABLE"]}, + "binding_issue_codes": {"$ref": "#/$defs/issue_code_set"} + }, + "allOf": [ + { + "if": { + "properties": { + "case_type_binding_status": {"const": "BOUND"}, + "sub_rule_binding_status": {"const": "BOUND"} + }, + "required": ["case_type_binding_status", "sub_rule_binding_status"] + }, + "then": { + "properties": { + "matched_case_type_predicate_ids": {"minItems": 1, "maxItems": 1}, + "case_type_id": {"$ref": "#/$defs/case_type_id"}, + "matched_sub_rule_predicate_ids": {"minItems": 1, "maxItems": 1}, + "sub_rule_id": {"$ref": "#/$defs/sub_rule_id"}, + "rule_branch_key": {"$ref": "#/$defs/rule_branch_key"}, + "renderer_ids": {"minItems": 1}, + "corpus_scope_id": {"$ref": "#/$defs/nonempty_string"}, + "element_set_id": {"$ref": "#/$defs/nonempty_string"}, + "party_set_rule_id": {"$ref": "#/$defs/nonempty_string"}, + "ce13_branch_id": {"$ref": "#/$defs/nonempty_string"}, + "legal_content_status": {"const": "APPROVED"}, + "binding_issue_codes": {"maxItems": 0} + } + }, + "else": { + "properties": {"binding_issue_codes": {"minItems": 1}} + } + } + ] + }, + "case_type_bindings_document": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "run_binding_digest", + "case_type_registry_sha256", + "case_type_rule_registry_sha256", + "binding_signature_projection_sha256", + "bindings", + "bound_atomic_claim_ids", + "unresolved_atomic_claim_ids", + "conflict_atomic_claim_ids", + "document_digest" + ], + "properties": { + "schema_version": {"const": "stage2_case_type_bindings.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "case_type_registry_sha256": {"$ref": "#/$defs/sha256"}, + "case_type_rule_registry_sha256": {"$ref": "#/$defs/sha256"}, + "binding_signature_projection_sha256": {"$ref": "#/$defs/sha256"}, + "bindings": {"type": "array", "items": {"$ref": "#/$defs/case_type_binding"}}, + "bound_atomic_claim_ids": {"type": "array", "items": {"$ref": "#/$defs/atomic_claim_id"}, "uniqueItems": true}, + "unresolved_atomic_claim_ids": {"type": "array", "items": {"$ref": "#/$defs/atomic_claim_id"}, "uniqueItems": true}, + "conflict_atomic_claim_ids": {"type": "array", "items": {"$ref": "#/$defs/atomic_claim_id"}, "uniqueItems": true}, + "document_digest": {"$ref": "#/$defs/sha256"} + } + }, + "corpus_scope_row": { + "type": "object", + "additionalProperties": false, + "required": [ + "corpus_scope_id", + "case_type_id", + "sub_rule_ids", + "element_set_id", + "corpus_element_ids", + "slot_crosswalk_rows", + "approval_status", + "legal_review_receipt_ref", + "legal_review_receipt_sha256" + ], + "properties": { + "corpus_scope_id": {"$ref": "#/$defs/nonempty_string"}, + "case_type_id": {"$ref": "#/$defs/case_type_id"}, + "sub_rule_ids": {"type": "array", "items": {"$ref": "#/$defs/sub_rule_id"}, "uniqueItems": true}, + "element_set_id": {"$ref": "#/$defs/nonempty_string"}, + "corpus_element_ids": {"$ref": "#/$defs/string_set"}, + "slot_crosswalk_rows": { + "type": "array", + "items": {"$ref": "#/$defs/slot_crosswalk_row"} + }, + "approval_status": {"enum": ["PENDING_LEGAL_CONTENT", "APPROVED", "REJECTED"]}, + "legal_review_receipt_ref": { + "oneOf": [ + {"$ref": "#/$defs/path_ref"}, + {"type": "null"} + ] + }, + "legal_review_receipt_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + } + }, + "allOf": [ + { + "if": { + "properties": {"approval_status": {"const": "PENDING_LEGAL_CONTENT"}}, + "required": ["approval_status"] + }, + "then": { + "properties": { + "sub_rule_ids": {"maxItems": 0}, + "corpus_element_ids": {"maxItems": 0}, + "slot_crosswalk_rows": {"maxItems": 0}, + "legal_review_receipt_ref": {"type": "null"}, + "legal_review_receipt_sha256": {"type": "null"} + } + } + } + ] + }, + "slot_crosswalk_row": { + "type": "object", + "additionalProperties": false, + "required": ["corpus_element_id", "doctrine_role", "stage1_slot_refs", "mapping_status", "source_refs"], + "properties": { + "corpus_element_id": {"$ref": "#/$defs/nonempty_string"}, + "doctrine_role": {"enum": ["element", "defense", "rebuttal", "burden", "relief_consistency"]}, + "stage1_slot_refs": {"$ref": "#/$defs/string_set"}, + "mapping_status": {"enum": ["EXACT", "CONTEXT_ONLY", "CONFLICT", "UNMAPPED"]}, + "source_refs": {"type": "array", "items": {"$ref": "#/$defs/source_ref"}} + } + }, + "requirement_fact_scope_registry": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "registry_id", "rows", "registry_status", "fuzzy_fallback_allowed"], + "properties": { + "schema_version": {"const": "stage2_requirement_fact_scope_registry.v1"}, + "registry_id": {"const": "STAGE2_REQUIREMENT_FACT_SCOPE_V1"}, + "rows": {"type": "array", "items": {"$ref": "#/$defs/corpus_scope_row"}}, + "registry_status": {"enum": ["PENDING_LEGAL_CONTENT", "PARTIALLY_APPROVED", "APPROVED"]}, + "fuzzy_fallback_allowed": {"const": false} + }, + "allOf": [ + { + "if": {"properties": {"registry_status": {"const": "APPROVED"}}, "required": ["registry_status"]}, + "then": {"properties": {"rows": {"minItems": 1}}} + } + ] + }, + "rule_context_pack": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "run_binding_digest", + "claim_group_id", + "binding_rows", + "structured_relief_rule_refs", + "markdown_source_refs", + "renderer_refs", + "party_set_rule_refs", + "ce13_branch_refs", + "review_policy_refs", + "authority_class", + "pack_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_rule_context_pack.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "claim_group_id": {"$ref": "#/$defs/claim_group_id"}, + "binding_rows": {"type": "array", "items": {"$ref": "#/$defs/case_type_binding"}}, + "structured_relief_rule_refs": {"type": "array", "items": {"$ref": "#/$defs/source_ref"}}, + "markdown_source_refs": {"type": "array", "items": {"$ref": "#/$defs/source_ref"}}, + "renderer_refs": {"type": "array", "items": {"$ref": "#/$defs/source_ref"}}, + "party_set_rule_refs": {"type": "array", "items": {"$ref": "#/$defs/source_ref"}}, + "ce13_branch_refs": {"type": "array", "items": {"$ref": "#/$defs/source_ref"}}, + "review_policy_refs": {"type": "array", "items": {"$ref": "#/$defs/source_ref"}}, + "authority_class": {"const": "RELIEF_ONLY"}, + "pack_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "query_row": { + "type": "object", + "additionalProperties": false, + "required": [ + "query_id", + "atomic_claim_id", + "case_type_id", + "sub_rule_id", + "rule_branch_key", + "corpus_scope_id", + "element_set_id", + "doctrine_role", + "query_status", + "query_text", + "query_sha256", + "filter", + "alpha", + "top_k", + "embedding_configuration_digest", + "reranker_configuration_digest", + "index_configuration_digest", + "expected_corpus_element_ids", + "issue_codes" + ], + "properties": { + "query_id": {"$ref": "#/$defs/nonempty_string"}, + "atomic_claim_id": {"$ref": "#/$defs/atomic_claim_id"}, + "case_type_id": {"$ref": "#/$defs/case_type_id"}, + "sub_rule_id": {"$ref": "#/$defs/sub_rule_id"}, + "rule_branch_key": {"$ref": "#/$defs/rule_branch_key"}, + "corpus_scope_id": {"$ref": "#/$defs/nonempty_string"}, + "element_set_id": {"$ref": "#/$defs/nonempty_string"}, + "doctrine_role": {"enum": ["element", "defense", "rebuttal", "burden", "relief_consistency"]}, + "query_status": {"enum": ["READY", "QUERY_NOT_RUN_UNRESOLVED_BINDING"]}, + "query_text": { + "oneOf": [ + {"$ref": "#/$defs/nonempty_string"}, + {"type": "null"} + ] + }, + "query_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + }, + "filter": { + "oneOf": [ + {"$ref": "../corpus/weaviate_collection.schema.json#/$defs/search_filter"}, + {"type": "null"} + ] + }, + "alpha": {"type": ["number", "null"], "minimum": 0, "maximum": 1}, + "top_k": {"type": ["integer", "null"], "minimum": 1, "maximum": 100}, + "embedding_configuration_digest": { + "oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}] + }, + "reranker_configuration_digest": { + "oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}] + }, + "index_configuration_digest": { + "oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}] + }, + "expected_corpus_element_ids": {"$ref": "#/$defs/string_set"}, + "issue_codes": {"$ref": "#/$defs/issue_code_set"} + }, + "allOf": [ + { + "if": { + "properties": {"query_status": {"const": "QUERY_NOT_RUN_UNRESOLVED_BINDING"}}, + "required": ["query_status"] + }, + "then": { + "properties": { + "query_text": {"type": "null"}, + "query_sha256": {"type": "null"}, + "filter": {"type": "null"}, + "alpha": {"type": "null"}, + "top_k": {"type": "null"}, + "embedding_configuration_digest": {"type": "null"}, + "reranker_configuration_digest": {"type": "null"}, + "index_configuration_digest": {"type": "null"}, + "issue_codes": {"contains": {"const": "QUERY_NOT_RUN_UNRESOLVED_BINDING"}} + } + }, + "else": { + "properties": { + "query_text": {"$ref": "#/$defs/nonempty_string"}, + "query_sha256": {"$ref": "#/$defs/sha256"}, + "filter": {"$ref": "../corpus/weaviate_collection.schema.json#/$defs/search_filter"}, + "alpha": {"type": "number", "minimum": 0, "maximum": 1}, + "top_k": {"type": "integer", "minimum": 1, "maximum": 100}, + "embedding_configuration_digest": {"$ref": "#/$defs/sha256"}, + "reranker_configuration_digest": {"$ref": "#/$defs/sha256"}, + "index_configuration_digest": {"$ref": "#/$defs/sha256"} + } + } + } + ] + }, + "query_plan": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "run_binding_digest", "atomic_claim_id", "rows", "query_plan_sha256"], + "properties": { + "schema_version": {"const": "stage2_requirement_fact_query_plan.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "atomic_claim_id": {"$ref": "#/$defs/atomic_claim_id"}, + "rows": {"type": "array", "items": {"$ref": "#/$defs/query_row"}}, + "query_plan_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "retrieval_hit": { + "$ref": "../corpus/weaviate_collection.schema.json#/$defs/hybrid_hit" + }, + "retrieval_result": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "run_binding_digest", + "atomic_claim_id", + "query_plan_sha256", + "snapshot_id", + "snapshot_sha256", + "result_status", + "selected_hits", + "dropped_hits", + "issue_codes", + "result_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_requirement_fact_retrieval_result.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "atomic_claim_id": {"$ref": "#/$defs/atomic_claim_id"}, + "query_plan_sha256": {"$ref": "#/$defs/sha256"}, + "snapshot_id": {"$ref": "#/$defs/nonempty_string"}, + "snapshot_sha256": {"$ref": "#/$defs/sha256"}, + "result_status": {"enum": ["COMPLETE", "NO_HIT", "NOT_RUN_UNRESOLVED_BINDING", "TECHNICAL_INCOMPLETE"]}, + "selected_hits": {"type": "array", "items": {"$ref": "#/$defs/retrieval_hit"}}, + "dropped_hits": {"type": "array", "items": {"$ref": "../corpus/weaviate_collection.schema.json#/$defs/dropped_hit"}}, + "issue_codes": {"$ref": "#/$defs/issue_code_set"}, + "result_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "retrieval_receipt": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "run_binding_digest", + "atomic_claim_id", + "query_plan_sha256", + "request_sha256", + "response_sha256", + "transport", + "endpoint", + "tool_name", + "collection_name", + "tenant", + "snapshot_sha256", + "filter_sha256", + "configuration_digest", + "attempt_count", + "replay_status", + "read_only_verified", + "credential_ref", + "credential_material_persisted", + "receipt_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_requirement_fact_retrieval_receipt.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "atomic_claim_id": {"$ref": "#/$defs/atomic_claim_id"}, + "query_plan_sha256": {"$ref": "#/$defs/sha256"}, + "request_sha256": {"$ref": "#/$defs/sha256"}, + "response_sha256": {"$ref": "#/$defs/sha256"}, + "transport": {"const": "MCP_OVER_HTTP_JSON_RPC"}, + "endpoint": {"const": "https://weaviate.eroomai.com/mcp"}, + "tool_name": {"const": "search_hybrid"}, + "collection_name": {"$ref": "#/$defs/nonempty_string"}, + "tenant": {"$ref": "#/$defs/nonempty_string"}, + "snapshot_sha256": {"$ref": "#/$defs/sha256"}, + "filter_sha256": {"$ref": "#/$defs/sha256"}, + "configuration_digest": {"$ref": "#/$defs/sha256"}, + "attempt_count": {"type": "integer", "minimum": 1, "maximum": 3}, + "replay_status": {"enum": ["NOT_REQUESTED", "DETERMINISTIC", "DRIFT_DETECTED"]}, + "read_only_verified": {"const": true}, + "credential_ref": {"$ref": "#/$defs/nonempty_string"}, + "credential_material_persisted": {"const": false}, + "receipt_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "requirement_fact_item": { + "type": "object", + "additionalProperties": false, + "required": [ + "corpus_element_id", + "doctrine_role", + "chunk_id", + "chunk_sha256", + "source_document_id", + "source_document_sha256", + "source_locator", + "authority_ids", + "proposition_ids", + "stage1_slot_refs", + "mapping_status", + "fact_creation_allowed", + "legal_decision_override_allowed" + ], + "properties": { + "corpus_element_id": {"$ref": "#/$defs/nonempty_string"}, + "doctrine_role": {"enum": ["element", "defense", "rebuttal", "burden", "relief_consistency"]}, + "chunk_id": {"$ref": "#/$defs/nonempty_string"}, + "chunk_sha256": {"$ref": "#/$defs/sha256"}, + "source_document_id": {"$ref": "#/$defs/nonempty_string"}, + "source_document_sha256": {"$ref": "#/$defs/sha256"}, + "source_locator": {"$ref": "#/$defs/nonempty_string"}, + "authority_ids": {"$ref": "#/$defs/string_set"}, + "proposition_ids": {"$ref": "#/$defs/string_set"}, + "stage1_slot_refs": {"$ref": "#/$defs/string_set"}, + "mapping_status": {"enum": ["EXACT", "CONTEXT_ONLY", "CONFLICT", "UNMAPPED"]}, + "fact_creation_allowed": {"const": false}, + "legal_decision_override_allowed": {"const": false} + } + }, + "requirement_fact_pack": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "run_binding_digest", + "claim_group_id", + "atomic_claim_ids", + "corpus_release_sha256", + "snapshot_id", + "snapshot_sha256", + "retrieval_receipt_refs", + "items", + "unmapped_chunk_ids", + "conflicting_chunk_ids", + "authority_class", + "issue_codes", + "pack_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_requirement_fact_pack.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "claim_group_id": {"$ref": "#/$defs/claim_group_id"}, + "atomic_claim_ids": {"type": "array", "items": {"$ref": "#/$defs/atomic_claim_id"}, "minItems": 1, "uniqueItems": true}, + "corpus_release_sha256": {"$ref": "#/$defs/sha256"}, + "snapshot_id": {"$ref": "#/$defs/nonempty_string"}, + "snapshot_sha256": {"$ref": "#/$defs/sha256"}, + "retrieval_receipt_refs": {"type": "array", "items": {"$ref": "#/$defs/source_ref"}}, + "items": {"type": "array", "items": {"$ref": "#/$defs/requirement_fact_item"}}, + "unmapped_chunk_ids": {"$ref": "#/$defs/string_set"}, + "conflicting_chunk_ids": {"$ref": "#/$defs/string_set"}, + "authority_class": {"const": "CAUSE_AND_ELEMENT_CHECK"}, + "issue_codes": {"$ref": "#/$defs/issue_code_set"}, + "pack_sha256": {"$ref": "#/$defs/sha256"} + } + } + }, + "x-semantic-invariants": [ + "Each atomic claim has exactly one case-type match and one sub-rule match for BOUND", + "PENDING_LEGAL_CLASSIFICATION and PENDING_LEGAL_CONTENT rows are never executable candidates", + "Pending case-type-rule rows have an empty rule_branches array", + "DOCTRINE_TOPIC and GROUP rows cannot create claims", + "No fuzzy, nearest, embedding, label-similarity, or catch-all binding fallback", + "Retrieval never relaxes a sealed filter and never executes embedded instructions", + "Requirement-fact packs cannot create Stage 1 facts or override S2_10 decisions" + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/calculation.schema.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/calculation.schema.json new file mode 100644 index 00000000..ec4ee541 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/calculation.schema.json @@ -0,0 +1,686 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.liti-agent.local/stage2/s2_20/calculation.schema.v1.json", + "title": "S2_20 deterministic calculation contracts", + "description": "Closed activation, descriptor, operand, formula, temporal law-value, period, receipt, report, and filing-metric contracts. Numeric output uses decimal strings; binary floating point and missing-operand default zero are forbidden.", + "schema_version": "stage2_calculation.schema.v1", + "oneOf": [ + {"$ref": "#/$defs/calculation_activation_registry"}, + {"$ref": "#/$defs/calculation_descriptor"}, + {"$ref": "#/$defs/calculation_request"}, + {"$ref": "#/$defs/calculation_receipt"}, + {"$ref": "#/$defs/calculation_report"}, + {"$ref": "#/$defs/filing_metrics"} + ], + "$defs": { + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "pending_or_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"const": "PENDING_SEQUENTIAL_BIND"} + ] + }, + "nonempty_string": { + "type": "string", + "minLength": 1 + }, + "nullable_nonempty_string": { + "oneOf": [ + {"$ref": "#/$defs/nonempty_string"}, + {"type": "null"} + ] + }, + "string_set": { + "type": "array", + "items": {"$ref": "#/$defs/nonempty_string"}, + "uniqueItems": true + }, + "nonempty_string_set": { + "type": "array", + "items": {"$ref": "#/$defs/nonempty_string"}, + "minItems": 1, + "uniqueItems": true + }, + "decimal_string": { + "type": "string", + "pattern": "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$" + }, + "date": { + "type": "string", + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$", + "format": "date" + }, + "date_or_null": { + "oneOf": [ + {"$ref": "#/$defs/date"}, + {"type": "null"} + ] + }, + "calculator_id": { + "type": "string", + "pattern": "^CE-(?:0[1-9]|1[0-3]|R[1-4])$" + }, + "artifact_ref": { + "type": "object", + "additionalProperties": false, + "required": ["path", "sha256", "schema_ref"], + "properties": { + "path": {"$ref": "#/$defs/nonempty_string"}, + "sha256": {"$ref": "#/$defs/sha256"}, + "schema_ref": {"$ref": "#/$defs/nonempty_string"} + } + }, + "effective_interval": { + "type": "object", + "additionalProperties": false, + "required": ["effective_from", "effective_to", "temporal_rule_id"], + "properties": { + "effective_from": {"$ref": "#/$defs/date"}, + "effective_to": {"$ref": "#/$defs/date_or_null"}, + "temporal_rule_id": {"$ref": "#/$defs/nonempty_string"} + } + }, + "rounding_rule": { + "type": "object", + "additionalProperties": false, + "required": ["mode", "quantum", "stage"], + "properties": { + "mode": {"enum": ["ROUND_HALF_UP", "ROUND_DOWN", "ROUND_UP", "TRUNCATE"]}, + "quantum": {"$ref": "#/$defs/decimal_string"}, + "stage": {"enum": ["PER_PERIOD", "PER_FORMULA", "FINAL_RESULT"]} + } + }, + "unit_contract_row": { + "type": "object", + "additionalProperties": false, + "required": ["operand_id", "unit_id", "currency"], + "properties": { + "operand_id": {"$ref": "#/$defs/nonempty_string"}, + "unit_id": {"$ref": "#/$defs/nonempty_string"}, + "currency": {"$ref": "#/$defs/nullable_nonempty_string"} + } + }, + "operand_contract": { + "type": "object", + "additionalProperties": false, + "required": ["required_operands", "optional_operands", "unit_contracts", "missing_operand_policy"], + "properties": { + "required_operands": {"$ref": "#/$defs/string_set"}, + "optional_operands": {"$ref": "#/$defs/string_set"}, + "unit_contracts": { + "type": "array", + "items": {"$ref": "#/$defs/unit_contract_row"} + }, + "missing_operand_policy": {"const": "ISSUE_AND_NO_RESULT"} + } + }, + "law_value_contract": { + "type": "object", + "additionalProperties": false, + "required": ["law_value_refs", "literal_output_affecting_legal_numbers_forbidden", "effective_interval_required", "authority_release_binding_required"], + "properties": { + "law_value_refs": {"$ref": "#/$defs/string_set"}, + "literal_output_affecting_legal_numbers_forbidden": {"const": true}, + "effective_interval_required": {"const": true}, + "authority_release_binding_required": {"const": true} + } + }, + "numeric_contract": { + "type": "object", + "additionalProperties": false, + "required": ["decimal_only", "binary_float_forbidden", "rounding_rule_required_per_formula", "explicit_units_required"], + "properties": { + "decimal_only": {"const": true}, + "binary_float_forbidden": {"const": true}, + "rounding_rule_required_per_formula": {"const": true}, + "explicit_units_required": {"const": true} + } + }, + "receipt_contract": { + "type": "object", + "additionalProperties": false, + "required": ["required_fields"], + "properties": { + "required_fields": {"$ref": "#/$defs/nonempty_string_set"} + } + }, + "review_contract": { + "type": "object", + "additionalProperties": false, + "required": ["required_role", "status"], + "properties": { + "required_role": {"const": "KOREAN_LAWYER"}, + "status": {"enum": ["PENDING_KOREAN_LAWYER_REVIEW", "APPROVED", "REJECTED"]} + } + }, + "activation_equals": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "normalized_claim_signature": {"$ref": "#/$defs/nonempty_string"}, + "case_type_id": {"$ref": "#/$defs/nonempty_string"}, + "rule_branch_key": {"$ref": "#/$defs/nonempty_string"}, + "requested_metric": {"$ref": "#/$defs/nonempty_string"} + } + }, + "activation_row": { + "type": "object", + "additionalProperties": false, + "required": ["calculator_id", "requested_metrics", "equals"], + "properties": { + "activation_row_id": {"$ref": "#/$defs/nonempty_string"}, + "calculator_id": {"$ref": "#/$defs/calculator_id"}, + "requested_metrics": {"$ref": "#/$defs/nonempty_string_set"}, + "equals": {"$ref": "#/$defs/activation_equals"} + } + }, + "calculator_descriptor_ref": { + "type": "object", + "additionalProperties": false, + "required": ["calculator_id", "descriptor_path", "activation_status", "status", "execution_eligible"], + "properties": { + "calculator_id": {"$ref": "#/$defs/calculator_id"}, + "descriptor_path": {"type": "string", "pattern": "^registry/calculations/CE-[A-Za-z0-9_-]+\\.yml$"}, + "descriptor_sha256": {"$ref": "#/$defs/sha256"}, + "activation_status": {"enum": ["PENDING_LEGAL_CONTENT", "LEGAL_CONTENT_ADMITTED"]}, + "status": {"enum": ["PENDING_LEGAL_CONTENT", "LEGAL_CONTENT_ADMITTED"]}, + "execution_eligible": {"type": "boolean"} + }, + "allOf": [ + { + "if": {"properties": {"execution_eligible": {"const": true}}, "required": ["execution_eligible"]}, + "then": { + "required": ["descriptor_sha256"], + "properties": { + "activation_status": {"const": "LEGAL_CONTENT_ADMITTED"}, + "status": {"const": "LEGAL_CONTENT_ADMITTED"} + } + } + } + ] + }, + "calculator_descriptor_ref_01": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-01"}}}]}, + "calculator_descriptor_ref_02": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-02"}}}]}, + "calculator_descriptor_ref_03": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-03"}}}]}, + "calculator_descriptor_ref_04": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-04"}}}]}, + "calculator_descriptor_ref_05": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-05"}}}]}, + "calculator_descriptor_ref_06": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-06"}}}]}, + "calculator_descriptor_ref_07": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-07"}}}]}, + "calculator_descriptor_ref_08": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-08"}}}]}, + "calculator_descriptor_ref_09": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-09"}}}]}, + "calculator_descriptor_ref_10": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-10"}}}]}, + "calculator_descriptor_ref_11": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-11"}}}]}, + "calculator_descriptor_ref_12": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-12"}}}]}, + "calculator_descriptor_ref_13": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-13"}}}]}, + "calculator_descriptor_ref_r1": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-R1"}}}]}, + "calculator_descriptor_ref_r2": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-R2"}}}]}, + "calculator_descriptor_ref_r3": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-R3"}}}]}, + "calculator_descriptor_ref_r4": {"allOf": [{"$ref": "#/$defs/calculator_descriptor_ref"}, {"properties": {"calculator_id": {"const": "CE-R4"}}}]}, + "activation_row_contract": { + "type": "object", + "additionalProperties": false, + "required": ["required_fields"], + "properties": { + "required_fields": { + "type": "array", + "prefixItems": [ + {"const": "calculator_id"}, + {"const": "requested_metrics"}, + {"const": "equals"} + ], + "items": false + } + } + }, + "activation_closure": { + "type": "object", + "additionalProperties": false, + "required": ["exactly_one_state_per_calculator_required", "activated_requires_nonempty_approved_predicate", "activated_requires_release_selected_descriptor_hash", "activated_requires_receipt", "zero_match_issue_code", "multi_match_issue_code", "model_arithmetic_forbidden", "missing_operand_default_zero_forbidden"], + "properties": { + "exactly_one_state_per_calculator_required": {"const": true}, + "activated_requires_nonempty_approved_predicate": {"const": true}, + "activated_requires_release_selected_descriptor_hash": {"const": true}, + "activated_requires_receipt": {"const": true}, + "zero_match_issue_code": {"const": "CALCULATION_ACTIVATION_GAP"}, + "multi_match_issue_code": {"const": "CALCULATION_ACTIVATION_CONFLICT"}, + "model_arithmetic_forbidden": {"const": true}, + "missing_operand_default_zero_forbidden": {"const": true} + } + }, + "calculation_activation_registry": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "registry_id", "status", "execution_eligible", "state_enum", "allowed_predicate_inputs", "allowed_predicate_operators", "activation_rows", "activation_row_contract", "calculator_descriptors", "closure", "review"], + "properties": { + "schema_version": {"const": "stage2.s2_20.calculation_activation_registry.v1"}, + "registry_id": {"const": "S2-20-CALCULATION-ACTIVATION"}, + "status": {"enum": ["PENDING_LEGAL_CONTENT", "LEGAL_CONTENT_ADMITTED"]}, + "execution_eligible": {"type": "boolean"}, + "state_enum": { + "type": "array", + "prefixItems": [ + {"const": "ACTIVATED"}, + {"const": "NOT_APPLICABLE"}, + {"const": "ACTIVATION_GAP"}, + {"const": "ACTIVATION_CONFLICT"} + ], + "items": false + }, + "allowed_predicate_inputs": { + "type": "array", + "prefixItems": [ + {"const": "normalized_claim_signature"}, + {"const": "case_type_id"}, + {"const": "rule_branch_key"}, + {"const": "requested_metric"} + ], + "items": false + }, + "allowed_predicate_operators": { + "type": "array", + "prefixItems": [ + {"const": "EQ"}, + {"const": "IN"}, + {"const": "ALL_OF"}, + {"const": "NONE_OF"}, + {"const": "EXISTS"} + ], + "items": false + }, + "activation_rows": {"type": "array", "items": {"$ref": "#/$defs/activation_row"}}, + "activation_row_contract": {"$ref": "#/$defs/activation_row_contract"}, + "calculator_descriptors": { + "type": "array", + "prefixItems": [ + {"$ref": "#/$defs/calculator_descriptor_ref_01"}, + {"$ref": "#/$defs/calculator_descriptor_ref_02"}, + {"$ref": "#/$defs/calculator_descriptor_ref_03"}, + {"$ref": "#/$defs/calculator_descriptor_ref_04"}, + {"$ref": "#/$defs/calculator_descriptor_ref_05"}, + {"$ref": "#/$defs/calculator_descriptor_ref_06"}, + {"$ref": "#/$defs/calculator_descriptor_ref_07"}, + {"$ref": "#/$defs/calculator_descriptor_ref_08"}, + {"$ref": "#/$defs/calculator_descriptor_ref_09"}, + {"$ref": "#/$defs/calculator_descriptor_ref_10"}, + {"$ref": "#/$defs/calculator_descriptor_ref_11"}, + {"$ref": "#/$defs/calculator_descriptor_ref_12"}, + {"$ref": "#/$defs/calculator_descriptor_ref_13"}, + {"$ref": "#/$defs/calculator_descriptor_ref_r1"}, + {"$ref": "#/$defs/calculator_descriptor_ref_r2"}, + {"$ref": "#/$defs/calculator_descriptor_ref_r3"}, + {"$ref": "#/$defs/calculator_descriptor_ref_r4"} + ], + "items": false + }, + "closure": {"$ref": "#/$defs/activation_closure"}, + "review": {"$ref": "#/$defs/review_contract"} + }, + "allOf": [ + { + "if": {"properties": {"execution_eligible": {"const": false}}, "required": ["execution_eligible"]}, + "then": { + "properties": { + "status": {"const": "PENDING_LEGAL_CONTENT"}, + "activation_rows": {"maxItems": 0}, + "calculator_descriptors": { + "not": {"contains": {"properties": {"execution_eligible": {"const": true}}, "required": ["execution_eligible"]}} + }, + "review": {"properties": {"status": {"const": "PENDING_KOREAN_LAWYER_REVIEW"}}} + } + } + }, + { + "if": {"properties": {"execution_eligible": {"const": true}}, "required": ["execution_eligible"]}, + "then": { + "properties": { + "status": {"const": "LEGAL_CONTENT_ADMITTED"}, + "activation_rows": {"minItems": 1}, + "calculator_descriptors": { + "not": {"contains": {"properties": {"execution_eligible": {"const": false}}, "required": ["execution_eligible"]}} + }, + "review": {"properties": {"status": {"const": "APPROVED"}}} + } + } + } + ] + }, + "formula_row": { + "type": "object", + "additionalProperties": false, + "required": ["formula_id", "operator", "input_operand_ids", "law_value_ids", "output_metric_id", "output_unit_id", "rounding_rule", "effective_interval", "formula_sha256"], + "properties": { + "formula_id": {"$ref": "#/$defs/nonempty_string"}, + "operator": {"enum": ["ADD", "SUBTRACT", "MULTIPLY", "DIVIDE", "MIN", "MAX"]}, + "input_operand_ids": {"$ref": "#/$defs/nonempty_string_set"}, + "law_value_ids": {"$ref": "#/$defs/string_set"}, + "output_metric_id": {"$ref": "#/$defs/nonempty_string"}, + "output_unit_id": {"$ref": "#/$defs/nonempty_string"}, + "rounding_rule": {"$ref": "#/$defs/rounding_rule"}, + "effective_interval": {"$ref": "#/$defs/effective_interval"}, + "formula_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "calculation_descriptor": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "calculator_id", "descriptor_id", "description", "status", "execution_eligible", "formula_rows", "operand_contract", "law_value_contract", "numeric_contract", "receipt_contract", "failure_codes", "review"], + "properties": { + "schema_version": {"const": "stage2.s2_20.calculation_descriptor.v1"}, + "calculator_id": {"$ref": "#/$defs/calculator_id"}, + "descriptor_id": {"$ref": "#/$defs/nonempty_string"}, + "descriptor_sha256": {"$ref": "#/$defs/sha256"}, + "description": {"$ref": "#/$defs/nonempty_string"}, + "status": {"enum": ["PENDING_LEGAL_CONTENT", "LEGAL_CONTENT_ADMITTED"]}, + "execution_eligible": {"type": "boolean"}, + "formula_rows": {"type": "array", "items": {"$ref": "#/$defs/formula_row"}}, + "operand_contract": {"$ref": "#/$defs/operand_contract"}, + "law_value_contract": {"$ref": "#/$defs/law_value_contract"}, + "numeric_contract": {"$ref": "#/$defs/numeric_contract"}, + "receipt_contract": {"$ref": "#/$defs/receipt_contract"}, + "failure_codes": {"$ref": "#/$defs/nonempty_string_set"}, + "legal_review_receipt_ref": {"$ref": "#/$defs/nonempty_string"}, + "legal_review_receipt_sha256": {"$ref": "#/$defs/sha256"}, + "review": {"$ref": "#/$defs/review_contract"} + }, + "allOf": [ + { + "if": {"properties": {"execution_eligible": {"const": false}}, "required": ["execution_eligible"]}, + "then": { + "properties": { + "status": {"const": "PENDING_LEGAL_CONTENT"}, + "formula_rows": {"maxItems": 0}, + "review": {"properties": {"status": {"const": "PENDING_KOREAN_LAWYER_REVIEW"}}} + } + } + }, + { + "if": {"properties": {"execution_eligible": {"const": true}}, "required": ["execution_eligible"]}, + "then": { + "required": ["descriptor_sha256", "legal_review_receipt_ref", "legal_review_receipt_sha256"], + "properties": { + "status": {"const": "LEGAL_CONTENT_ADMITTED"}, + "formula_rows": {"minItems": 1}, + "review": {"properties": {"status": {"const": "APPROVED"}}} + } + } + } + ] + }, + "operand_row": { + "type": "object", + "additionalProperties": false, + "required": ["operand_id", "source_ref", "status", "value_type", "value", "unit_id", "currency", "value_sha256"], + "properties": { + "operand_id": {"$ref": "#/$defs/nonempty_string"}, + "source_ref": {"$ref": "#/$defs/nonempty_string"}, + "status": {"enum": ["PRESENT", "MISSING", "CONFLICT"]}, + "value_type": {"enum": ["DECIMAL", "INTEGER", "DATE", "DURATION_DAYS", "RATIO", "BOOLEAN"]}, + "value": { + "oneOf": [ + {"$ref": "#/$defs/decimal_string"}, + {"type": "integer"}, + {"$ref": "#/$defs/date"}, + {"type": "boolean"}, + {"type": "null"} + ] + }, + "unit_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "currency": {"$ref": "#/$defs/nullable_nonempty_string"}, + "value_sha256": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + } + }, + "allOf": [ + { + "if": {"properties": {"status": {"const": "PRESENT"}}, "required": ["status"]}, + "then": {"properties": {"value": {"not": {"type": "null"}}, "value_sha256": {"$ref": "#/$defs/sha256"}}} + }, + { + "if": {"properties": {"status": {"enum": ["MISSING", "CONFLICT"]}}, "required": ["status"]}, + "then": {"properties": {"value": {"type": "null"}, "value_sha256": {"type": "null"}}} + } + ] + }, + "law_value_use": { + "type": "object", + "additionalProperties": false, + "required": ["law_value_id", "authority_id", "authority_release_sha256", "law_value_release_sha256", "effective_interval", "value", "unit_id", "approval_status"], + "properties": { + "law_value_id": {"$ref": "#/$defs/nonempty_string"}, + "authority_id": {"$ref": "#/$defs/nonempty_string"}, + "authority_release_sha256": {"$ref": "#/$defs/sha256"}, + "law_value_release_sha256": {"$ref": "#/$defs/sha256"}, + "effective_interval": {"$ref": "#/$defs/effective_interval"}, + "value": {"$ref": "#/$defs/decimal_string"}, + "unit_id": {"$ref": "#/$defs/nonempty_string"}, + "approval_status": {"const": "APPROVED"} + } + }, + "period_row": { + "type": "object", + "additionalProperties": false, + "required": ["period_id", "start_date", "end_date", "start_inclusive", "end_inclusive", "operand_refs", "law_value_id", "rate_or_value", "unit_id", "subtotal", "result_sha256"], + "properties": { + "period_id": {"$ref": "#/$defs/nonempty_string"}, + "start_date": {"$ref": "#/$defs/date"}, + "end_date": {"$ref": "#/$defs/date"}, + "start_inclusive": {"type": "boolean"}, + "end_inclusive": {"type": "boolean"}, + "operand_refs": {"$ref": "#/$defs/nonempty_string_set"}, + "law_value_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "rate_or_value": {"$ref": "#/$defs/decimal_string"}, + "unit_id": {"$ref": "#/$defs/nonempty_string"}, + "subtotal": {"$ref": "#/$defs/decimal_string"}, + "result_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "calculation_request": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "calculation_request_id", "run_binding_digest", "atomic_claim_id", "claim_group_id", "calculator_id", "case_type_id", "rule_branch_key", "activation_row_id", "descriptor_sha256", "operand_rows", "operand_refs", "requested_metric_ids", "law_value_release_sha256", "input_snapshot_digest", "request_sha256"], + "properties": { + "schema_version": {"const": "stage2_calculation_request.v1"}, + "calculation_request_id": {"$ref": "#/$defs/nonempty_string"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "atomic_claim_id": {"$ref": "#/$defs/nonempty_string"}, + "claim_group_id": {"$ref": "#/$defs/nonempty_string"}, + "calculator_id": {"$ref": "#/$defs/calculator_id"}, + "case_type_id": {"$ref": "#/$defs/nonempty_string"}, + "rule_branch_key": {"$ref": "#/$defs/nonempty_string"}, + "activation_row_id": {"$ref": "#/$defs/nonempty_string"}, + "descriptor_sha256": {"$ref": "#/$defs/sha256"}, + "operand_rows": {"type": "array", "items": {"$ref": "#/$defs/operand_row"}}, + "operand_refs": {"$ref": "#/$defs/string_set"}, + "requested_metric_ids": {"$ref": "#/$defs/nonempty_string_set"}, + "law_value_release_sha256": {"$ref": "#/$defs/sha256"}, + "input_snapshot_digest": {"$ref": "#/$defs/sha256"}, + "request_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "calculation_result": { + "type": "object", + "additionalProperties": false, + "required": ["metric_id", "value", "unit_id", "currency"], + "properties": { + "metric_id": {"$ref": "#/$defs/nonempty_string"}, + "value": {"$ref": "#/$defs/decimal_string"}, + "unit_id": {"$ref": "#/$defs/nonempty_string"}, + "currency": {"$ref": "#/$defs/nullable_nonempty_string"} + } + }, + "calculation_receipt": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "calculation_receipt_id", "run_binding_digest", "atomic_claim_id", "claim_group_id", "calculator_id", "activation_status", "calculation_status", "activation_row_id", "descriptor_sha256", "formula_id", "formula_sha256", "operand_rows", "operand_refs", "units", "law_value_uses", "effective_interval", "rounding_rule", "period_rows", "missing_operands", "result", "result_sha256", "issue_codes", "receipt_sha256"], + "properties": { + "schema_version": {"const": "stage2.calculation_receipt.v1"}, + "calculation_receipt_id": {"type": "string", "pattern": "^CR-[A-Za-z0-9_-]+$"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "atomic_claim_id": {"$ref": "#/$defs/nonempty_string"}, + "claim_group_id": {"$ref": "#/$defs/nonempty_string"}, + "calculator_id": {"$ref": "#/$defs/calculator_id"}, + "activation_status": {"enum": ["ACTIVATED", "NOT_APPLICABLE", "ACTIVATION_GAP", "ACTIVATION_CONFLICT"]}, + "calculation_status": {"enum": ["CALCULATED", "NOT_APPLICABLE", "MISSING_OPERAND", "LAW_VALUE_GAP", "TEMPORAL_GAP", "UNIT_CONFLICT", "ACTIVATION_GAP", "ACTIVATION_CONFLICT"]}, + "activation_row_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "descriptor_sha256": { + "oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}] + }, + "formula_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "formula_sha256": { + "oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}] + }, + "operand_rows": {"type": "array", "items": {"$ref": "#/$defs/operand_row"}}, + "operand_refs": {"$ref": "#/$defs/string_set"}, + "units": {"$ref": "#/$defs/string_set"}, + "law_value_uses": {"type": "array", "items": {"$ref": "#/$defs/law_value_use"}}, + "effective_interval": { + "oneOf": [{"$ref": "#/$defs/effective_interval"}, {"type": "null"}] + }, + "rounding_rule": { + "oneOf": [{"$ref": "#/$defs/rounding_rule"}, {"type": "null"}] + }, + "period_rows": {"type": "array", "items": {"$ref": "#/$defs/period_row"}}, + "missing_operands": {"$ref": "#/$defs/string_set"}, + "result": { + "oneOf": [{"$ref": "#/$defs/calculation_result"}, {"type": "null"}] + }, + "result_sha256": { + "oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}] + }, + "issue_codes": {"$ref": "#/$defs/string_set"}, + "receipt_sha256": {"$ref": "#/$defs/sha256"} + }, + "allOf": [ + { + "if": {"properties": {"calculation_status": {"const": "CALCULATED"}}, "required": ["calculation_status"]}, + "then": { + "properties": { + "activation_status": {"const": "ACTIVATED"}, + "formula_id": {"$ref": "#/$defs/nonempty_string"}, + "formula_sha256": {"$ref": "#/$defs/sha256"}, + "missing_operands": {"maxItems": 0}, + "result": {"$ref": "#/$defs/calculation_result"}, + "result_sha256": {"$ref": "#/$defs/sha256"}, + "rounding_rule": {"$ref": "#/$defs/rounding_rule"} + } + } + }, + { + "if": {"properties": {"calculation_status": {"enum": ["MISSING_OPERAND", "LAW_VALUE_GAP", "TEMPORAL_GAP", "UNIT_CONFLICT", "ACTIVATION_GAP", "ACTIVATION_CONFLICT", "NOT_APPLICABLE"]}}, "required": ["calculation_status"]}, + "then": {"properties": {"result": {"type": "null"}, "result_sha256": {"type": "null"}}} + }, + { + "if": {"properties": {"activation_status": {"const": "NOT_APPLICABLE"}}, "required": ["activation_status"]}, + "then": {"properties": {"calculation_status": {"const": "NOT_APPLICABLE"}, "activation_row_id": {"type": "null"}, "formula_id": {"type": "null"}, "formula_sha256": {"type": "null"}}} + }, + { + "if": {"properties": {"activation_status": {"const": "ACTIVATION_GAP"}}, "required": ["activation_status"]}, + "then": {"properties": {"calculation_status": {"const": "ACTIVATION_GAP"}, "activation_row_id": {"type": "null"}, "formula_id": {"type": "null"}, "formula_sha256": {"type": "null"}}} + }, + { + "if": {"properties": {"activation_status": {"const": "ACTIVATION_CONFLICT"}}, "required": ["activation_status"]}, + "then": {"properties": {"calculation_status": {"const": "ACTIVATION_CONFLICT"}, "activation_row_id": {"type": "null"}, "formula_id": {"type": "null"}, "formula_sha256": {"type": "null"}}} + } + ] + }, + "calculation_state_counts": { + "type": "object", + "additionalProperties": false, + "required": ["ACTIVATED", "NOT_APPLICABLE", "ACTIVATION_GAP", "ACTIVATION_CONFLICT"], + "properties": { + "ACTIVATED": {"type": "integer", "minimum": 0}, + "NOT_APPLICABLE": {"type": "integer", "minimum": 0}, + "ACTIVATION_GAP": {"type": "integer", "minimum": 0}, + "ACTIVATION_CONFLICT": {"type": "integer", "minimum": 0} + } + }, + "calculation_report": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "run_binding_digest", "atomic_claim_ids", "receipt_refs", "receipt_sha256s", "state_counts", "missing_operand_ids", "issue_codes", "report_sha256"], + "properties": { + "schema_version": {"const": "stage2.calculation_report.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "atomic_claim_ids": {"$ref": "#/$defs/string_set"}, + "receipt_refs": {"$ref": "#/$defs/string_set"}, + "receipt_sha256s": {"type": "array", "items": {"$ref": "#/$defs/sha256"}, "uniqueItems": true}, + "state_counts": {"$ref": "#/$defs/calculation_state_counts"}, + "missing_operand_ids": {"$ref": "#/$defs/string_set"}, + "issue_codes": {"$ref": "#/$defs/string_set"}, + "report_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "filing_metric_row": { + "type": "object", + "additionalProperties": false, + "required": ["metric_id", "metric_kind", "status", "value", "lower_bound", "upper_bound", "unit_id", "source_calculation_receipt_ids", "missing_operands", "issue_codes"], + "properties": { + "metric_id": {"$ref": "#/$defs/nonempty_string"}, + "metric_kind": {"enum": ["SOGA", "FILING_FEE", "SERVICE_COST", "OTHER"]}, + "status": {"enum": ["EXACT", "RANGE", "UNEVALUABLE"]}, + "value": {"oneOf": [{"$ref": "#/$defs/decimal_string"}, {"type": "null"}]}, + "lower_bound": {"oneOf": [{"$ref": "#/$defs/decimal_string"}, {"type": "null"}]}, + "upper_bound": {"oneOf": [{"$ref": "#/$defs/decimal_string"}, {"type": "null"}]}, + "unit_id": {"$ref": "#/$defs/nonempty_string"}, + "source_calculation_receipt_ids": {"$ref": "#/$defs/string_set"}, + "missing_operands": {"$ref": "#/$defs/string_set"}, + "issue_codes": {"$ref": "#/$defs/string_set"} + }, + "allOf": [ + { + "if": {"properties": {"status": {"const": "EXACT"}}, "required": ["status"]}, + "then": {"properties": {"value": {"$ref": "#/$defs/decimal_string"}, "lower_bound": {"type": "null"}, "upper_bound": {"type": "null"}, "missing_operands": {"maxItems": 0}}} + }, + { + "if": {"properties": {"status": {"const": "RANGE"}}, "required": ["status"]}, + "then": {"properties": {"value": {"type": "null"}, "lower_bound": {"$ref": "#/$defs/decimal_string"}, "upper_bound": {"$ref": "#/$defs/decimal_string"}}} + }, + { + "if": {"properties": {"status": {"const": "UNEVALUABLE"}}, "required": ["status"]}, + "then": {"properties": {"value": {"type": "null"}, "lower_bound": {"type": "null"}, "upper_bound": {"type": "null"}, "issue_codes": {"minItems": 1}}} + } + ] + }, + "filing_metrics": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "run_binding_digest", "rows", "filing_decision_status", "source_ce13_receipt_ids", "report_sha256"], + "properties": { + "schema_version": {"const": "stage2.filing_metrics.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "rows": {"type": "array", "items": {"$ref": "#/$defs/filing_metric_row"}}, + "filing_decision_status": {"enum": ["READY", "REVIEW_REQUIRED"]}, + "source_ce13_receipt_ids": {"$ref": "#/$defs/string_set"}, + "report_sha256": {"$ref": "#/$defs/sha256"} + }, + "allOf": [ + { + "if": {"properties": {"filing_decision_status": {"const": "READY"}}, "required": ["filing_decision_status"]}, + "then": { + "properties": { + "rows": { + "not": {"contains": {"properties": {"status": {"enum": ["RANGE", "UNEVALUABLE"]}}, "required": ["status"]}} + } + } + } + } + ] + } + }, + "x-semantic-invariants": [ + "Every atomic claim and release-selected calculator has exactly one activation state.", + "An ACTIVATED calculator must use one approved activation row and one release-bound descriptor hash.", + "Missing or conflicting operands never default to zero and never produce a result.", + "Every legal numeric value is authority-release-bound and effective on the applicable interval.", + "Period rows are ordered, non-overlapping, gap-accounted, and recompute to the final result.", + "Binary floating point, model arithmetic, unapproved formulas, and implicit units are forbidden.", + "A filing decision may be READY only when every required filing metric is EXACT." + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/deployment.schema.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/deployment.schema.json index eb8dfb4a..e49c8c47 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/deployment.schema.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/deployment.schema.json @@ -1,9 +1,15 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://schemas.liti-agent.local/stage2/shared/deployment.schema.v2.json", + "$id": "https://schemas.liti-agent.local/stage2/shared/deployment.schema.v3.json", "title": "Stage 2 release, direct Code Executor binding, structured handoff and invocation receipts", - "schema_version": "stage2_deployment.v2", + "schema_version": "stage2_deployment.v3", "oneOf": [ + {"$ref": "#/$defs/stage2_code_executor_binding_v3"}, + {"$ref": "#/$defs/s2_20_inline_code_receipt"}, + {"$ref": "#/$defs/s2_20_request"}, + {"$ref": "#/$defs/s2_20_execution_receipt"}, + {"$ref": "#/$defs/s2_20_outer_execution_receipt"}, + {"$ref": "#/$defs/stage2_deterministic_admission_receipt"}, {"$ref": "#/$defs/code_executor_binding"}, {"$ref": "#/$defs/inline_code_receipt"}, {"$ref": "#/$defs/code_executor_outer_result"}, @@ -59,7 +65,7 @@ "asset_id": {"$ref": "#/$defs/nonempty_string"}, "path": {"const": "deployment/stage2_code_executor_binding.yml"}, "schema_id": { - "const": "stage2_code_executor_binding.v2" + "const": "stage2_code_executor_binding.v3" }, "binding_status": { "enum": [ @@ -107,6 +113,321 @@ "non_finite_numbers_allowed": {"const": false} } }, + "stage_code_executor_binding": { + "type": "object", + "additionalProperties": false, + "required": [ + "stage_id", + "binding_id", + "workflow_id", + "execution_class", + "active_runtime_authority", + "agent_script_ref", + "workflow_contract_ref", + "inline_code_receipt_ref", + "stage2_release_ref", + "expected_release_sha256", + "agent_script_sha256", + "canonical_code_sha256", + "mcp_server_id", + "tool_name", + "language", + "network", + "timeout_seconds", + "runtime_image_digest", + "runtime_image_status", + "dependency_lock", + "localdocs_contract", + "external_mcp_contract", + "egress_profile_id", + "egress_profile_status", + "downstream_handoff_owner", + "live_admission_status", + "legacy_fallbacks" + ], + "properties": { + "stage_id": {"enum": ["S2_00", "S2_20", "S2_40"]}, + "binding_id": {"$ref": "#/$defs/nonempty_string"}, + "workflow_id": {"enum": ["S2_00", "S2_20", "S2_40"]}, + "execution_class": {"const": "NON-LLM-DETERMINISTIC"}, + "active_runtime_authority": {"type": "boolean"}, + "agent_script_ref": {"$ref": "#/$defs/asset_ref"}, + "workflow_contract_ref": {"$ref": "#/$defs/asset_ref"}, + "inline_code_receipt_ref": {"$ref": "#/$defs/asset_ref"}, + "stage2_release_ref": {"$ref": "#/$defs/asset_ref"}, + "expected_release_sha256": {"$ref": "#/$defs/bindable_sha256"}, + "agent_script_sha256": {"$ref": "#/$defs/bindable_sha256"}, + "canonical_code_sha256": {"$ref": "#/$defs/bindable_sha256"}, + "mcp_server_id": {"const": "code-executor"}, + "tool_name": {"const": "run_code"}, + "language": {"const": "python"}, + "network": {"const": "agent-network"}, + "timeout_seconds": {"type": "integer", "minimum": 1, "maximum": 900}, + "runtime_image_digest": {"$ref": "#/$defs/bindable_sha256"}, + "runtime_image_status": {"enum": ["PINNED_VERIFIED", "PENDING_BACKEND_EVIDENCE"]}, + "dependency_lock": {"$ref": "#/$defs/dependency_lock"}, + "localdocs_contract": { + "type": "object", + "additionalProperties": false, + "required": ["endpoint", "user_id_template", "workspace_id_template", "tool_allowlist", "fixed_request_path", "read_path_allowlist", "write_root_rule"], + "properties": { + "endpoint": {"const": "http://mcp-localdocs:8012/mcp"}, + "user_id_template": {"const": "{{__user_hash__}}"}, + "workspace_id_template": {"const": "{{__workspace_hash__}}"}, + "tool_allowlist": { + "type": "array", + "prefixItems": [{"const": "read_binary_doc"}, {"const": "write_binary_file"}], + "items": false, + "minItems": 2, + "maxItems": 2 + }, + "fixed_request_path": {"enum": ["stage2_control/s2_00_request.json", "stage2_control/s2_20_request.json", "stage2_control/s2_40_request.json"]}, + "read_path_allowlist": {"type": "array", "items": {"$ref": "#/$defs/path_ref"}, "minItems": 1, "uniqueItems": true}, + "write_root_rule": {"const": "stage2_runs/by-binding//"} + } + }, + "external_mcp_contract": { + "oneOf": [ + {"type": "null"}, + { + "type": "object", + "additionalProperties": false, + "required": ["endpoint", "protocol_version", "tool_allowlist", "collection", "tenant", "credential_ref", "operation_mode", "admission_status"], + "properties": { + "endpoint": {"const": "https://weaviate.eroomai.com/mcp"}, + "protocol_version": {"const": "2025-03-26"}, + "tool_allowlist": {"type": "array", "prefixItems": [{"const": "search_hybrid"}], "items": false, "minItems": 1, "maxItems": 1}, + "collection": {"$ref": "#/$defs/nonempty_string"}, + "tenant": {"$ref": "#/$defs/nonempty_string"}, + "credential_ref": {"$ref": "#/$defs/nonempty_string"}, + "operation_mode": {"const": "READ_ONLY_EXACT_FILTER"}, + "admission_status": {"enum": ["PENDING_LIVE_VERIFICATION", "LIVE_VERIFIED"]} + } + } + ] + }, + "egress_profile_id": {"$ref": "#/$defs/nonempty_string"}, + "egress_profile_status": {"enum": ["STATIC_ALLOWLIST_BOUND", "PENDING_LIVE_VERIFICATION", "LIVE_VERIFIED"]}, + "downstream_handoff_owner": {"type": ["object", "null"]}, + "live_admission_status": {"enum": ["PENDING_SECRET_BINDING", "PENDING_LIVE_EVIDENCE", "LIVE_CANARY_ADMITTED"]}, + "legacy_fallbacks": {"type": "array", "maxItems": 0} + }, + "allOf": [ + {"if": {"properties": {"stage_id": {"const": "S2_00"}}, "required": ["stage_id"]}, "then": {"properties": {"workflow_id": {"const": "S2_00"}, "external_mcp_contract": {"type": "null"}}}}, + {"if": {"properties": {"stage_id": {"const": "S2_20"}}, "required": ["stage_id"]}, "then": {"properties": {"workflow_id": {"const": "S2_20"}, "external_mcp_contract": {"type": "object"}}}}, + {"if": {"properties": {"stage_id": {"const": "S2_40"}}, "required": ["stage_id"]}, "then": {"properties": {"workflow_id": {"const": "S2_40"}}}} + ] + }, + "stage2_code_executor_binding_v3": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "binding_id", "stage_bindings", "legacy_fallbacks"], + "properties": { + "schema_version": {"const": "stage2_code_executor_binding.v3"}, + "binding_id": {"const": "S2-BINDING-DETERMINISTIC-STAGES-V3"}, + "stage_bindings": {"type": "array", "items": {"$ref": "#/$defs/stage_code_executor_binding"}, "minItems": 1, "maxItems": 3}, + "legacy_fallbacks": {"type": "array", "maxItems": 0} + }, + "x-cross-field-invariants": ["stage_bindings stage_id values are unique", "every stage_id equals workflow_id", "S2_00 row semantics are preserved during v2 migration"] + }, + "s2_20_host_cas_receipt": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", "stage_id", "run_binding_digest", "exclusivity_key", + "cas_status", "single_writer_guaranteed", "backend_capability_receipt_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_host_cas_receipt.v1"}, + "stage_id": {"const": "S2_20"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "exclusivity_key": {"type": "string", "pattern": "^S2_20:[a-f0-9]{64}$"}, + "cas_status": {"const": "ACQUIRED"}, + "single_writer_guaranteed": {"const": true}, + "backend_capability_receipt_sha256": {"$ref": "#/$defs/sha256"} + }, + "x-cross-field-invariants": [ + "exclusivity_key suffix equals run_binding_digest" + ] + }, + "s2_20_request": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "request_id", "attempt_id", "run_binding_digest", "user_identity_hash", "workspace_identity_hash", "stage2_run_root_ref", "plan_output_root_ref", "host_cas_receipt_ref", "host_cas_receipt_sha256", "detached_admission_receipt_ref", "detached_admission_receipt_sha256", "outer_execution_receipt_target_ref", "expected_s2_10_publish_status_sha256", "expected_parent_stage2_release_sha256", "expected_s2_10_release_sha256", "expected_s2_20_agent_sha256", "expected_s2_20_executor_binding_sha256"], + "properties": { + "schema_version": {"const": "stage2_s2_20_request.v1"}, + "request_id": {"$ref": "#/$defs/nonempty_string"}, + "attempt_id": {"$ref": "#/$defs/nonempty_string"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "user_identity_hash": {"$ref": "#/$defs/sha256"}, + "workspace_identity_hash": {"$ref": "#/$defs/sha256"}, + "stage2_run_root_ref": {"type": "string", "pattern": "^stage2_runs/by-binding/[a-f0-9]{64}$"}, + "plan_output_root_ref": {"type": "string", "pattern": "^stage2_runs/by-binding/[a-f0-9]{64}/plan$"}, + "host_cas_receipt_ref": {"type": "string", "pattern": "^stage2_control/host_cas/[a-f0-9]{64}/S2_20\\.json$"}, + "host_cas_receipt_sha256": {"$ref": "#/$defs/sha256"}, + "detached_admission_receipt_ref": {"const": "Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json"}, + "detached_admission_receipt_sha256": {"$ref": "#/$defs/bindable_sha256"}, + "outer_execution_receipt_target_ref": {"type": "string", "pattern": "^stage2_runs/by-binding/[a-f0-9]{64}/control/s2_20_outer_execution_receipt\\.json$"}, + "expected_s2_10_publish_status_sha256": {"$ref": "#/$defs/sha256"}, + "expected_parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"}, + "expected_s2_10_release_sha256": {"$ref": "#/$defs/sha256"}, + "expected_s2_20_agent_sha256": {"$ref": "#/$defs/bindable_sha256"}, + "expected_s2_20_executor_binding_sha256": {"$ref": "#/$defs/bindable_sha256"} + }, + "x-cross-field-invariants": ["all run-root and receipt target digests equal run_binding_digest", "user/workspace identity hashes equal AgentBackend substitutions"] + }, + "s2_20_execution_receipt": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "ok", "workflow_id", "request_id", "run_binding_digest", "plan_status", "route", "artifact_set_digest", "plan_publish_status_path", "plan_publish_status_sha256", "error"], + "properties": { + "schema_version": {"const": "stage2_s2_20_execution_receipt.v1"}, + "ok": {"type": "boolean"}, + "workflow_id": {"const": "S2_20"}, + "request_id": {"$ref": "#/$defs/nonempty_string"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "plan_status": {"enum": ["PLAN_COMPLETE", "PLAN_COMPLETE_WITH_REVIEW", "TECHNICAL_INCOMPLETE"]}, + "route": {"enum": ["TO_S2_30", "STOP_TECHNICAL_INCOMPLETE"]}, + "artifact_set_digest": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]}, + "plan_publish_status_path": {"const": "plan/plan_publish_status.json"}, + "plan_publish_status_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]}, + "error": {"type": ["object", "null"]} + } + }, + "s2_20_inline_code_receipt": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "workflow_id", "authoring", "deployment_projection", "canonical_code", "full_code_mirrors", "task_contract", "parity_status"], + "properties": { + "schema_version": {"const": "stage2_s2_20_inline_code_receipt.v1"}, + "workflow_id": {"const": "S2_20"}, + "authoring": {"$ref": "#/$defs/physical_artifact_digest"}, + "deployment_projection": {"$ref": "#/$defs/physical_artifact_digest"}, + "canonical_code": { + "type": "object", + "additionalProperties": false, + "required": ["yaml_pointer", "encoding", "extraction_transform", "code_sha256", "code_size_bytes", "compile_status", "ast_status", "imports", "forbidden_import_count", "forbidden_dynamic_call_count", "external_python_source_ref_count", "plaintext_secret_count", "endpoint_literals", "expected_parent_stage2_release_sha256"], + "properties": { + "yaml_pointer": {"const": "/Agent/Stages/0/tasks/0/parameters/code"}, + "encoding": {"const": "UTF-8"}, + "extraction_transform": {"const": "NONE"}, + "code_sha256": {"$ref": "#/$defs/sha256"}, + "code_size_bytes": {"type": "integer", "minimum": 1}, + "compile_status": {"const": "PASS"}, + "ast_status": {"const": "PASS"}, + "imports": {"type": "array", "items": {"$ref": "#/$defs/nonempty_string"}, "uniqueItems": true}, + "forbidden_import_count": {"const": 0}, + "forbidden_dynamic_call_count": {"const": 0}, + "external_python_source_ref_count": {"const": 0}, + "plaintext_secret_count": {"const": 0}, + "endpoint_literals": { + "const": [ + "http://mcp-localdocs:8012/mcp", + "https://weaviate.eroomai.com/mcp" + ] + }, + "expected_parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "full_code_mirrors": { + "type": "array", + "prefixItems": [ + {"const": "Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.py"}, + {"const": "Default_Agent/Stage_2_Clean/runtime/s2_20_reduce.txt"} + ], + "items": false, + "minItems": 2, + "maxItems": 2 + }, + "task_contract": { + "type": "object", + "additionalProperties": false, + "required": ["agent", "stage", "mcp_servers", "task", "task_procedure", "internal_dag", "canonical_task_semantics_sha256", "exactly_one_stage", "exactly_one_task", "exactly_one_code_executor_run_code", "authoring_rewritten", "projection_byte_identical_to_authoring", "code_mirrors_byte_identical", "expected_parent_stage2_release_sha256"], + "properties": { + "agent": {"type": "object"}, + "stage": {"type": "object"}, + "mcp_servers": {"type": "object"}, + "task": {"type": "object"}, + "task_procedure": {"type": "object"}, + "internal_dag": {"const": "(C20||C21)->C25->C26->(C22||(C27->C28->C29))->C30->C35"}, + "canonical_task_semantics_sha256": {"$ref": "#/$defs/sha256"}, + "exactly_one_stage": {"const": true}, + "exactly_one_task": {"const": true}, + "exactly_one_code_executor_run_code": {"const": true}, + "authoring_rewritten": {"const": false}, + "projection_byte_identical_to_authoring": {"const": true}, + "code_mirrors_byte_identical": {"const": true}, + "expected_parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "parity_status": {"const": "PASS"} + } + }, + "physical_artifact_digest": { + "type": "object", + "additionalProperties": false, + "required": ["path", "sha256", "size_bytes"], + "properties": {"path": {"$ref": "#/$defs/path_ref"}, "sha256": {"$ref": "#/$defs/bindable_sha256"}, "size_bytes": {"type": "integer", "minimum": 1}} + }, + "s2_20_outer_execution_receipt": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "workflow_id", "agent_sha256", "binding_sha256", "outer_status", "inner_receipt_sha256", "host_cas_receipt_sha256"], + "properties": { + "schema_version": {"const": "stage2_s2_20_outer_execution_receipt.v1"}, + "workflow_id": {"const": "S2_20"}, + "agent_sha256": {"$ref": "#/$defs/sha256"}, + "binding_sha256": {"$ref": "#/$defs/sha256"}, + "outer_status": {"enum": ["SUCCEEDED", "FAILED"]}, + "inner_receipt_sha256": {"oneOf": [{"$ref": "#/$defs/sha256"}, {"type": "null"}]}, + "host_cas_receipt_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "stage2_deterministic_admission_receipt": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "parent_release_sha256", "executor_binding_sha256", "present_deterministic_stage_ids", "stage_receipts", "admission_status", "signature", "signature_verification_status", "signed_payload_sha256", "backend_capability_receipt_sha256"], + "properties": { + "schema_version": {"const": "stage2_deterministic_admission_receipt.v1"}, + "parent_release_sha256": {"$ref": "#/$defs/sha256"}, + "executor_binding_sha256": {"$ref": "#/$defs/sha256"}, + "present_deterministic_stage_ids": {"type": "array", "items": {"enum": ["S2_00", "S2_20", "S2_40"]}, "minItems": 1, "uniqueItems": true}, + "stage_receipts": {"type": "array", "items": {"$ref": "#/$defs/asset_ref"}, "minItems": 1}, + "admission_status": {"enum": ["PENDING", "CANARY_ADMITTED", "PRODUCTION_ADMITTED"]}, + "signature": {"type": "string", "minLength": 1}, + "signature_verification_status": {"enum": ["PENDING_EXTERNAL_SIGNATURE", "BACKEND_VERIFIED"]}, + "signed_payload_sha256": {"$ref": "#/$defs/bindable_sha256"}, + "backend_capability_receipt_sha256": {"$ref": "#/$defs/bindable_sha256"} + }, + "allOf": [ + { + "if": {"properties": {"admission_status": {"const": "PENDING"}}, "required": ["admission_status"]}, + "then": { + "properties": { + "signature": {"const": "PENDING_EXTERNAL_SIGNATURE"}, + "signature_verification_status": {"const": "PENDING_EXTERNAL_SIGNATURE"}, + "signed_payload_sha256": {"const": "PENDING_SEQUENTIAL_BIND"}, + "backend_capability_receipt_sha256": {"const": "PENDING_SEQUENTIAL_BIND"} + } + } + }, + { + "if": {"properties": {"admission_status": {"enum": ["CANARY_ADMITTED", "PRODUCTION_ADMITTED"]}}, "required": ["admission_status"]}, + "then": { + "properties": { + "signature": {"type": "string", "minLength": 16, "not": {"pattern": "^PENDING"}}, + "signature_verification_status": {"const": "BACKEND_VERIFIED"}, + "signed_payload_sha256": {"$ref": "#/$defs/sha256"}, + "backend_capability_receipt_sha256": {"$ref": "#/$defs/sha256"} + } + } + } + ], + "x-cross-field-invariants": [ + "stage_receipts stage IDs equal present_deterministic_stage_ids exactly", + "for admitted receipts signed_payload_sha256 covers the entire object after removing only signature, signature_verification_status and signed_payload_sha256; backend_capability_receipt_sha256 remains in scope" + ] + }, "code_executor_binding": { "type": "object", "additionalProperties": false, diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/domain_verdict.schema.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/domain_verdict.schema.json new file mode 100644 index 00000000..87f8c4b7 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/domain_verdict.schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.liti-agent.local/stage2/s2_20/domain_verdict.schema.v1.json", + "title": "S2_20 canonical domain-verdict facade", + "description": "Thin facade over the S2_10 canonical verdict contract. This file intentionally does not duplicate S2_10 legal-judgment semantics.", + "schema_version": "stage2_s2_20_domain_verdict_facade.v1", + "$ref": "#/$defs/canonical_domain_verdict", + "$defs": { + "canonical_domain_verdict": { + "$ref": "s2_10.schema.json#/$defs/canonical_domain_verdict" + }, + "canonical_claim_option": { + "$ref": "s2_10.schema.json#/$defs/canonical_claim_option" + }, + "canonical_option_relation": { + "$ref": "s2_10.schema.json#/$defs/canonical_option_relation" + }, + "normalized_claim_signature": { + "$ref": "s2_10.schema.json#/$defs/normalized_claim_signature" + }, + "typed_provenance": { + "$ref": "s2_10.schema.json#/$defs/typed_provenance" + } + }, + "x-contract-boundaries": { + "semantic_source_of_truth": "schemas/s2_10.schema.json#/$defs/canonical_domain_verdict", + "s2_20_may_reinterpret_legal_decision": false, + "s2_20_may_mint_claim_options": false, + "s2_20_may_drop_claim_options": false + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/relief_plan.schema.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/relief_plan.schema.json new file mode 100644 index 00000000..8ac473c3 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/relief_plan.schema.json @@ -0,0 +1,838 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.liti-agent.local/stage2/s2_20/relief_plan.schema.v1.json", + "title": "S2_20 canonical ReliefPlan contracts", + "description": "Closed structural contracts for option conservation, atomic claims, stable claim groups, frozen ReliefPlan artifacts, group slices, reports, and the status-last publish barrier. Cross-document conservation and hash invariants remain deterministic validator duties.", + "schema_version": "stage2_s2_20_relief_plan_contract_bundle.v1", + "oneOf": [ + {"$ref": "#/$defs/option_disposition"}, + {"$ref": "#/$defs/atomic_claim"}, + {"$ref": "#/$defs/claim_group"}, + {"$ref": "#/$defs/canonical_relief_plan"}, + {"$ref": "#/$defs/claim_groups_document"}, + {"$ref": "#/$defs/option_conservation_report"}, + {"$ref": "#/$defs/party_completeness_report"}, + {"$ref": "#/$defs/exhibit_register"}, + {"$ref": "#/$defs/element_evidence_gap"}, + {"$ref": "#/$defs/group_slice"}, + {"$ref": "#/$defs/prompt_segment"}, + {"$ref": "#/$defs/plan_publish_status"}, + {"$ref": "#/$defs/technical_diagnostic"} + ], + "$defs": { + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "nonempty_string": { + "type": "string", + "minLength": 1 + }, + "nullable_nonempty_string": { + "oneOf": [ + {"$ref": "#/$defs/nonempty_string"}, + {"type": "null"} + ] + }, + "string_set": { + "type": "array", + "items": {"$ref": "#/$defs/nonempty_string"}, + "uniqueItems": true + }, + "nonempty_string_set": { + "type": "array", + "items": {"$ref": "#/$defs/nonempty_string"}, + "minItems": 1, + "uniqueItems": true + }, + "claim_option_id": { + "type": "string", + "pattern": "^CO-[a-f0-9]{64}$" + }, + "atomic_claim_id": { + "type": "string", + "pattern": "^AC-[a-f0-9]{16,64}$" + }, + "claim_group_id": { + "type": "string", + "pattern": "^CG-[a-f0-9]{16,64}$" + }, + "artifact_path": { + "type": "string", + "minLength": 1, + "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*[\\x00-\\x1f]).+$" + }, + "schema_ref": { + "type": "string", + "pattern": "^(?:schemas|corpus)/[^#]+#/(?:\\$defs|properties)/.+$" + }, + "permission": { + "enum": ["CLEAN_DRAFT", "REVIEW_DRAFT", "WORKNOTE_ONLY"] + }, + "binding_status": { + "enum": ["BOUND", "UNRESOLVED", "CONFLICT", "NOT_APPLICABLE"] + }, + "issue_ref": { + "type": "string", + "pattern": "^ISS-[A-Fa-f0-9]{16,64}$" + }, + "issue_ref_set": { + "type": "array", + "items": {"$ref": "#/$defs/issue_ref"}, + "uniqueItems": true + }, + "provenance_ref": { + "type": "object", + "additionalProperties": false, + "required": ["source_artifact_path", "source_artifact_sha256", "json_pointer"], + "properties": { + "source_artifact_path": {"$ref": "#/$defs/artifact_path"}, + "source_artifact_sha256": {"$ref": "#/$defs/sha256"}, + "json_pointer": { + "type": "string", + "pattern": "^(?:/(?:[^~/]|~0|~1)*)*$" + } + } + }, + "amount_slot": { + "type": "object", + "additionalProperties": false, + "required": [ + "slot_id", + "status", + "currency", + "exact_value", + "minimum_value", + "maximum_value", + "calculation_receipt_ids", + "missing_operand_ids" + ], + "properties": { + "slot_id": {"$ref": "#/$defs/nonempty_string"}, + "status": {"enum": ["EXACT", "RANGE", "UNRESOLVED", "NOT_APPLICABLE"]}, + "currency": {"enum": ["KRW", "NONE"]}, + "exact_value": { + "oneOf": [ + {"type": "string", "pattern": "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$"}, + {"type": "null"} + ] + }, + "minimum_value": { + "oneOf": [ + {"type": "string", "pattern": "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$"}, + {"type": "null"} + ] + }, + "maximum_value": { + "oneOf": [ + {"type": "string", "pattern": "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$"}, + {"type": "null"} + ] + }, + "calculation_receipt_ids": {"$ref": "#/$defs/string_set"}, + "missing_operand_ids": {"$ref": "#/$defs/string_set"} + } + }, + "period_slot": { + "type": "object", + "additionalProperties": false, + "required": ["period_slot_id", "start_date", "end_date", "rate_ref", "status", "source_refs"], + "properties": { + "period_slot_id": {"$ref": "#/$defs/nonempty_string"}, + "start_date": { + "oneOf": [ + {"type": "string", "format": "date"}, + {"type": "null"} + ] + }, + "end_date": { + "oneOf": [ + {"type": "string", "format": "date"}, + {"type": "null"} + ] + }, + "rate_ref": {"$ref": "#/$defs/nullable_nonempty_string"}, + "status": {"enum": ["EXACT", "RANGE", "UNRESOLVED", "NOT_APPLICABLE"]}, + "source_refs": {"$ref": "#/$defs/string_set"} + } + }, + "option_disposition": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "claim_option_id", + "source_cluster_id", + "source_domain_verdict_sha256", + "legal_decision", + "client_instruction", + "disposition", + "reason_codes", + "atomic_claim_ids", + "claim_group_id", + "issue_refs" + ], + "properties": { + "schema_version": {"const": "stage2_option_disposition.v1"}, + "claim_option_id": {"$ref": "#/$defs/claim_option_id"}, + "source_cluster_id": {"$ref": "#/$defs/nonempty_string"}, + "source_domain_verdict_sha256": {"$ref": "#/$defs/sha256"}, + "legal_decision": {"enum": ["SUPPORTED", "CONDITIONAL", "UNRESOLVED", "EXCLUDED"]}, + "client_instruction": {"enum": ["PURSUE", "NO_SUE", "UNSPECIFIED"]}, + "disposition": {"enum": ["SELECTED", "ALTERNATIVE", "EXCLUDED", "DEFERRED"]}, + "reason_codes": {"$ref": "#/$defs/nonempty_string_set"}, + "atomic_claim_ids": { + "type": "array", + "items": {"$ref": "#/$defs/atomic_claim_id"}, + "uniqueItems": true + }, + "claim_group_id": { + "oneOf": [ + {"$ref": "#/$defs/claim_group_id"}, + {"type": "null"} + ] + }, + "issue_refs": {"$ref": "#/$defs/issue_ref_set"} + }, + "allOf": [ + { + "if": { + "properties": {"client_instruction": {"const": "NO_SUE"}}, + "required": ["client_instruction"] + }, + "then": { + "properties": {"disposition": {"const": "DEFERRED"}} + } + } + ] + }, + "atomic_claim": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "atomic_claim_id", + "source_claim_option_ids", + "normalized_signature_sha256", + "right_holder_refs", + "obligor_refs", + "object_refs", + "remedy_mode", + "case_type_binding_status", + "case_type_id", + "sub_rule_binding_status", + "sub_rule_id", + "rule_branch_key", + "claim_group_id", + "drafting_permission", + "source_refs", + "issue_refs" + ], + "properties": { + "schema_version": {"const": "stage2_atomic_claim.v1"}, + "atomic_claim_id": {"$ref": "#/$defs/atomic_claim_id"}, + "source_claim_option_ids": { + "type": "array", + "items": {"$ref": "#/$defs/claim_option_id"}, + "minItems": 1, + "uniqueItems": true + }, + "normalized_signature_sha256": {"$ref": "#/$defs/sha256"}, + "right_holder_refs": {"$ref": "#/$defs/string_set"}, + "obligor_refs": {"$ref": "#/$defs/string_set"}, + "object_refs": {"$ref": "#/$defs/string_set"}, + "remedy_mode": {"$ref": "#/$defs/nonempty_string"}, + "case_type_binding_status": {"$ref": "#/$defs/binding_status"}, + "case_type_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "sub_rule_binding_status": {"$ref": "#/$defs/binding_status"}, + "sub_rule_id": {"$ref": "#/$defs/nullable_nonempty_string"}, + "rule_branch_key": {"$ref": "#/$defs/nullable_nonempty_string"}, + "claim_group_id": {"$ref": "#/$defs/claim_group_id"}, + "drafting_permission": {"$ref": "#/$defs/permission"}, + "source_refs": { + "type": "array", + "items": {"$ref": "#/$defs/provenance_ref"}, + "minItems": 1 + }, + "issue_refs": {"$ref": "#/$defs/issue_ref_set"} + }, + "allOf": [ + { + "if": { + "properties": { + "case_type_binding_status": {"const": "BOUND"}, + "sub_rule_binding_status": {"const": "BOUND"} + }, + "required": ["case_type_binding_status", "sub_rule_binding_status"] + }, + "then": { + "properties": { + "case_type_id": {"$ref": "#/$defs/nonempty_string"}, + "sub_rule_id": {"$ref": "#/$defs/nonempty_string"}, + "rule_branch_key": {"$ref": "#/$defs/nonempty_string"} + } + }, + "else": { + "not": { + "properties": {"drafting_permission": {"const": "CLEAN_DRAFT"}}, + "required": ["drafting_permission"] + } + } + } + ] + }, + "group_dependency": { + "type": "object", + "additionalProperties": false, + "required": ["source_group_id", "target_group_id", "relation", "source_option_relation_refs"], + "properties": { + "source_group_id": {"$ref": "#/$defs/claim_group_id"}, + "target_group_id": {"$ref": "#/$defs/claim_group_id"}, + "relation": {"enum": ["PRECONDITION", "ALTERNATIVE", "CUMULATIVE", "ACCESSORY", "INCOMPATIBLE"]}, + "source_option_relation_refs": {"$ref": "#/$defs/nonempty_string_set"} + } + }, + "claim_group": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "claim_group_id", + "atomic_claim_ids", + "party_refs", + "object_refs", + "case_type_ids", + "rule_branch_keys", + "renderer_ids", + "party_set_rule_ids", + "calculation_receipt_ids", + "corpus_scope_ids", + "amount_slots", + "period_slots", + "counter_performance_refs", + "procedural_declaration_refs", + "dependencies", + "drafting_permission", + "issue_refs" + ], + "properties": { + "schema_version": {"const": "stage2_claim_group.v1"}, + "claim_group_id": {"$ref": "#/$defs/claim_group_id"}, + "atomic_claim_ids": { + "type": "array", + "items": {"$ref": "#/$defs/atomic_claim_id"}, + "minItems": 1, + "uniqueItems": true + }, + "party_refs": {"$ref": "#/$defs/string_set"}, + "object_refs": {"$ref": "#/$defs/string_set"}, + "case_type_ids": {"$ref": "#/$defs/string_set"}, + "rule_branch_keys": {"$ref": "#/$defs/string_set"}, + "renderer_ids": {"$ref": "#/$defs/string_set"}, + "party_set_rule_ids": {"$ref": "#/$defs/string_set"}, + "calculation_receipt_ids": {"$ref": "#/$defs/string_set"}, + "corpus_scope_ids": {"$ref": "#/$defs/string_set"}, + "amount_slots": { + "type": "array", + "items": {"$ref": "#/$defs/amount_slot"} + }, + "period_slots": { + "type": "array", + "items": {"$ref": "#/$defs/period_slot"} + }, + "counter_performance_refs": {"$ref": "#/$defs/string_set"}, + "procedural_declaration_refs": {"$ref": "#/$defs/string_set"}, + "dependencies": { + "type": "array", + "items": {"$ref": "#/$defs/group_dependency"} + }, + "drafting_permission": {"$ref": "#/$defs/permission"}, + "issue_refs": {"$ref": "#/$defs/issue_ref_set"} + } + }, + "artifact_ref": { + "type": "object", + "additionalProperties": false, + "required": ["path", "schema_ref", "sha256"], + "properties": { + "path": {"$ref": "#/$defs/artifact_path"}, + "schema_ref": {"$ref": "#/$defs/schema_ref"}, + "sha256": {"$ref": "#/$defs/sha256"} + } + }, + "canonical_relief_plan": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "producer_id", + "run_binding_digest", + "input_snapshot_digest", + "parent_stage2_release_sha256", + "s2_10_publish_status_sha256", + "option_dispositions", + "atomic_claims", + "claim_groups", + "group_dependencies", + "selected_option_ids", + "alternative_option_ids", + "excluded_option_ids", + "deferred_option_ids", + "report_refs", + "issue_ledger_ref", + "plan_content_digest" + ], + "properties": { + "schema_version": {"const": "stage2_canonical_relief_plan.v1"}, + "producer_id": {"const": "S2_20"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "input_snapshot_digest": {"$ref": "#/$defs/sha256"}, + "parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"}, + "s2_10_publish_status_sha256": {"$ref": "#/$defs/sha256"}, + "option_dispositions": { + "type": "array", + "items": {"$ref": "#/$defs/option_disposition"} + }, + "atomic_claims": { + "type": "array", + "items": {"$ref": "#/$defs/atomic_claim"} + }, + "claim_groups": { + "type": "array", + "items": {"$ref": "#/$defs/claim_group"} + }, + "group_dependencies": { + "type": "array", + "items": {"$ref": "#/$defs/group_dependency"} + }, + "selected_option_ids": { + "type": "array", + "items": {"$ref": "#/$defs/claim_option_id"}, + "uniqueItems": true + }, + "alternative_option_ids": { + "type": "array", + "items": {"$ref": "#/$defs/claim_option_id"}, + "uniqueItems": true + }, + "excluded_option_ids": { + "type": "array", + "items": {"$ref": "#/$defs/claim_option_id"}, + "uniqueItems": true + }, + "deferred_option_ids": { + "type": "array", + "items": {"$ref": "#/$defs/claim_option_id"}, + "uniqueItems": true + }, + "report_refs": { + "type": "array", + "items": {"$ref": "#/$defs/artifact_ref"} + }, + "issue_ledger_ref": {"$ref": "#/$defs/artifact_ref"}, + "plan_content_digest": {"$ref": "#/$defs/sha256"} + }, + "x-cross-field-invariants": [ + "Every S2_10 claim_option_id appears in exactly one disposition partition", + "Every atomic_claim_id appears in exactly one claim_group", + "CLEAN_DRAFT requires exact case-type, sub-rule, renderer, party-set, calculator, corpus, and authority bindings", + "plan_content_digest is the canonical object hash with plan_content_digest removed" + ] + }, + "claim_groups_document": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "run_binding_digest", "claim_groups", "group_dependencies", "document_digest"], + "properties": { + "schema_version": {"const": "stage2_claim_groups.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "claim_groups": { + "type": "array", + "items": {"$ref": "#/$defs/claim_group"} + }, + "group_dependencies": { + "type": "array", + "items": {"$ref": "#/$defs/group_dependency"} + }, + "document_digest": {"$ref": "#/$defs/sha256"} + } + }, + "option_conservation_report": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "run_binding_digest", + "all_option_ids", + "selected_option_ids", + "alternative_option_ids", + "excluded_option_ids", + "deferred_option_ids", + "partition_disjoint", + "partition_union_exact", + "silent_loss_count", + "duplicate_recovery_issue_refs", + "status" + ], + "properties": { + "schema_version": {"const": "stage2_option_conservation_report.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "all_option_ids": {"type": "array", "items": {"$ref": "#/$defs/claim_option_id"}, "uniqueItems": true}, + "selected_option_ids": {"type": "array", "items": {"$ref": "#/$defs/claim_option_id"}, "uniqueItems": true}, + "alternative_option_ids": {"type": "array", "items": {"$ref": "#/$defs/claim_option_id"}, "uniqueItems": true}, + "excluded_option_ids": {"type": "array", "items": {"$ref": "#/$defs/claim_option_id"}, "uniqueItems": true}, + "deferred_option_ids": {"type": "array", "items": {"$ref": "#/$defs/claim_option_id"}, "uniqueItems": true}, + "partition_disjoint": {"type": "boolean"}, + "partition_union_exact": {"type": "boolean"}, + "silent_loss_count": {"type": "integer", "minimum": 0}, + "duplicate_recovery_issue_refs": {"$ref": "#/$defs/issue_ref_set"}, + "status": {"enum": ["PASS", "REVIEW_REQUIRED", "FAIL"]} + } + }, + "party_completeness_row": { + "type": "object", + "additionalProperties": false, + "required": [ + "claim_group_id", + "party_set_rule_id", + "required_role_tokens", + "present_party_refs", + "missing_role_tokens", + "identity_conflict_refs", + "status", + "issue_refs" + ], + "properties": { + "claim_group_id": {"$ref": "#/$defs/claim_group_id"}, + "party_set_rule_id": {"$ref": "#/$defs/nonempty_string"}, + "required_role_tokens": {"$ref": "#/$defs/string_set"}, + "present_party_refs": {"$ref": "#/$defs/string_set"}, + "missing_role_tokens": {"$ref": "#/$defs/string_set"}, + "identity_conflict_refs": {"$ref": "#/$defs/string_set"}, + "status": {"enum": ["COMPLETE", "REVIEW_REQUIRED", "INCOMPLETE"]}, + "issue_refs": {"$ref": "#/$defs/issue_ref_set"} + } + }, + "party_completeness_report": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "run_binding_digest", "rows", "report_status"], + "properties": { + "schema_version": {"const": "stage2_party_completeness_report.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "rows": {"type": "array", "items": {"$ref": "#/$defs/party_completeness_row"}}, + "report_status": {"enum": ["COMPLETE", "REVIEW_REQUIRED", "INCOMPLETE"]} + } + }, + "exhibit_row": { + "type": "object", + "additionalProperties": false, + "required": ["evidence_ref", "source_key", "existing_label", "assigned_label", "assignment_status"], + "properties": { + "evidence_ref": {"$ref": "#/$defs/nonempty_string"}, + "source_key": {"$ref": "#/$defs/nonempty_string"}, + "existing_label": {"$ref": "#/$defs/nullable_nonempty_string"}, + "assigned_label": {"$ref": "#/$defs/nonempty_string"}, + "assignment_status": {"enum": ["PRESERVED_EXISTING", "DETERMINISTICALLY_ASSIGNED", "CONFLICT"]} + } + }, + "exhibit_register": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "run_binding_digest", "rows", "register_digest"], + "properties": { + "schema_version": {"const": "stage2_exhibit_register.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "rows": {"type": "array", "items": {"$ref": "#/$defs/exhibit_row"}}, + "register_digest": {"$ref": "#/$defs/sha256"} + } + }, + "element_gap_row": { + "type": "object", + "additionalProperties": false, + "required": [ + "corpus_element_id", + "doctrine_role", + "stage1_slot_refs", + "fact_refs", + "evidence_refs", + "coverage_status", + "issue_refs" + ], + "properties": { + "corpus_element_id": {"$ref": "#/$defs/nonempty_string"}, + "doctrine_role": {"enum": ["element", "defense", "rebuttal", "burden", "relief_consistency"]}, + "stage1_slot_refs": {"$ref": "#/$defs/string_set"}, + "fact_refs": {"$ref": "#/$defs/string_set"}, + "evidence_refs": {"$ref": "#/$defs/string_set"}, + "coverage_status": {"enum": ["SUPPORTED", "PARTIAL", "GAP", "CONFLICT", "CONTEXT_ONLY"]}, + "issue_refs": {"$ref": "#/$defs/issue_ref_set"} + } + }, + "element_evidence_gap": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "run_binding_digest", "claim_group_id", "rows", "gap_count", "conflict_count"], + "properties": { + "schema_version": {"const": "stage2_element_evidence_gap.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "claim_group_id": {"$ref": "#/$defs/claim_group_id"}, + "rows": {"type": "array", "items": {"$ref": "#/$defs/element_gap_row"}}, + "gap_count": {"type": "integer", "minimum": 0}, + "conflict_count": {"type": "integer", "minimum": 0} + } + }, + "group_slice": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "run_binding_digest", + "claim_group_id", + "frozen_plan_sha256", + "claim_group", + "rule_context_pack_ref", + "requirement_fact_pack_ref", + "element_evidence_gap_ref", + "calculation_receipt_refs", + "issue_refs", + "drafting_permission", + "group_slice_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_s2_20_group_slice.v1"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "claim_group_id": {"$ref": "#/$defs/claim_group_id"}, + "frozen_plan_sha256": {"$ref": "#/$defs/sha256"}, + "claim_group": {"$ref": "#/$defs/claim_group"}, + "rule_context_pack_ref": {"$ref": "#/$defs/artifact_ref"}, + "requirement_fact_pack_ref": {"$ref": "#/$defs/artifact_ref"}, + "element_evidence_gap_ref": {"$ref": "#/$defs/artifact_ref"}, + "calculation_receipt_refs": {"type": "array", "items": {"$ref": "#/$defs/artifact_ref"}}, + "issue_refs": {"$ref": "#/$defs/issue_ref_set"}, + "drafting_permission": {"$ref": "#/$defs/permission"}, + "group_slice_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "prompt_segment": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "segment_id", + "claim_group_id", + "authority_class", + "ordered_asset_refs", + "source_group_slice_sha256", + "segment_payload_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_s2_20_prompt_segment.v1"}, + "segment_id": {"enum": ["P31_rule_and_pack", "P32_common_authority"]}, + "claim_group_id": {"$ref": "#/$defs/claim_group_id"}, + "authority_class": {"enum": ["RELIEF_ONLY_AND_CAUSE_CHECK", "COMMON_AUTHORITY"]}, + "ordered_asset_refs": {"type": "array", "items": {"$ref": "#/$defs/artifact_ref"}}, + "source_group_slice_sha256": {"$ref": "#/$defs/sha256"}, + "segment_payload_sha256": {"$ref": "#/$defs/sha256"} + } + }, + "artifact_row": { + "type": "object", + "additionalProperties": false, + "required": ["path", "schema_ref", "raw_sha256", "byte_size", "producer_component"], + "properties": { + "path": {"$ref": "#/$defs/artifact_path"}, + "schema_ref": {"$ref": "#/$defs/schema_ref"}, + "raw_sha256": {"$ref": "#/$defs/sha256"}, + "byte_size": {"type": "integer", "minimum": 1}, + "producer_component": {"enum": ["C20", "C21", "C22", "C25", "C26", "C27", "C28", "C29", "C30", "C35", "S2_20_DIAGNOSTIC"]} + } + }, + "issue_summary": { + "type": "object", + "additionalProperties": false, + "required": ["total", "legal", "binding", "corpus", "calculation", "technical"], + "properties": { + "total": {"type": "integer", "minimum": 0}, + "legal": {"type": "integer", "minimum": 0}, + "binding": {"type": "integer", "minimum": 0}, + "corpus": {"type": "integer", "minimum": 0}, + "calculation": {"type": "integer", "minimum": 0}, + "technical": {"type": "integer", "minimum": 0} + } + }, + "plan_publish_status": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "workflow_id", + "run_binding_digest", + "input_snapshot_digest", + "parent_stage2_release_sha256", + "s2_10_publish_status_sha256", + "s2_20_agent_sha256", + "s2_20_code_sha256", + "plan_status", + "route", + "consumer_authorized", + "artifact_rows", + "artifact_set_digest", + "group_ids", + "clean_draft_group_ids", + "review_draft_group_ids", + "worknote_only_group_ids", + "issue_summary", + "barrier_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_plan_publish_status.v1"}, + "workflow_id": {"const": "S2_20"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "input_snapshot_digest": {"$ref": "#/$defs/sha256"}, + "parent_stage2_release_sha256": {"$ref": "#/$defs/sha256"}, + "s2_10_publish_status_sha256": {"$ref": "#/$defs/sha256"}, + "s2_20_agent_sha256": {"$ref": "#/$defs/sha256"}, + "s2_20_code_sha256": {"$ref": "#/$defs/sha256"}, + "plan_status": {"enum": ["PLAN_COMPLETE", "PLAN_COMPLETE_WITH_REVIEW", "TECHNICAL_INCOMPLETE"]}, + "route": {"enum": ["TO_S2_30", "STOP_TECHNICAL_INCOMPLETE"]}, + "consumer_authorized": {"type": "boolean"}, + "artifact_rows": { + "type": "array", + "items": {"$ref": "#/$defs/artifact_row"} + }, + "artifact_set_digest": {"$ref": "#/$defs/sha256"}, + "group_ids": {"type": "array", "items": {"$ref": "#/$defs/claim_group_id"}, "uniqueItems": true}, + "clean_draft_group_ids": {"type": "array", "items": {"$ref": "#/$defs/claim_group_id"}, "uniqueItems": true}, + "review_draft_group_ids": {"type": "array", "items": {"$ref": "#/$defs/claim_group_id"}, "uniqueItems": true}, + "worknote_only_group_ids": {"type": "array", "items": {"$ref": "#/$defs/claim_group_id"}, "uniqueItems": true}, + "issue_summary": {"$ref": "#/$defs/issue_summary"}, + "barrier_sha256": {"$ref": "#/$defs/sha256"} + }, + "allOf": [ + { + "if": { + "properties": {"plan_status": {"const": "TECHNICAL_INCOMPLETE"}}, + "required": ["plan_status"] + }, + "then": { + "properties": { + "route": {"const": "STOP_TECHNICAL_INCOMPLETE"}, + "consumer_authorized": {"const": false}, + "artifact_rows": { + "minItems": 1, + "maxItems": 1, + "contains": { + "type": "object", + "properties": { + "path": {"pattern": "(?:^|/)plan/technical_diagnostic\\.json$"}, + "schema_ref": {"const": "schemas/relief_plan.schema.json#/$defs/technical_diagnostic"} + }, + "required": ["path", "schema_ref"] + } + }, + "group_ids": {"maxItems": 0}, + "clean_draft_group_ids": {"maxItems": 0}, + "review_draft_group_ids": {"maxItems": 0}, + "worknote_only_group_ids": {"maxItems": 0} + } + }, + "else": { + "properties": { + "route": {"const": "TO_S2_30"}, + "consumer_authorized": {"const": true}, + "artifact_rows": { + "minItems": 9, + "contains": { + "type": "object", + "properties": { + "path": {"pattern": "(?:^|/)plan/canonical_relief_plan\\.json$"}, + "schema_ref": {"const": "schemas/relief_plan.schema.json#/$defs/canonical_relief_plan"} + }, + "required": ["path", "schema_ref"] + } + } + } + } + } + ], + "x-cross-field-invariants": [ + "artifact_rows paths are unique and canonical-codepoint sorted", + "artifact_set_digest covers exactly artifact_rows and excludes this barrier", + "group partition arrays are pairwise disjoint and their union equals group_ids", + "barrier_sha256 is the canonical object hash with barrier_sha256 removed" + ] + }, + "issue_ledger_plan": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "base_ledger_sha256", "preserved_base_issue_ids", "plan_issues"], + "properties": { + "schema_version": {"const": "stage2_issue_ledger_plan.v1"}, + "base_ledger_sha256": {"$ref": "#/$defs/sha256"}, + "preserved_base_issue_ids": {"$ref": "#/$defs/string_set"}, + "plan_issues": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["issue_id", "code", "scope_ref", "status"], + "properties": { + "issue_id": {"$ref": "#/$defs/issue_ref"}, + "code": {"$ref": "#/$defs/nonempty_string"}, + "scope_ref": {"$ref": "#/$defs/nonempty_string"}, + "status": {"const": "OPEN"} + } + } + } + } + }, + "technical_diagnostic": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "workflow_id", + "run_binding_digest", + "attempt_id", + "failure_component", + "failure_code", + "failure_scope", + "message", + "input_snapshot_digest", + "source_refs", + "retryable", + "operator_action_required", + "diagnostic_sha256" + ], + "properties": { + "schema_version": {"const": "stage2_s2_20_technical_diagnostic.v1"}, + "workflow_id": {"const": "S2_20"}, + "run_binding_digest": {"$ref": "#/$defs/sha256"}, + "attempt_id": {"$ref": "#/$defs/nonempty_string"}, + "failure_component": {"enum": ["PREFLIGHT", "C20", "C21", "C22", "C25", "C26", "C27", "C28", "C29", "C30", "C35", "PUBLISH"]}, + "failure_code": {"$ref": "#/$defs/nonempty_string"}, + "failure_scope": {"enum": ["RUN_WIDE", "GROUP_LOCAL", "ATOMIC_CLAIM_LOCAL", "RETRIEVAL_REQUEST_LOCAL"]}, + "message": {"$ref": "#/$defs/nonempty_string"}, + "input_snapshot_digest": { + "oneOf": [ + {"$ref": "#/$defs/sha256"}, + {"type": "null"} + ] + }, + "source_refs": {"$ref": "#/$defs/string_set"}, + "retryable": {"type": "boolean"}, + "operator_action_required": {"const": true}, + "diagnostic_sha256": {"$ref": "#/$defs/sha256"} + } + } + }, + "x-runtime-ownership": { + "single_writer": "S2_20", + "status_last_barrier": "plan/plan_publish_status.json", + "normal_downstream_route": "TO_S2_30", + "technical_route": "STOP_TECHNICAL_INCOMPLETE", + "runtime_directory_scan_allowed": false, + "frozen_plan_mutation_by_s2_30_allowed": false + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/review_status.schema.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/review_status.schema.json index c0ca8359..02c7eac1 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/review_status.schema.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/schemas/review_status.schema.json @@ -5,6 +5,7 @@ "schema_version": "stage2_review_status.v1", "oneOf": [ {"$ref": "#/$defs/issue_ledger_base"}, + {"$ref": "relief_plan.schema.json#/$defs/issue_ledger_plan"}, {"$ref": "#/$defs/review_normalization_receipt"}, {"$ref": "#/$defs/status_only_commit"} ], diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/regression_manifest.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/regression_manifest.json index e8c6b982..2426bf15 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/regression_manifest.json +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/regression_manifest.json @@ -1195,6 +1195,47 @@ "closure_claimed": true, "closure_scope": "DESCRIPTOR_BYTES_AND_EXPECTED_CONTRACTS_ONLY" }, + "s2_20_fixture_binding": { + "binding_status": "OFFLINE_BYTES_BOUND_LEGAL_AND_LIVE_PENDING", + "fixture_manifest_path": "tests/fixtures/s2_20/regression_manifest.json", + "fixture_manifest_sha256": "2a1a163d62a041990d71feaef54f3c56753ea3aa02666d3dc4c631552f7ee746", + "logical_fixture_count": 17, + "payload_file_count": 8, + "payloads": [ + { + "path": "tests/fixtures/s2_20/actio_route_cap_matrix.json", + "sha256": "f922b3ec10cc1da44c62d6ca5cf9c8ec5a53f5328b1eaabf236db46448907ca8" + }, + { + "path": "tests/fixtures/s2_20/calculation_temporal_boundaries.json", + "sha256": "a31af03c418466b1dfc05f47ea05b58109fe142b813da2b3a8863e9d5f71004d" + }, + { + "path": "tests/fixtures/s2_20/case_type_rule_binding_matrix.json", + "sha256": "45e0f7470e2907d20d6359d192fe8320051bf8a278f8eaa8153fbe964819685c" + }, + { + "path": "tests/fixtures/s2_20/case_type_zero_multi_hash_mutations.json", + "sha256": "0fbe5f68f4e60dfdd4fd03b9f6f076526f1b3d273b328ec853e69b4f9c315180" + }, + { + "path": "tests/fixtures/s2_20/minimal_supported_portfolio.json", + "sha256": "4bcbcf19019e086e4eb4772cb55e87832c3a68c4829f67d50c6a7318dda79c86" + }, + { + "path": "tests/fixtures/s2_20/mixed_option_dispositions.json", + "sha256": "509f3f436c8107c03033324413560b706487279bf91f2e231bd43bbc5c86b8e0" + }, + { + "path": "tests/fixtures/s2_20/plan_publish_barrier_failure.json", + "sha256": "29afbbdfa609234b06473542c35df7083ca6313155456cc6d1ec311fd50ea275" + }, + { + "path": "tests/fixtures/s2_20/retrieval_replay_injection.json", + "sha256": "8fbed161a30d52d8f5018f238dbf2cd01e0dd84162c97e3293df62d82a7ca804" + } + ] + }, "test_sources": [ { "path": "tests/s2_00/test_canonical_ids.py", @@ -1203,7 +1244,7 @@ }, { "path": "tests/s2_00/test_cluster_bundle_compile.py", - "sha256": "9d9254af239abef75393698676a8b158de828f42406a85c1472399f6fa8cb17d", + "sha256": "aa31cc87246a5c8f6b82e98b9f86dd654bc9306dfc7849af19ec16cfd5137a2a", "status": "DEV_HASH_BOUND" }, { @@ -1213,12 +1254,12 @@ }, { "path": "tests/s2_00/test_failure_and_atomic_publish.py", - "sha256": "47c8ed00d713ba0be53aed3ac76b4d2396bd5557cfd5d4bdd93a5e9f0fbe2ac6", + "sha256": "128a82b4e413132c0ff4a61ae2dc7f85b6c1c050ea33ccf4e53f23ccc993aba6", "status": "DEV_HASH_BOUND" }, { "path": "tests/s2_00/test_inline_code_parity.py", - "sha256": "00b592fa94a9b7ea393f3a3504530f0602c4efc9bd54c84a272fc9ba3ca3a3b4", + "sha256": "8f0f55e8e1b970de572129946ab8bf34967c7fd84e316a9193509cab07a455ed", "status": "DEV_HASH_BOUND" }, { @@ -1228,8 +1269,48 @@ }, { "path": "tests/s2_00/test_schema_hash_and_signals.py", - "sha256": "71a9a15e5910d99c8e8e7b8b2f607b41244b3fd83a8c6e07d03bc0a0b986193c", + "sha256": "fc19c57dce1545590a52cc863d25a0175d0b2df30fbe499d6f57e7d30b8b36d8", "status": "DEV_HASH_BOUND" + }, + { + "path": "tests/s2_20/test_agent_and_inline_parity.py", + "sha256": "0f73c112b28088be191b6717cb24cac468762cc286f8dc806bf65d98a95bf262", + "status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING" + }, + { + "path": "tests/s2_20/test_calculation_and_reports.py", + "sha256": "c1bd36c351173d25e6d8e68d1a90d738b90aabb51b201ca13f4370654e584e00", + "status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING" + }, + { + "path": "tests/s2_20/test_inline_output_schema.py", + "sha256": "e2cb938dd639874a4b150997d66eacd89b22016c8fd3385a243edf781cd3e85b", + "status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING" + }, + { + "path": "tests/s2_20/test_offline_compilers.py", + "sha256": "5e75f659a9e5add1345f1596c92c0b143cb1e48110c746a8785fb5b38f1371af", + "status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING" + }, + { + "path": "tests/s2_20/test_option_group_and_binding.py", + "sha256": "6021d93fb47377cf6c00ccb5a880a7e85987ec5a1ed7976a84baca2d7fb39985", + "status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING" + }, + { + "path": "tests/s2_20/test_plan_publish_and_release.py", + "sha256": "6373571eb1123389bf5d585507a1f694d75ecfa9eb572cff2a0c5ad1d21751aa", + "status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING" + }, + { + "path": "tests/s2_20/test_regression_manifest_closure.py", + "sha256": "886ae584a7846acdb5499d52e1ae8ec177e8d1dc34aa1d43e99dbebafa255516", + "status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING" + }, + { + "path": "tests/s2_20/test_retrieval_and_pack.py", + "sha256": "c314cf27511b0fe065b8961196084a28b1c7c5cb2fed3dd5c8765172ba40462f", + "status": "OFFLINE_VERIFIED_LEGAL_AND_LIVE_PENDING" } ] } diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/actio_route_cap_matrix.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/actio_route_cap_matrix.json new file mode 100644 index 00000000..6d4cc4f2 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/actio_route_cap_matrix.json @@ -0,0 +1,13 @@ +{ + "schema_version": "stage2_s2_20_fixture_package.v1", + "fixture_package_id": "S2F20-ACTIO-ROUTE-CAP", + "status": "PENDING_LEGAL_ORACLE", + "cases": [ + { + "fixture_case_id": "S20-F10-ACTIO-ROUTE-CAP", + "source_refs": ["S2_20_SOW.md#11.2.10"], + "input": {"route_registry_status": "PENDING_LEGAL_CONTENT", "operand_refs": []}, + "mutations": [{"mutation_id": "M-F10-ROUTE", "single_defect": "ACTIO_ROUTE_NOT_APPROVED", "expected_issue_codes": ["ACTIO_ROUTE_PENDING_LEGAL_CONTENT"], "expected_plan_status": "PLAN_COMPLETE_WITH_REVIEW"}] + } + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/calculation_temporal_boundaries.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/calculation_temporal_boundaries.json new file mode 100644 index 00000000..9f1a74fe --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/calculation_temporal_boundaries.json @@ -0,0 +1,11 @@ +{ + "schema_version": "stage2_s2_20_fixture_package.v1", + "fixture_package_id": "S2F20-CALCULATION-BOUNDARIES", + "status": "PENDING_LEGAL_ORACLE", + "cases": [ + {"fixture_case_id": "S20-F07-CE01-INTEREST", "source_refs": ["S2_20_SOW.md#11.2.7"], "input": {"calculator_id": "CE-01", "law_value_id": null}, "mutations": [{"mutation_id": "M-F07-LAW-VALUE", "single_defect": "LAW_VALUE_NOT_RELEASED", "expected_issue_codes": ["LAW_VALUE_RELEASE_PENDING"], "expected_calculation_status": "MISSING_OPERAND"}]}, + {"fixture_case_id": "S20-F08-CE03-LIMITATION", "source_refs": ["S2_20_SOW.md#11.2.8"], "input": {"calculator_id": "CE-03", "event_date": null}, "mutations": [{"mutation_id": "M-F08-DATE", "single_defect": "REQUIRED_EVENT_DATE_MISSING", "expected_issue_codes": ["CALCULATION_OPERAND_MISSING"], "expected_calculation_status": "MISSING_OPERAND"}]}, + {"fixture_case_id": "S20-F09-CE08-RESERVED-SHARE", "source_refs": ["S2_20_SOW.md#11.2.9"], "input": {"calculator_id": "CE-08", "inheritance_opening_date": null}, "mutations": [{"mutation_id": "M-F09-TEMPORAL", "single_defect": "TEMPORAL_BRANCH_INPUT_MISSING", "expected_issue_codes": ["TEMPORAL_BRANCH_UNRESOLVED"], "expected_calculation_status": "MISSING_OPERAND"}]}, + {"fixture_case_id": "S20-F11-CE13-COURT-VALUE", "source_refs": ["S2_20_SOW.md#11.2.11"], "input": {"calculator_id": "CE-13", "court_value_rule_ref": null}, "mutations": [{"mutation_id": "M-F11-RULE", "single_defect": "COURT_VALUE_RULE_NOT_APPROVED", "expected_issue_codes": ["CALCULATION_RULE_PENDING_LEGAL_CONTENT"], "expected_calculation_status": "ACTIVATION_GAP"}]} + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/case_type_rule_binding_matrix.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/case_type_rule_binding_matrix.json new file mode 100644 index 00000000..5ff016a9 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/case_type_rule_binding_matrix.json @@ -0,0 +1,20 @@ +{ + "schema_version": "stage2_s2_20_fixture_package.v1", + "fixture_package_id": "S2F20-BINDING-ROW-KINDS", + "status": "STRUCTURAL_ORACLE_LEGAL_CONTENT_PENDING", + "cases": [ + { + "fixture_case_id": "S20-F05-ROW-KIND-BRANCH", + "source_refs": ["S2_20_SOW.md#11.2.5"], + "input": { + "rows": [ + {"case_type_id": "CT-F05-CLAIM", "row_kind": "CLAIM", "claim_capable_disposition": "CLAIM_CAPABLE"}, + {"case_type_id": "CT-F05-GENERIC", "row_kind": "GENERIC", "claim_capable_disposition": "PENDING_LEGAL_CONTENT"}, + {"case_type_id": "CT-F05-GROUP", "row_kind": "GROUP", "claim_capable_disposition": "CLASSIFICATION_ONLY"}, + {"case_type_id": "CT-F05-DOCTRINE", "row_kind": "DOCTRINE_TOPIC", "claim_capable_disposition": "COMPANION_ONLY"} + ] + }, + "mutations": [{"mutation_id": "M-F05-GROUP-AS-CLAIM", "single_defect": "NON_CLAIM_ROW_SELECTED_AS_ATOMIC_CLAIM", "expected_issue_codes": ["CASE_TYPE_ROW_NOT_CLAIM_CAPABLE"], "expected_binding_status": "UNRESOLVED"}] + } + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/case_type_zero_multi_hash_mutations.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/case_type_zero_multi_hash_mutations.json new file mode 100644 index 00000000..426d4bc5 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/case_type_zero_multi_hash_mutations.json @@ -0,0 +1,18 @@ +{ + "schema_version": "stage2_s2_20_fixture_package.v1", + "fixture_package_id": "S2F20-BINDING-MUTATIONS", + "status": "STRUCTURAL_ORACLE", + "cases": [ + { + "fixture_case_id": "S20-F04-CASE-RULE-EXACTNESS", + "source_refs": ["S2_20_SOW.md#11.2.4"], + "input": {"signature_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, + "mutations": [ + {"mutation_id": "M-F04-ZERO", "single_defect": "ZERO_CASE_TYPE_MATCH", "expected_issue_codes": ["CASE_TYPE_BINDING_ZERO_MATCH"], "expected_binding_status": "UNRESOLVED"}, + {"mutation_id": "M-F04-MULTI", "single_defect": "MULTIPLE_CASE_TYPE_MATCH", "expected_issue_codes": ["CASE_TYPE_BINDING_MULTI_MATCH"], "expected_binding_status": "CONFLICT"}, + {"mutation_id": "M-F04-ALIAS", "single_defect": "FUZZY_ALIAS_ONLY_MATCH", "expected_issue_codes": ["CASE_TYPE_BINDING_ZERO_MATCH"], "expected_binding_status": "UNRESOLVED"}, + {"mutation_id": "M-F04-HASH", "single_defect": "RULE_DOCUMENT_HASH_MISMATCH", "expected_issue_codes": ["RULE_HASH_MISMATCH"], "expected_binding_status": "CONFLICT"} + ] + } + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/minimal_supported_portfolio.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/minimal_supported_portfolio.json new file mode 100644 index 00000000..3311ef7e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/minimal_supported_portfolio.json @@ -0,0 +1,25 @@ +{ + "schema_version": "stage2_s2_20_fixture_package.v1", + "fixture_package_id": "S2F20-MINIMAL-PORTFOLIO", + "status": "STRUCTURAL_ORACLE_LEGAL_CONTENT_PENDING", + "cases": [ + { + "fixture_case_id": "S20-F01-SINGLE-OPTION-GROUP", + "source_refs": ["S2_20_SOW.md#11.2.1"], + "input": { + "claim_option_ids": ["CO-F01-001"], + "relations": [], + "legal_content_status": "PENDING_LEGAL_CONTENT" + }, + "mutations": [ + { + "mutation_id": "M-F01-NONE", + "single_defect": "NONE", + "expected_plan_status": "PLAN_COMPLETE_WITH_REVIEW", + "expected_route": "TO_S2_30", + "expected_issue_codes": ["LEGAL_CONTENT_PENDING"] + } + ] + } + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/mixed_option_dispositions.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/mixed_option_dispositions.json new file mode 100644 index 00000000..3867839c --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/mixed_option_dispositions.json @@ -0,0 +1,25 @@ +{ + "schema_version": "stage2_s2_20_fixture_package.v1", + "fixture_package_id": "S2F20-OPTION-CONSERVATION", + "status": "STRUCTURAL_ORACLE", + "cases": [ + { + "fixture_case_id": "S20-F02-MIXED-PORTFOLIO", + "source_refs": ["S2_20_SOW.md#11.2.2"], + "input": {"option_ids": ["CO-P", "CO-A", "CO-C", "CO-X", "CO-R"]}, + "mutations": [{"mutation_id": "M-F02-NONE", "single_defect": "NONE", "expected_partition_count": 5, "expected_issue_codes": []}] + }, + { + "fixture_case_id": "S20-F03-CLIENT-NO-SUE", + "source_refs": ["S2_20_SOW.md#11.2.3"], + "input": {"claim_option_id": "CO-NOSUE", "legal_decision": "SUPPORTED", "client_disposition": "DEFERRED_BY_CLIENT_INSTRUCTION", "instruction_ref": "CI-F03-001"}, + "mutations": [{"mutation_id": "M-F03-DROP-INSTRUCTION", "single_defect": "CLIENT_INSTRUCTION_REF_REMOVED", "expected_issue_codes": ["CLIENT_INSTRUCTION_REF_REQUIRED"], "expected_disposition": "DEFERRED"}] + }, + { + "fixture_case_id": "S20-F15-OPTION-SILENT-LOSS", + "source_refs": ["S2_20_SOW.md#11.2.15"], + "input": {"all_option_ids": ["CO-1", "CO-2"], "selected_option_ids": ["CO-1"], "alternative_option_ids": [], "excluded_option_ids": [], "deferred_option_ids": []}, + "mutations": [{"mutation_id": "M-F15-LOSS", "single_defect": "OPTION_MISSING_FROM_PARTITION", "expected_issue_codes": ["OPTION_CONSERVATION_FAILED"], "expected_route": "STOP_TECHNICAL_INCOMPLETE"}] + } + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/plan_publish_barrier_failure.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/plan_publish_barrier_failure.json new file mode 100644 index 00000000..58daf621 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/plan_publish_barrier_failure.json @@ -0,0 +1,11 @@ +{ + "schema_version": "stage2_s2_20_fixture_package.v1", + "fixture_package_id": "S2F20-PUBLISH-AND-EDGE", + "status": "STRUCTURAL_ORACLE", + "cases": [ + {"fixture_case_id": "S20-F06-PARTY-TITLE", "source_refs": ["S2_20_SOW.md#11.2.6"], "input": {"required_party_refs": ["BO-P1"], "present_party_refs": []}, "mutations": [{"mutation_id": "M-F06-PARTY", "single_defect": "REQUIRED_PARTY_MISSING", "expected_issue_codes": ["REQUIRED_PARTY_MISSING"], "expected_plan_status": "PLAN_COMPLETE_WITH_REVIEW"}]}, + {"fixture_case_id": "S20-F14-EXHIBIT-LABEL", "source_refs": ["S2_20_SOW.md#11.2.14"], "input": {"evidence_id": "EV-F14", "existing_label": "갑 제1호증"}, "mutations": [{"mutation_id": "M-F14-RELABEL", "single_defect": "EXISTING_EXHIBIT_LABEL_CHANGED", "expected_issue_codes": ["EXHIBIT_LABEL_STABILITY_FAILED"], "expected_route": "STOP_TECHNICAL_INCOMPLETE"}]}, + {"fixture_case_id": "S20-F16-PUBLISH-BARRIER", "source_refs": ["S2_20_SOW.md#11.2.16"], "input": {"artifact_count": 2, "written_artifact_count": 1}, "mutations": [{"mutation_id": "M-F16-EARLY", "single_defect": "BARRIER_WRITTEN_BEFORE_ALL_ARTIFACTS", "expected_issue_codes": ["BARRIER_PRECONDITION_FAILED"], "expected_route": "STOP_TECHNICAL_INCOMPLETE"}]}, + {"fixture_case_id": "S20-F17-LEGACY-PATH", "source_refs": ["S2_20_SOW.md#11.2.17"], "input": {"requested_path": "YAML_Prompts/2. Stage_2/v.3/Stage_2_2_D_complete.yml"}, "mutations": [{"mutation_id": "M-F17-LEGACY", "single_defect": "LEGACY_STAGE2_V0_V3_PATH_INJECTION", "expected_issue_codes": ["LEGACY_STAGE2_RUNTIME_DEPENDENCY_FORBIDDEN"], "expected_route": "STOP_TECHNICAL_INCOMPLETE"}]} + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/regression_manifest.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/regression_manifest.json new file mode 100644 index 00000000..6c16559e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/regression_manifest.json @@ -0,0 +1,408 @@ +{ + "schema_version": "stage2_s2_20_regression_manifest.v1", + "manifest_id": "S2-20-REGRESSION-MANIFEST", + "status": "STRUCTURAL_ORACLES_BOUND_LEGAL_ORACLES_PENDING", + "hash_algorithm": "SHA-256", + "canonical_oracle_algorithm": "SORTED-COMPACT-JSON-SHA256", + "logical_fixture_count": 17, + "payload_file_count": 8, + "entries": [ + { + "fixture_case_id": "S20-F01-SINGLE-OPTION-GROUP", + "payload_path": "tests/fixtures/s2_20/minimal_supported_portfolio.json", + "payload_sha256": "4bcbcf19019e086e4eb4772cb55e87832c3a68c4829f67d50c6a7318dda79c86", + "case_selector": "/cases/0", + "mutation_oracles": [ + { + "mutation_id": "M-F01-NONE", + "mutation_selector": "/cases/0/mutations/0", + "expected": { + "expected_plan_status": "PLAN_COMPLETE_WITH_REVIEW", + "expected_route": "TO_S2_30", + "expected_issue_codes": [ + "LEGAL_CONTENT_PENDING" + ] + } + } + ], + "oracle_sha256": "b440e548d0404bbac592a979d19c48daa6360de16523f01be35484da254b0818", + "oracle_status": "STRUCTURAL_ORACLE_LEGAL_CONTENT_PENDING" + }, + { + "fixture_case_id": "S20-F02-MIXED-PORTFOLIO", + "payload_path": "tests/fixtures/s2_20/mixed_option_dispositions.json", + "payload_sha256": "509f3f436c8107c03033324413560b706487279bf91f2e231bd43bbc5c86b8e0", + "case_selector": "/cases/0", + "mutation_oracles": [ + { + "mutation_id": "M-F02-NONE", + "mutation_selector": "/cases/0/mutations/0", + "expected": { + "expected_partition_count": 5, + "expected_issue_codes": [] + } + } + ], + "oracle_sha256": "44a4db41809005fac50084cf93b263ac48873928a04ba9618e42f00eaadd2351", + "oracle_status": "STRUCTURAL_ORACLE" + }, + { + "fixture_case_id": "S20-F03-CLIENT-NO-SUE", + "payload_path": "tests/fixtures/s2_20/mixed_option_dispositions.json", + "payload_sha256": "509f3f436c8107c03033324413560b706487279bf91f2e231bd43bbc5c86b8e0", + "case_selector": "/cases/1", + "mutation_oracles": [ + { + "mutation_id": "M-F03-DROP-INSTRUCTION", + "mutation_selector": "/cases/1/mutations/0", + "expected": { + "expected_issue_codes": [ + "CLIENT_INSTRUCTION_REF_REQUIRED" + ], + "expected_disposition": "DEFERRED" + } + } + ], + "oracle_sha256": "0be3285cc4ff27423ffecff9beab85e330b52016e12cb277bd37fe20026dd9ab", + "oracle_status": "STRUCTURAL_ORACLE" + }, + { + "fixture_case_id": "S20-F04-CASE-RULE-EXACTNESS", + "payload_path": "tests/fixtures/s2_20/case_type_zero_multi_hash_mutations.json", + "payload_sha256": "0fbe5f68f4e60dfdd4fd03b9f6f076526f1b3d273b328ec853e69b4f9c315180", + "case_selector": "/cases/0", + "mutation_oracles": [ + { + "mutation_id": "M-F04-ZERO", + "mutation_selector": "/cases/0/mutations/0", + "expected": { + "expected_issue_codes": [ + "CASE_TYPE_BINDING_ZERO_MATCH" + ], + "expected_binding_status": "UNRESOLVED" + } + }, + { + "mutation_id": "M-F04-MULTI", + "mutation_selector": "/cases/0/mutations/1", + "expected": { + "expected_issue_codes": [ + "CASE_TYPE_BINDING_MULTI_MATCH" + ], + "expected_binding_status": "CONFLICT" + } + }, + { + "mutation_id": "M-F04-ALIAS", + "mutation_selector": "/cases/0/mutations/2", + "expected": { + "expected_issue_codes": [ + "CASE_TYPE_BINDING_ZERO_MATCH" + ], + "expected_binding_status": "UNRESOLVED" + } + }, + { + "mutation_id": "M-F04-HASH", + "mutation_selector": "/cases/0/mutations/3", + "expected": { + "expected_issue_codes": [ + "RULE_HASH_MISMATCH" + ], + "expected_binding_status": "CONFLICT" + } + } + ], + "oracle_sha256": "acc63d505e72a4830f2c36d63693a7cf4d4867a6e854ca5434589f2fcabcc2cd", + "oracle_status": "STRUCTURAL_ORACLE" + }, + { + "fixture_case_id": "S20-F05-ROW-KIND-BRANCH", + "payload_path": "tests/fixtures/s2_20/case_type_rule_binding_matrix.json", + "payload_sha256": "45e0f7470e2907d20d6359d192fe8320051bf8a278f8eaa8153fbe964819685c", + "case_selector": "/cases/0", + "mutation_oracles": [ + { + "mutation_id": "M-F05-GROUP-AS-CLAIM", + "mutation_selector": "/cases/0/mutations/0", + "expected": { + "expected_issue_codes": [ + "CASE_TYPE_ROW_NOT_CLAIM_CAPABLE" + ], + "expected_binding_status": "UNRESOLVED" + } + } + ], + "oracle_sha256": "3728ee97123d488b8de7a5526f74ba7c986f4ba362b195daaa0efb45f3a97264", + "oracle_status": "STRUCTURAL_ORACLE_LEGAL_CONTENT_PENDING" + }, + { + "fixture_case_id": "S20-F06-PARTY-TITLE", + "payload_path": "tests/fixtures/s2_20/plan_publish_barrier_failure.json", + "payload_sha256": "29afbbdfa609234b06473542c35df7083ca6313155456cc6d1ec311fd50ea275", + "case_selector": "/cases/0", + "mutation_oracles": [ + { + "mutation_id": "M-F06-PARTY", + "mutation_selector": "/cases/0/mutations/0", + "expected": { + "expected_issue_codes": [ + "REQUIRED_PARTY_MISSING" + ], + "expected_plan_status": "PLAN_COMPLETE_WITH_REVIEW" + } + } + ], + "oracle_sha256": "849715f68bd858692210ea964619efd1aaab20a502d367464f3fad13f21f55ac", + "oracle_status": "STRUCTURAL_ORACLE" + }, + { + "fixture_case_id": "S20-F07-CE01-INTEREST", + "payload_path": "tests/fixtures/s2_20/calculation_temporal_boundaries.json", + "payload_sha256": "a31af03c418466b1dfc05f47ea05b58109fe142b813da2b3a8863e9d5f71004d", + "case_selector": "/cases/0", + "mutation_oracles": [ + { + "mutation_id": "M-F07-LAW-VALUE", + "mutation_selector": "/cases/0/mutations/0", + "expected": { + "expected_issue_codes": [ + "LAW_VALUE_RELEASE_PENDING" + ], + "expected_calculation_status": "MISSING_OPERAND" + } + } + ], + "oracle_sha256": "555d54c5e2f44f6dc27d1509217fe0887c96d1d8e26e383f788fd8df9c9e158a", + "oracle_status": "PENDING_LEGAL_ORACLE" + }, + { + "fixture_case_id": "S20-F08-CE03-LIMITATION", + "payload_path": "tests/fixtures/s2_20/calculation_temporal_boundaries.json", + "payload_sha256": "a31af03c418466b1dfc05f47ea05b58109fe142b813da2b3a8863e9d5f71004d", + "case_selector": "/cases/1", + "mutation_oracles": [ + { + "mutation_id": "M-F08-DATE", + "mutation_selector": "/cases/1/mutations/0", + "expected": { + "expected_issue_codes": [ + "CALCULATION_OPERAND_MISSING" + ], + "expected_calculation_status": "MISSING_OPERAND" + } + } + ], + "oracle_sha256": "6eb39a319d89453a14b3e32ed0f81677b0d20f18a729314c5e289779f66b3f54", + "oracle_status": "PENDING_LEGAL_ORACLE" + }, + { + "fixture_case_id": "S20-F09-CE08-RESERVED-SHARE", + "payload_path": "tests/fixtures/s2_20/calculation_temporal_boundaries.json", + "payload_sha256": "a31af03c418466b1dfc05f47ea05b58109fe142b813da2b3a8863e9d5f71004d", + "case_selector": "/cases/2", + "mutation_oracles": [ + { + "mutation_id": "M-F09-TEMPORAL", + "mutation_selector": "/cases/2/mutations/0", + "expected": { + "expected_issue_codes": [ + "TEMPORAL_BRANCH_UNRESOLVED" + ], + "expected_calculation_status": "MISSING_OPERAND" + } + } + ], + "oracle_sha256": "2adbcad6a3c4bd34e8363d7082c1b5c31c2edfb717d52d5e5b2ecef5169852a1", + "oracle_status": "PENDING_LEGAL_ORACLE" + }, + { + "fixture_case_id": "S20-F10-ACTIO-ROUTE-CAP", + "payload_path": "tests/fixtures/s2_20/actio_route_cap_matrix.json", + "payload_sha256": "f922b3ec10cc1da44c62d6ca5cf9c8ec5a53f5328b1eaabf236db46448907ca8", + "case_selector": "/cases/0", + "mutation_oracles": [ + { + "mutation_id": "M-F10-ROUTE", + "mutation_selector": "/cases/0/mutations/0", + "expected": { + "expected_issue_codes": [ + "ACTIO_ROUTE_PENDING_LEGAL_CONTENT" + ], + "expected_plan_status": "PLAN_COMPLETE_WITH_REVIEW" + } + } + ], + "oracle_sha256": "0c73080bc0503bfff43802ecb720a3264e080cdd5427d3f4a826964aa97ff89e", + "oracle_status": "PENDING_LEGAL_ORACLE" + }, + { + "fixture_case_id": "S20-F11-CE13-COURT-VALUE", + "payload_path": "tests/fixtures/s2_20/calculation_temporal_boundaries.json", + "payload_sha256": "a31af03c418466b1dfc05f47ea05b58109fe142b813da2b3a8863e9d5f71004d", + "case_selector": "/cases/3", + "mutation_oracles": [ + { + "mutation_id": "M-F11-RULE", + "mutation_selector": "/cases/3/mutations/0", + "expected": { + "expected_issue_codes": [ + "CALCULATION_RULE_PENDING_LEGAL_CONTENT" + ], + "expected_calculation_status": "ACTIVATION_GAP" + } + } + ], + "oracle_sha256": "93a2e418f29fefa1de105ea06d40dc48e5d06780ef687299fc98006c5feecddf", + "oracle_status": "PENDING_LEGAL_ORACLE" + }, + { + "fixture_case_id": "S20-F12-RETRIEVAL-BOUNDARY", + "payload_path": "tests/fixtures/s2_20/retrieval_replay_injection.json", + "payload_sha256": "8fbed161a30d52d8f5018f238dbf2cd01e0dd84162c97e3293df62d82a7ca804", + "case_selector": "/cases/0", + "mutation_oracles": [ + { + "mutation_id": "M-F12-NO-HIT", + "mutation_selector": "/cases/0/mutations/0", + "expected": { + "expected_issue_codes": [ + "CORPUS_COVERAGE_MISSING" + ], + "expected_plan_status": "PLAN_COMPLETE_WITH_REVIEW" + } + }, + { + "mutation_id": "M-F12-REPLAY", + "mutation_selector": "/cases/0/mutations/1", + "expected": { + "expected_issue_codes": [ + "RETRIEVAL_TECHNICAL_INCOMPLETE" + ], + "expected_route": "STOP_TECHNICAL_INCOMPLETE" + } + }, + { + "mutation_id": "M-F12-INJECTION", + "mutation_selector": "/cases/0/mutations/2", + "expected": { + "expected_issue_codes": [ + "RETRIEVED_INSTRUCTION_TREATED_AS_DATA" + ], + "expected_route": "TO_S2_30" + } + } + ], + "oracle_sha256": "ba40f993db13b8aefed3fd15881994623935b6b38408b16559321a8a5b44ccb1", + "oracle_status": "STRUCTURAL_ORACLE" + }, + { + "fixture_case_id": "S20-F13-CORPUS-INTEGRITY", + "payload_path": "tests/fixtures/s2_20/retrieval_replay_injection.json", + "payload_sha256": "8fbed161a30d52d8f5018f238dbf2cd01e0dd84162c97e3293df62d82a7ca804", + "case_selector": "/cases/1", + "mutation_oracles": [ + { + "mutation_id": "M-F13-HASH", + "mutation_selector": "/cases/1/mutations/0", + "expected": { + "expected_issue_codes": [ + "CORPUS_HASH_MISMATCH" + ], + "expected_route": "STOP_TECHNICAL_INCOMPLETE" + } + } + ], + "oracle_sha256": "f0daf8cc34b9d567fec2eea6ac1b3db983011376253aefbc7c0e92467d0949e9", + "oracle_status": "STRUCTURAL_ORACLE" + }, + { + "fixture_case_id": "S20-F14-EXHIBIT-LABEL", + "payload_path": "tests/fixtures/s2_20/plan_publish_barrier_failure.json", + "payload_sha256": "29afbbdfa609234b06473542c35df7083ca6313155456cc6d1ec311fd50ea275", + "case_selector": "/cases/1", + "mutation_oracles": [ + { + "mutation_id": "M-F14-RELABEL", + "mutation_selector": "/cases/1/mutations/0", + "expected": { + "expected_issue_codes": [ + "EXHIBIT_LABEL_STABILITY_FAILED" + ], + "expected_route": "STOP_TECHNICAL_INCOMPLETE" + } + } + ], + "oracle_sha256": "334d55f31e264b8ad854915d0c3e4f6fde97a6c259b4e073d2bc078d22fc6102", + "oracle_status": "STRUCTURAL_ORACLE" + }, + { + "fixture_case_id": "S20-F15-OPTION-SILENT-LOSS", + "payload_path": "tests/fixtures/s2_20/mixed_option_dispositions.json", + "payload_sha256": "509f3f436c8107c03033324413560b706487279bf91f2e231bd43bbc5c86b8e0", + "case_selector": "/cases/2", + "mutation_oracles": [ + { + "mutation_id": "M-F15-LOSS", + "mutation_selector": "/cases/2/mutations/0", + "expected": { + "expected_issue_codes": [ + "OPTION_CONSERVATION_FAILED" + ], + "expected_route": "STOP_TECHNICAL_INCOMPLETE" + } + } + ], + "oracle_sha256": "6a1d67f037ac34bfe959881f0ab43abdb089a430d1d739fb17e045f07afa602b", + "oracle_status": "STRUCTURAL_ORACLE" + }, + { + "fixture_case_id": "S20-F16-PUBLISH-BARRIER", + "payload_path": "tests/fixtures/s2_20/plan_publish_barrier_failure.json", + "payload_sha256": "29afbbdfa609234b06473542c35df7083ca6313155456cc6d1ec311fd50ea275", + "case_selector": "/cases/2", + "mutation_oracles": [ + { + "mutation_id": "M-F16-EARLY", + "mutation_selector": "/cases/2/mutations/0", + "expected": { + "expected_issue_codes": [ + "BARRIER_PRECONDITION_FAILED" + ], + "expected_route": "STOP_TECHNICAL_INCOMPLETE" + } + } + ], + "oracle_sha256": "7874c7a6a10dc18ed90e836703af72687fd98b6aef049ab992fd4fdf5bba66c1", + "oracle_status": "STRUCTURAL_ORACLE" + }, + { + "fixture_case_id": "S20-F17-LEGACY-PATH", + "payload_path": "tests/fixtures/s2_20/plan_publish_barrier_failure.json", + "payload_sha256": "29afbbdfa609234b06473542c35df7083ca6313155456cc6d1ec311fd50ea275", + "case_selector": "/cases/3", + "mutation_oracles": [ + { + "mutation_id": "M-F17-LEGACY", + "mutation_selector": "/cases/3/mutations/0", + "expected": { + "expected_issue_codes": [ + "LEGACY_STAGE2_RUNTIME_DEPENDENCY_FORBIDDEN" + ], + "expected_route": "STOP_TECHNICAL_INCOMPLETE" + } + } + ], + "oracle_sha256": "6cb10a2919317d89aab6b2158ef685085ff2918286ee99ffc14229fb747b8310", + "oracle_status": "STRUCTURAL_ORACLE" + } + ], + "closure": { + "fixture_case_id_unique": true, + "each_case_exactly_one_payload_selector": true, + "each_case_at_least_one_mutation": true, + "payload_and_oracle_hash_required": true, + "legal_oracle_pending_does_not_prove_legal_correctness": true, + "legacy_v0_v3_path_allowed_only_as_negative_fixture": "S20-F17-LEGACY-PATH" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/retrieval_replay_injection.json b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/retrieval_replay_injection.json new file mode 100644 index 00000000..985fc8a8 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/fixtures/s2_20/retrieval_replay_injection.json @@ -0,0 +1,23 @@ +{ + "schema_version": "stage2_s2_20_fixture_package.v1", + "fixture_package_id": "S2F20-RETRIEVAL", + "status": "STRUCTURAL_ORACLE", + "cases": [ + { + "fixture_case_id": "S20-F12-RETRIEVAL-BOUNDARY", + "source_refs": ["S2_20_SOW.md#11.2.12"], + "input": {"case_type_id": "CT-F12", "sub_rule_id": "SR-F12", "snapshot_id": "SNAP-F12"}, + "mutations": [ + {"mutation_id": "M-F12-NO-HIT", "single_defect": "NO_APPROVED_HIT", "expected_issue_codes": ["CORPUS_COVERAGE_MISSING"], "expected_plan_status": "PLAN_COMPLETE_WITH_REVIEW"}, + {"mutation_id": "M-F12-REPLAY", "single_defect": "SAME_QUERY_ORDER_DRIFT", "expected_issue_codes": ["RETRIEVAL_TECHNICAL_INCOMPLETE"], "expected_route": "STOP_TECHNICAL_INCOMPLETE"}, + {"mutation_id": "M-F12-INJECTION", "single_defect": "CHUNK_CONTAINS_EMBEDDED_INSTRUCTION", "expected_issue_codes": ["RETRIEVED_INSTRUCTION_TREATED_AS_DATA"], "expected_route": "TO_S2_30"} + ] + }, + { + "fixture_case_id": "S20-F13-CORPUS-INTEGRITY", + "source_refs": ["S2_20_SOW.md#11.2.13"], + "input": {"source_doc_id": "DOC-F13", "approval_status": "APPROVED"}, + "mutations": [{"mutation_id": "M-F13-HASH", "single_defect": "SOURCE_HASH_MISMATCH", "expected_issue_codes": ["CORPUS_HASH_MISMATCH"], "expected_route": "STOP_TECHNICAL_INCOMPLETE"}] + } + ] +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_cluster_bundle_compile.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_cluster_bundle_compile.py index 9e93d8c2..2c16e42d 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_cluster_bundle_compile.py +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_cluster_bundle_compile.py @@ -603,7 +603,18 @@ class ClusterAndBundleTests(unittest.TestCase): release_path = ROOT / "manifest/stage2_release.json" release = s2.load_json_strict(release_path.read_bytes()) binding_path = ROOT / "deployment/stage2_code_executor_binding.yml" - binding = yaml.safe_load(binding_path.read_text(encoding="utf-8")) + binding_wrapper = yaml.safe_load(binding_path.read_text(encoding="utf-8")) + self.assertEqual( + binding_wrapper.get("schema_version"), + "stage2_code_executor_binding.v3", + ) + matches = [ + row + for row in binding_wrapper.get("stage_bindings", []) + if row.get("stage_id") == "S2_00" + ] + self.assertEqual(len(matches), 1) + binding = matches[0] def assert_ref(ref: dict[str, str]) -> Path: path = ROOT / ref["path"] diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_cluster_bundle_compile.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_cluster_bundle_compile.txt index 9e93d8c2..2c16e42d 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_cluster_bundle_compile.txt +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_cluster_bundle_compile.txt @@ -603,7 +603,18 @@ class ClusterAndBundleTests(unittest.TestCase): release_path = ROOT / "manifest/stage2_release.json" release = s2.load_json_strict(release_path.read_bytes()) binding_path = ROOT / "deployment/stage2_code_executor_binding.yml" - binding = yaml.safe_load(binding_path.read_text(encoding="utf-8")) + binding_wrapper = yaml.safe_load(binding_path.read_text(encoding="utf-8")) + self.assertEqual( + binding_wrapper.get("schema_version"), + "stage2_code_executor_binding.v3", + ) + matches = [ + row + for row in binding_wrapper.get("stage_bindings", []) + if row.get("stage_id") == "S2_00" + ] + self.assertEqual(len(matches), 1) + binding = matches[0] def assert_ref(ref: dict[str, str]) -> Path: path = ROOT / ref["path"] diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_failure_and_atomic_publish.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_failure_and_atomic_publish.py index d379beca..11c5f52b 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_failure_and_atomic_publish.py +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_failure_and_atomic_publish.py @@ -3,7 +3,7 @@ from __future__ import annotations import hashlib import io import json -from pathlib import Path +from pathlib import Path, PurePosixPath import sys import tempfile import unittest @@ -558,13 +558,23 @@ class FailureAndAtomicPublishTests(unittest.TestCase): test_source_rows = manifest["test_sources"] declared_test_paths = [row["path"] for row in test_source_rows] self.assertEqual(len(declared_test_paths), len(set(declared_test_paths))) - physical_test_paths = { + declared_s2_00_test_paths = { + relative + for relative in declared_test_paths + if relative.startswith("tests/s2_00/") + } + physical_s2_00_test_paths = { path.relative_to(ROOT).as_posix() for path in (ROOT / "tests" / "s2_00").glob("test_*.py") } - self.assertEqual(set(declared_test_paths), physical_test_paths) + self.assertEqual(declared_s2_00_test_paths, physical_s2_00_test_paths) for row in test_source_rows: relative = s2._safe_relative_path(row["path"]).as_posix() + self.assertIn( + PurePosixPath(relative).parent.as_posix(), + {"tests/s2_00", "tests/s2_20"}, + ) + self.assertTrue((ROOT / relative).is_file(), relative) observed_sha256 = hashlib.sha256((ROOT / relative).read_bytes()).hexdigest() self.assertEqual(observed_sha256, row["sha256"], relative) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_failure_and_atomic_publish.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_failure_and_atomic_publish.txt index d379beca..11c5f52b 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_failure_and_atomic_publish.txt +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_failure_and_atomic_publish.txt @@ -3,7 +3,7 @@ from __future__ import annotations import hashlib import io import json -from pathlib import Path +from pathlib import Path, PurePosixPath import sys import tempfile import unittest @@ -558,13 +558,23 @@ class FailureAndAtomicPublishTests(unittest.TestCase): test_source_rows = manifest["test_sources"] declared_test_paths = [row["path"] for row in test_source_rows] self.assertEqual(len(declared_test_paths), len(set(declared_test_paths))) - physical_test_paths = { + declared_s2_00_test_paths = { + relative + for relative in declared_test_paths + if relative.startswith("tests/s2_00/") + } + physical_s2_00_test_paths = { path.relative_to(ROOT).as_posix() for path in (ROOT / "tests" / "s2_00").glob("test_*.py") } - self.assertEqual(set(declared_test_paths), physical_test_paths) + self.assertEqual(declared_s2_00_test_paths, physical_s2_00_test_paths) for row in test_source_rows: relative = s2._safe_relative_path(row["path"]).as_posix() + self.assertIn( + PurePosixPath(relative).parent.as_posix(), + {"tests/s2_00", "tests/s2_20"}, + ) + self.assertTrue((ROOT / relative).is_file(), relative) observed_sha256 = hashlib.sha256((ROOT / relative).read_bytes()).hexdigest() self.assertEqual(observed_sha256, row["sha256"], relative) diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_inline_code_parity.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_inline_code_parity.py index 5de541ca..0b216f9c 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_inline_code_parity.py +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_inline_code_parity.py @@ -284,7 +284,7 @@ class DeployedArtifactParityTests(unittest.TestCase): mismatches = builder.compare_outputs(outputs) self.assertEqual(mismatches, [], json.dumps(mismatches, ensure_ascii=False, indent=2)) - def test_real_receipt_and_binding_validate_against_deployment_schema_v2(self) -> None: + def test_real_receipt_and_s2_00_binding_validate_against_deployment_schema_v3(self) -> None: schema = json.loads( (ROOT / "schemas/deployment.schema.json").read_text(encoding="utf-8") ) @@ -293,13 +293,15 @@ class DeployedArtifactParityTests(unittest.TestCase): encoding="utf-8" ) ) - binding = builder.parse_authoring_bytes( + binding_wrapper = builder.parse_authoring_bytes( (ROOT / "deployment/stage2_code_executor_binding.yml").read_bytes(), source="stage2_code_executor_binding.yml", ) + binding = builder.select_s2_00_binding(binding_wrapper) for value, definition in ( (receipt, "inline_code_receipt"), - (binding, "code_executor_binding"), + (binding_wrapper, "stage2_code_executor_binding_v3"), + (binding, "stage_code_executor_binding"), ): s2._validate_schema_node( value, diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_inline_code_parity.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_inline_code_parity.txt index 5de541ca..0b216f9c 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_inline_code_parity.txt +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_inline_code_parity.txt @@ -284,7 +284,7 @@ class DeployedArtifactParityTests(unittest.TestCase): mismatches = builder.compare_outputs(outputs) self.assertEqual(mismatches, [], json.dumps(mismatches, ensure_ascii=False, indent=2)) - def test_real_receipt_and_binding_validate_against_deployment_schema_v2(self) -> None: + def test_real_receipt_and_s2_00_binding_validate_against_deployment_schema_v3(self) -> None: schema = json.loads( (ROOT / "schemas/deployment.schema.json").read_text(encoding="utf-8") ) @@ -293,13 +293,15 @@ class DeployedArtifactParityTests(unittest.TestCase): encoding="utf-8" ) ) - binding = builder.parse_authoring_bytes( + binding_wrapper = builder.parse_authoring_bytes( (ROOT / "deployment/stage2_code_executor_binding.yml").read_bytes(), source="stage2_code_executor_binding.yml", ) + binding = builder.select_s2_00_binding(binding_wrapper) for value, definition in ( (receipt, "inline_code_receipt"), - (binding, "code_executor_binding"), + (binding_wrapper, "stage2_code_executor_binding_v3"), + (binding, "stage_code_executor_binding"), ): s2._validate_schema_node( value, diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_schema_hash_and_signals.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_schema_hash_and_signals.py index 3ffa6b24..264e13e4 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_schema_hash_and_signals.py +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_schema_hash_and_signals.py @@ -7,6 +7,8 @@ import sys import tempfile import unittest +import yaml + ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) @@ -21,6 +23,23 @@ def write_json(path: Path, value: object) -> str: class SchemaHashAndSignalTests(unittest.TestCase): + def test_v3_executor_binding_has_exactly_one_semantics_preserved_s2_00_row(self) -> None: + wrapper = yaml.safe_load( + (ROOT / "deployment/stage2_code_executor_binding.yml").read_text( + encoding="utf-8" + ) + ) + self.assertEqual(wrapper["schema_version"], "stage2_code_executor_binding.v3") + rows = wrapper["stage_bindings"] + self.assertEqual(len({row["stage_id"] for row in rows}), len(rows)) + matches = [row for row in rows if row["stage_id"] == "S2_00"] + self.assertEqual(len(matches), 1) + row = matches[0] + self.assertEqual(row["workflow_id"], "S2_00") + self.assertTrue(row["active_runtime_authority"]) + self.assertIsNone(row["external_mcp_contract"]) + self.assertEqual(row["tool_name"], "run_code") + def _stage1_source_rows(self) -> list[dict[str, object]]: rows = [ { diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_schema_hash_and_signals.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_schema_hash_and_signals.txt index 3ffa6b24..264e13e4 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_schema_hash_and_signals.txt +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_00/test_schema_hash_and_signals.txt @@ -7,6 +7,8 @@ import sys import tempfile import unittest +import yaml + ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) @@ -21,6 +23,23 @@ def write_json(path: Path, value: object) -> str: class SchemaHashAndSignalTests(unittest.TestCase): + def test_v3_executor_binding_has_exactly_one_semantics_preserved_s2_00_row(self) -> None: + wrapper = yaml.safe_load( + (ROOT / "deployment/stage2_code_executor_binding.yml").read_text( + encoding="utf-8" + ) + ) + self.assertEqual(wrapper["schema_version"], "stage2_code_executor_binding.v3") + rows = wrapper["stage_bindings"] + self.assertEqual(len({row["stage_id"] for row in rows}), len(rows)) + matches = [row for row in rows if row["stage_id"] == "S2_00"] + self.assertEqual(len(matches), 1) + row = matches[0] + self.assertEqual(row["workflow_id"], "S2_00") + self.assertTrue(row["active_runtime_authority"]) + self.assertIsNone(row["external_mcp_contract"]) + self.assertEqual(row["tool_name"], "run_code") + def _stage1_source_rows(self) -> list[dict[str, object]]: rows = [ { diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_agent_and_inline_parity.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_agent_and_inline_parity.py new file mode 100644 index 00000000..bf81f218 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_agent_and_inline_parity.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +"""Static S2_20 Agent/build parity tests.""" + +from __future__ import annotations + +import ast +import importlib.util +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +MAIN = ROOT.parent.parent + + +def load(name: str, path: Path): + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class AgentAndParityTest(unittest.TestCase): + def test_all_pure_core_sources_compile_without_forbidden_imports(self) -> None: + paths = [ + ROOT / "runtime/binding_signature_projection.py", + ROOT / "runtime/c25_case_type_bind.py", + ROOT / "runtime/c26_rule_branch_bind.py", + ROOT / "runtime/c27_query_plan.py", + ROOT / "runtime/c28_weaviate_retrieve.py", + ROOT / "runtime/c29_pack_verify.py", + ROOT / "runtime/calculators/registry_engine.py", + ROOT / "runtime/authority/authority_preflight.py", + ] + forbidden = {"subprocess", "socket", "urllib", "ftplib", "telnetlib", "importlib"} + for path in paths: + with self.subTest(path=path): + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + imports = set() + for node in ast.walk(tree): + if isinstance(node, ast.Import): + imports.update(alias.name.split(".")[0] for alias in node.names) + elif isinstance(node, ast.ImportFrom) and node.module: + imports.add(node.module.split(".")[0]) + self.assertFalse(imports & forbidden) + mirror = path.with_suffix(".txt") + self.assertTrue(mirror.is_file()) + self.assertEqual(path.read_bytes(), mirror.read_bytes()) + + def test_authoring_shape_when_sequential_stage_has_created_it(self) -> None: + authoring = MAIN / "Stage_2_S2_20.yml" + if not authoring.is_file(): + self.skipTest("sequential authoring/projection stage pending") + builder = load("s2_20_builder", ROOT / "offline_build/build_s2_20_inline_projection.py") + raw = authoring.read_bytes() + code = builder.extract_code(raw) + builder.validate_agent(raw, code) + document = builder.parse_authoring_bytes(raw, source=authoring.as_posix()) + stage, task = builder.extract_run_code_task(document) + self.assertEqual(len(stage["tasks"]), 1) + self.assertEqual(task["mcp"], "code-executor") + self.assertEqual(task["tool_name"], "run_code") + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_agent_and_inline_parity.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_agent_and_inline_parity.txt new file mode 100644 index 00000000..bf81f218 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_agent_and_inline_parity.txt @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +"""Static S2_20 Agent/build parity tests.""" + +from __future__ import annotations + +import ast +import importlib.util +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +MAIN = ROOT.parent.parent + + +def load(name: str, path: Path): + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class AgentAndParityTest(unittest.TestCase): + def test_all_pure_core_sources_compile_without_forbidden_imports(self) -> None: + paths = [ + ROOT / "runtime/binding_signature_projection.py", + ROOT / "runtime/c25_case_type_bind.py", + ROOT / "runtime/c26_rule_branch_bind.py", + ROOT / "runtime/c27_query_plan.py", + ROOT / "runtime/c28_weaviate_retrieve.py", + ROOT / "runtime/c29_pack_verify.py", + ROOT / "runtime/calculators/registry_engine.py", + ROOT / "runtime/authority/authority_preflight.py", + ] + forbidden = {"subprocess", "socket", "urllib", "ftplib", "telnetlib", "importlib"} + for path in paths: + with self.subTest(path=path): + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + imports = set() + for node in ast.walk(tree): + if isinstance(node, ast.Import): + imports.update(alias.name.split(".")[0] for alias in node.names) + elif isinstance(node, ast.ImportFrom) and node.module: + imports.add(node.module.split(".")[0]) + self.assertFalse(imports & forbidden) + mirror = path.with_suffix(".txt") + self.assertTrue(mirror.is_file()) + self.assertEqual(path.read_bytes(), mirror.read_bytes()) + + def test_authoring_shape_when_sequential_stage_has_created_it(self) -> None: + authoring = MAIN / "Stage_2_S2_20.yml" + if not authoring.is_file(): + self.skipTest("sequential authoring/projection stage pending") + builder = load("s2_20_builder", ROOT / "offline_build/build_s2_20_inline_projection.py") + raw = authoring.read_bytes() + code = builder.extract_code(raw) + builder.validate_agent(raw, code) + document = builder.parse_authoring_bytes(raw, source=authoring.as_posix()) + stage, task = builder.extract_run_code_task(document) + self.assertEqual(len(stage["tasks"]), 1) + self.assertEqual(task["mcp"], "code-executor") + self.assertEqual(task["tool_name"], "run_code") + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_calculation_and_reports.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_calculation_and_reports.py new file mode 100644 index 00000000..10f69b28 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_calculation_and_reports.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +"""Calculator dispatch, Decimal, and canonical receipt tests.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +SPEC = importlib.util.spec_from_file_location("calc", ROOT / "runtime/calculators/registry_engine.py") +if SPEC is None or SPEC.loader is None: + raise RuntimeError("calculator module unavailable") +calc = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(calc) + + +RUN_DIGEST = "a" * 64 +DESCRIPTOR_DIGEST = "b" * 64 +RECEIPT_REQUIRED = { + "schema_version", "calculation_receipt_id", "run_binding_digest", "atomic_claim_id", + "claim_group_id", "calculator_id", "activation_status", "calculation_status", + "activation_row_id", "descriptor_sha256", "formula_id", "formula_sha256", + "operand_rows", "operand_refs", "units", "law_value_uses", "effective_interval", + "rounding_rule", "period_rows", "missing_operands", "result", "result_sha256", + "issue_codes", "receipt_sha256", +} + + +def formula() -> dict: + return { + "calculator_id": "CE-01", + "formula_id": "F-ADD", + "input_operand_ids": ["a", "b"], + "law_value_ids": [], + "operator": "ADD", + "output_metric_id": "TOTAL", + "output_unit_id": "KRW", + "currency": "KRW", + "rounding_rule": {"mode": "ROUND_HALF_UP", "quantum": "0.01", "stage": "FINAL_RESULT"}, + "effective_interval": {"effective_from": "2026-01-01", "effective_to": None, "temporal_rule_id": "TR-1"}, + } + + +def execute(definition: dict, operands: dict) -> dict: + return calc.execute_formula( + definition, + operands, + {}, + run_binding_digest=RUN_DIGEST, + atomic_claim_id="AC-" + "1" * 16, + claim_group_id="CG-" + "2" * 16, + activation_row_id="AR-1", + descriptor_sha256=DESCRIPTOR_DIGEST, + ) + + +class CalculationTest(unittest.TestCase): + def test_activation_is_closed_and_deterministic(self) -> None: + rows = [{"activation_row_id": "AR-1", "calculator_id": "CE-01", "requested_metrics": ["INTEREST"], "equals": {"performance_kind": "MONEY_PAYMENT"}}] + result = calc.activate_calculators({"performance_kind": "MONEY_PAYMENT"}, ["INTEREST"], rows) + self.assertEqual(result, [{"calculator_id": "CE-01", "activation_row_id": "AR-1", "activation_status": "ACTIVATED"}]) + + def test_missing_operand_is_not_zero_filled_and_receipt_is_complete(self) -> None: + receipt = execute(formula(), {"a": "100"}) + self.assertEqual(set(receipt), RECEIPT_REQUIRED) + self.assertEqual(receipt["calculation_status"], "MISSING_OPERAND") + self.assertIsNone(receipt["result"]) + self.assertIsNone(receipt["result_sha256"]) + self.assertEqual(receipt["missing_operands"], ["b"]) + + def test_decimal_addition_emits_structured_result(self) -> None: + receipt = execute(formula(), {"a": "0.10", "b": "0.20"}) + self.assertEqual(receipt["calculation_status"], "CALCULATED") + self.assertEqual(receipt["result"]["value"], "0.30") + self.assertEqual(receipt["result"]["unit_id"], "KRW") + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_calculation_and_reports.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_calculation_and_reports.txt new file mode 100644 index 00000000..10f69b28 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_calculation_and_reports.txt @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +"""Calculator dispatch, Decimal, and canonical receipt tests.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +SPEC = importlib.util.spec_from_file_location("calc", ROOT / "runtime/calculators/registry_engine.py") +if SPEC is None or SPEC.loader is None: + raise RuntimeError("calculator module unavailable") +calc = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(calc) + + +RUN_DIGEST = "a" * 64 +DESCRIPTOR_DIGEST = "b" * 64 +RECEIPT_REQUIRED = { + "schema_version", "calculation_receipt_id", "run_binding_digest", "atomic_claim_id", + "claim_group_id", "calculator_id", "activation_status", "calculation_status", + "activation_row_id", "descriptor_sha256", "formula_id", "formula_sha256", + "operand_rows", "operand_refs", "units", "law_value_uses", "effective_interval", + "rounding_rule", "period_rows", "missing_operands", "result", "result_sha256", + "issue_codes", "receipt_sha256", +} + + +def formula() -> dict: + return { + "calculator_id": "CE-01", + "formula_id": "F-ADD", + "input_operand_ids": ["a", "b"], + "law_value_ids": [], + "operator": "ADD", + "output_metric_id": "TOTAL", + "output_unit_id": "KRW", + "currency": "KRW", + "rounding_rule": {"mode": "ROUND_HALF_UP", "quantum": "0.01", "stage": "FINAL_RESULT"}, + "effective_interval": {"effective_from": "2026-01-01", "effective_to": None, "temporal_rule_id": "TR-1"}, + } + + +def execute(definition: dict, operands: dict) -> dict: + return calc.execute_formula( + definition, + operands, + {}, + run_binding_digest=RUN_DIGEST, + atomic_claim_id="AC-" + "1" * 16, + claim_group_id="CG-" + "2" * 16, + activation_row_id="AR-1", + descriptor_sha256=DESCRIPTOR_DIGEST, + ) + + +class CalculationTest(unittest.TestCase): + def test_activation_is_closed_and_deterministic(self) -> None: + rows = [{"activation_row_id": "AR-1", "calculator_id": "CE-01", "requested_metrics": ["INTEREST"], "equals": {"performance_kind": "MONEY_PAYMENT"}}] + result = calc.activate_calculators({"performance_kind": "MONEY_PAYMENT"}, ["INTEREST"], rows) + self.assertEqual(result, [{"calculator_id": "CE-01", "activation_row_id": "AR-1", "activation_status": "ACTIVATED"}]) + + def test_missing_operand_is_not_zero_filled_and_receipt_is_complete(self) -> None: + receipt = execute(formula(), {"a": "100"}) + self.assertEqual(set(receipt), RECEIPT_REQUIRED) + self.assertEqual(receipt["calculation_status"], "MISSING_OPERAND") + self.assertIsNone(receipt["result"]) + self.assertIsNone(receipt["result_sha256"]) + self.assertEqual(receipt["missing_operands"], ["b"]) + + def test_decimal_addition_emits_structured_result(self) -> None: + receipt = execute(formula(), {"a": "0.10", "b": "0.20"}) + self.assertEqual(receipt["calculation_status"], "CALCULATED") + self.assertEqual(receipt["result"]["value"], "0.30") + self.assertEqual(receipt["result"]["unit_id"], "KRW") + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_inline_output_schema.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_inline_output_schema.py new file mode 100644 index 00000000..5b50589d --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_inline_output_schema.py @@ -0,0 +1,924 @@ +#!/usr/bin/env python3 +"""Execute the S2_20 inline core and validate every emitted JSON artifact. + +This test deliberately uses the checked-in PENDING registries. It verifies +their representability and the shape of the resulting review-only plan; it +does not promote any legal, corpus, model, adapter, or admission status. +""" + +from __future__ import annotations + +import json +from pathlib import Path +import re +from typing import Any, Mapping, Sequence +from urllib.parse import unquote, urljoin, urlparse +import unittest + +import yaml + + +ASSET_ROOT = Path(__file__).resolve().parents[2] +STAGE2_ROOT = ASSET_ROOT.parents[1] +AUTHORING_AGENT = STAGE2_ROOT / "Stage_2_S2_20.yml" +HEX_A = "a" * 64 +HEX_B = "b" * 64 +HEX_C = "c" * 64 +CLAIM_OPTION_ID = "CO-" + ("1" * 64) + + +class SchemaResolutionError(AssertionError): + """Raised when a schema reference cannot be resolved deterministically.""" + + +def load_document(path: Path) -> Any: + """Load JSON or YAML without changing the source document.""" + text = path.read_text(encoding="utf-8") + if path.suffix == ".json": + return json.loads(text) + return yaml.safe_load(text) + + +def json_pointer(document: Any, fragment: str) -> Any: + """Resolve an RFC 6901 JSON pointer fragment.""" + if fragment in {"", "#"}: + return document + pointer = unquote(fragment[1:] if fragment.startswith("#") else fragment) + if not pointer.startswith("/"): + raise SchemaResolutionError(f"unsupported non-pointer fragment: {fragment}") + current = document + for raw_token in pointer[1:].split("/"): + token = raw_token.replace("~1", "/").replace("~0", "~") + try: + current = current[int(token)] if isinstance(current, list) else current[token] + except (KeyError, IndexError, TypeError, ValueError) as exc: + raise SchemaResolutionError(f"missing JSON pointer token {token!r}: {fragment}") from exc + return current + + +class Draft2020Subset: + """Small, dependency-free validator for the keywords used by S2_20. + + The implementation intentionally rejects an unresolved reference. It + supports local and external ``$ref`` plus the structural/applicator + keywords needed by the canonical S2_20 contracts. + """ + + def __init__(self, asset_root: Path) -> None: + self.asset_root = asset_root.resolve() + self.documents: dict[Path, Any] = {} + self.id_index: dict[str, Path] = {} + for path in sorted( + list((self.asset_root / "schemas").glob("*.json")) + + list((self.asset_root / "corpus").glob("*.json")) + ): + try: + document = self._load(path) + except (OSError, json.JSONDecodeError): + continue + schema_id = document.get("$id") if isinstance(document, dict) else None + if isinstance(schema_id, str): + self.id_index[schema_id] = path.resolve() + + def _load(self, path: Path) -> Any: + canonical = path.resolve() + if canonical not in self.documents: + self.documents[canonical] = json.loads(canonical.read_text(encoding="utf-8")) + return self.documents[canonical] + + def resolve(self, ref: str, current_path: Path) -> tuple[Any, Path]: + """Resolve a JSON Schema reference using its base ``$id`` or file path.""" + current_path = current_path.resolve() + current_document = self._load(current_path) + target_part, separator, fragment_part = ref.partition("#") + fragment = f"#{fragment_part}" if separator else "" + if not target_part: + return json_pointer(current_document, fragment), current_path + + parsed = urlparse(target_part) + target_path: Path | None = None + if parsed.scheme in {"http", "https"}: + target_path = self.id_index.get(target_part) + else: + base_id = current_document.get("$id") if isinstance(current_document, dict) else None + if isinstance(base_id, str): + resolved_id = urljoin(base_id, target_part) + target_path = self.id_index.get(resolved_id) + if target_path is None: + file_candidate = (current_path.parent / target_part).resolve() + if file_candidate.is_file(): + target_path = file_candidate + if target_path is None: + raise SchemaResolutionError( + f"unresolved external $ref {ref!r} from {current_path.relative_to(self.asset_root)}" + ) + target_document = self._load(target_path) + return json_pointer(target_document, fragment), target_path + + @staticmethod + def _type_matches(instance: Any, expected: str) -> bool: + if expected == "null": + return instance is None + if expected == "boolean": + return isinstance(instance, bool) + if expected == "object": + return isinstance(instance, dict) + if expected == "array": + return isinstance(instance, list) + if expected == "string": + return isinstance(instance, str) + if expected == "integer": + return isinstance(instance, int) and not isinstance(instance, bool) + if expected == "number": + return isinstance(instance, (int, float)) and not isinstance(instance, bool) + raise SchemaResolutionError(f"unsupported JSON Schema type: {expected}") + + @staticmethod + def _json_identity(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + def validate_ref(self, instance: Any, schema_ref: str) -> list[str]: + target_part, separator, fragment_part = schema_ref.partition("#") + if not target_part: + raise SchemaResolutionError(f"top-level schema_ref needs a path: {schema_ref}") + schema_path = (self.asset_root / target_part).resolve() + if not schema_path.is_file() or self.asset_root not in schema_path.parents: + raise SchemaResolutionError(f"schema path is unavailable or outside asset root: {target_part}") + document = self._load(schema_path) + fragment = f"#{fragment_part}" if separator else "" + schema = json_pointer(document, fragment) + return self._validate(instance, schema, schema_path, "$", set()) + + def _branch_errors( + self, + instance: Any, + schemas: Sequence[Any], + current_path: Path, + instance_path: str, + stack: set[tuple[Path, int, int]], + ) -> list[list[str]]: + return [ + self._validate(instance, branch, current_path, instance_path, set(stack)) + for branch in schemas + ] + + def _validate( + self, + instance: Any, + schema: Any, + current_path: Path, + instance_path: str, + stack: set[tuple[Path, int, int]], + ) -> list[str]: + if schema is True: + return [] + if schema is False: + return [f"{instance_path}: boolean schema false"] + if not isinstance(schema, dict): + raise SchemaResolutionError(f"schema at {instance_path} is not an object or boolean") + + marker = (current_path.resolve(), id(schema), id(instance)) + if marker in stack: + return [] + stack = set(stack) + stack.add(marker) + errors: list[str] = [] + + if "$ref" in schema: + target, target_path = self.resolve(str(schema["$ref"]), current_path) + errors.extend(self._validate(instance, target, target_path, instance_path, stack)) + + if "allOf" in schema: + for index, branch_errors in enumerate( + self._branch_errors(instance, schema["allOf"], current_path, instance_path, stack) + ): + errors.extend(f"{message} [allOf[{index}]]" for message in branch_errors) + + if "anyOf" in schema: + branches = self._branch_errors(instance, schema["anyOf"], current_path, instance_path, stack) + if not any(not branch for branch in branches): + details = " | ".join( + f"branch {index}: {branch[0] if branch else 'matched'}" + for index, branch in enumerate(branches) + ) + errors.append(f"{instance_path}: anyOf matched no branch ({details})") + + if "oneOf" in schema: + branches = self._branch_errors(instance, schema["oneOf"], current_path, instance_path, stack) + matched = [index for index, branch in enumerate(branches) if not branch] + if len(matched) != 1: + details = " | ".join( + f"branch {index}: {branch[0] if branch else 'matched'}" + for index, branch in enumerate(branches) + ) + errors.append( + f"{instance_path}: oneOf expected exactly one match, got {matched} ({details})" + ) + + if "not" in schema and not self._validate( + instance, schema["not"], current_path, instance_path, set(stack) + ): + errors.append(f"{instance_path}: not subschema matched") + + if "if" in schema: + condition_matches = not self._validate( + instance, schema["if"], current_path, instance_path, set(stack) + ) + chosen = "then" if condition_matches else "else" + if chosen in schema: + errors.extend( + f"{message} [{chosen}]" + for message in self._validate( + instance, schema[chosen], current_path, instance_path, set(stack) + ) + ) + + expected_type = schema.get("type") + if expected_type is not None: + expected_types = [expected_type] if isinstance(expected_type, str) else expected_type + if not isinstance(expected_types, list) or not all(isinstance(item, str) for item in expected_types): + raise SchemaResolutionError(f"invalid type declaration at {instance_path}") + if not any(self._type_matches(instance, item) for item in expected_types): + errors.append( + f"{instance_path}: expected type {expected_types}, got {type(instance).__name__}" + ) + return errors + + if "const" in schema and instance != schema["const"]: + errors.append(f"{instance_path}: expected const {schema['const']!r}, got {instance!r}") + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{instance_path}: value {instance!r} is outside enum {schema['enum']!r}") + + if isinstance(instance, str): + if "pattern" in schema and re.search(str(schema["pattern"]), instance) is None: + errors.append(f"{instance_path}: string does not match {schema['pattern']!r}") + if len(instance) < int(schema.get("minLength", 0)): + errors.append(f"{instance_path}: string is shorter than minLength {schema['minLength']}") + if "maxLength" in schema and len(instance) > int(schema["maxLength"]): + errors.append(f"{instance_path}: string is longer than maxLength {schema['maxLength']}") + + if isinstance(instance, (int, float)) and not isinstance(instance, bool): + if "minimum" in schema and instance < schema["minimum"]: + errors.append(f"{instance_path}: {instance} is below minimum {schema['minimum']}") + if "maximum" in schema and instance > schema["maximum"]: + errors.append(f"{instance_path}: {instance} exceeds maximum {schema['maximum']}") + if "exclusiveMinimum" in schema and instance <= schema["exclusiveMinimum"]: + errors.append( + f"{instance_path}: {instance} is not above exclusiveMinimum {schema['exclusiveMinimum']}" + ) + if "exclusiveMaximum" in schema and instance >= schema["exclusiveMaximum"]: + errors.append( + f"{instance_path}: {instance} is not below exclusiveMaximum {schema['exclusiveMaximum']}" + ) + + if isinstance(instance, dict): + required = schema.get("required", []) + for key in required: + if key not in instance: + errors.append(f"{instance_path}: missing required property {key!r}") + properties = schema.get("properties", {}) + for key, value in instance.items(): + child_path = f"{instance_path}.{key}" + if key in properties: + errors.extend( + self._validate(value, properties[key], current_path, child_path, stack) + ) + elif schema.get("additionalProperties") is False: + errors.append(f"{child_path}: additional property is forbidden") + elif isinstance(schema.get("additionalProperties"), dict): + errors.extend( + self._validate( + value, + schema["additionalProperties"], + current_path, + child_path, + stack, + ) + ) + if len(instance) < int(schema.get("minProperties", 0)): + errors.append( + f"{instance_path}: object has fewer than minProperties {schema['minProperties']}" + ) + if "maxProperties" in schema and len(instance) > int(schema["maxProperties"]): + errors.append( + f"{instance_path}: object has more than maxProperties {schema['maxProperties']}" + ) + + if isinstance(instance, list): + if len(instance) < int(schema.get("minItems", 0)): + errors.append(f"{instance_path}: array has fewer than minItems {schema['minItems']}") + if "maxItems" in schema and len(instance) > int(schema["maxItems"]): + errors.append(f"{instance_path}: array has more than maxItems {schema['maxItems']}") + if schema.get("uniqueItems") is True: + identities = [self._json_identity(item) for item in instance] + if len(identities) != len(set(identities)): + errors.append(f"{instance_path}: array items are not unique") + prefix_items = schema.get("prefixItems", []) + if isinstance(prefix_items, list): + for index, child_schema in enumerate(prefix_items[: len(instance)]): + errors.extend( + self._validate( + instance[index], child_schema, current_path, f"{instance_path}[{index}]", stack + ) + ) + items_schema = schema.get("items") + if items_schema is not None: + start = len(prefix_items) if isinstance(prefix_items, list) else 0 + for index in range(start, len(instance)): + errors.extend( + self._validate( + instance[index], items_schema, current_path, f"{instance_path}[{index}]", stack + ) + ) + if "contains" in schema: + matched = sum( + not self._validate(item, schema["contains"], current_path, f"{instance_path}[{index}]", set(stack)) + for index, item in enumerate(instance) + ) + minimum = int(schema.get("minContains", 1)) + maximum = schema.get("maxContains") + if matched < minimum or (maximum is not None and matched > int(maximum)): + errors.append( + f"{instance_path}: contains matched {matched}, expected {minimum}..{maximum or 'unbounded'}" + ) + return errors + + +def load_inline_namespace() -> tuple[Mapping[str, Any], str]: + """Parse the Agent YAML and execute only its inline definitions.""" + agent = yaml.safe_load(AUTHORING_AGENT.read_text(encoding="utf-8")) + tasks = [ + task + for stage in agent["Agent"]["Stages"] + for task in stage.get("tasks", []) + if task.get("mcp") == "code-executor" and task.get("tool_name") == "run_code" + ] + if len(tasks) != 1: + raise AssertionError(f"expected exactly one inline run_code task, got {len(tasks)}") + code = tasks[0].get("parameters", {}).get("code") + if not isinstance(code, str) or not code.strip(): + raise AssertionError("inline Python code is missing") + namespace: dict[str, Any] = {"__name__": "s2_20_inline_offline_schema_test"} + exec(compile(code, str(AUTHORING_AGENT), "exec"), namespace) + return namespace, code + + +def pending_inputs_and_assets(namespace: Mapping[str, Any], code: str) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]: + """Create one minimal S2_10-like selected claim with checked-in PENDING assets.""" + digest = namespace["digest"] + option = { + "claim_option_id": CLAIM_OPTION_ID, + "source_local_ref": "option-minimal-001", + "normalized_claim_signature": { + "vocabulary_version": "stage2.claim_signature_vocab.v1", + "legal_basis_family": "CONTRACT", + "right_holder_refs": ["PARTY-P-001"], + "obligor_refs": ["PARTY-D-001"], + "performance_kind": "MONEY_PAYMENT", + "object_refs": [], + "legal_effect": "PERFORMANCE", + "source_occurrence_refs": ["FACT-001"], + }, + "relation": "primary", + "decision": "SUPPORTED", + "client_disposition": "CANDIDATE", + "client_instruction_refs": [], + "authority_refs": [], + "incompatible_with": [], + "precondition_refs": [], + "same_underlying_recovery_key": "RECOVERY-MINIMAL-001", + "element_statuses": [], + "defense_statuses": [], + "remedy_candidates": [], + "limitation": { + "urgency": "NONE_IDENTIFIED", + "basis_refs": [], + "authority_refs": [], + "unresolved_reason_codes": [], + }, + "impact_scope": "OPTION_ONLY", + "risk_level": "UNASSESSED", + "forbidden_outputs": [], + "review_resolutions": [], + "typed_provenance": [], + "missing_inputs": [], + "review_flags": [], + } + verdict = {"cluster_id": "CL-MINIMAL-001", "claim_options": [option]} + inputs = { + "verdicts": [verdict], + "verdict_sources": { + "CL-MINIMAL-001": { + "path": "map_s2_10/domain_verdicts/CL-MINIMAL-001.json", + "sha256": digest(verdict), + } + }, + "party_context": { + "parties": [{"party_id": "PARTY-P-001"}, {"party_id": "PARTY-D-001"}], + "title_chains": [], + "identity_issues": [], + }, + "evidence_inventory": { + "items": [ + {"evidence_id": "EVIDENCE-002"}, + {"evidence_id": "EVIDENCE-001"}, + ] + }, + "issue_ledger": {"schema_version": "stage2_issue_ledger.v1", "issues": []}, + "issue_ledger_raw": b'{"issues":[],"schema_version":"stage2_issue_ledger.v1"}\n', + } + + asset_paths = { + "case_registry": "manifest/case_type_registry.yml", + "rule_registry": "manifest/case_type_rule_registry.yml", + "activation_registry": "registry/calculations/calculation_activation_registry.yml", + "corpus_release": "manifest/corpus_release.json", + "corpus_scope": "registry/corpus/requirement_fact_scope.yml", + "signature_projection_registry": "registry/binding/binding_signature_projection.yml", + "party_rules": "registry/party/party_set_rules.yml", + "disposition_policy": "registry/planning/option_disposition_policy.yml", + "authority_release": "manifest/authority_release.json", + } + loaded = {key: load_document(ASSET_ROOT / path) for key, path in asset_paths.items()} + descriptor_paths = sorted((ASSET_ROOT / "registry/calculations").glob("CE-*.yml")) + descriptor_hashes = { + str(load_document(path)["calculator_id"]): digest(path.read_bytes()) + for path in descriptor_paths + } + assets = { + "case_registry": loaded["case_registry"], + "rule_registry": loaded["rule_registry"], + "activation_registry": loaded["activation_registry"], + "corpus_release": loaded["corpus_release"], + "corpus_scope": loaded["corpus_scope"], + "signature_projection_registry": loaded["signature_projection_registry"], + "disposition_policy": loaded["disposition_policy"], + "party_rules_eligible": loaded["party_rules"].get("registry_status") == "APPROVED", + "signature_projection_eligible": loaded["signature_projection_registry"].get("registry_status") == "APPROVED", + "disposition_policy_eligible": loaded["disposition_policy"].get("policy_status") == "APPROVED", + "authority_admitted": loaded["authority_release"].get("status") == "ADMITTED", + "asset_hashes": { + "case_registry": digest((ASSET_ROOT / asset_paths["case_registry"]).read_bytes()), + "rule_registry": digest((ASSET_ROOT / asset_paths["rule_registry"]).read_bytes()), + "signature_projection": digest((ASSET_ROOT / asset_paths["signature_projection_registry"]).read_bytes()), + "corpus_release": digest((ASSET_ROOT / asset_paths["corpus_release"]).read_bytes()), + }, + "calculator_descriptor_hashes": descriptor_hashes, + "schema_documents": { + path: load_document(ASSET_ROOT / path) + for path in ( + "schemas/relief_plan.schema.json", + "schemas/binding_retrieval.schema.json", + "schemas/calculation.schema.json", + ) + }, + "s2_20_code_sha256": digest(code.encode("utf-8")), + } + request = { + "run_binding_digest": HEX_A, + "expected_parent_stage2_release_sha256": HEX_B, + "expected_s2_10_publish_status_sha256": HEX_C, + } + return inputs, assets, request + + +class InlineOutputSchemaTest(unittest.TestCase): + maxDiff = None + + def test_inline_outputs_and_fixed_assets_match_canonical_defs(self) -> None: + namespace, code = load_inline_namespace() + inputs, assets, request = pending_inputs_and_assets(namespace, code) + result = namespace["execute_core"](inputs, assets, request, HEX_C) + payloads = namespace["artifact_payloads"](result, request) + validator = Draft2020Subset(ASSET_ROOT) + + failures: list[str] = [] + canonical_option = inputs["verdicts"][0]["claim_options"][0] + failures.extend( + "minimal canonical S2_10 option -> schemas/s2_10.schema.json#/$defs/canonical_claim_option: " + + message + for message in validator.validate_ref( + canonical_option, + "schemas/s2_10.schema.json#/$defs/canonical_claim_option", + ) + ) + for artifact_path, (payload, schema_ref, _producer) in sorted(payloads.items()): + try: + value = json.loads(payload.decode("utf-8")) + errors = validator.validate_ref(value, schema_ref) + namespace["validate_artifact_payload"]( + payload, schema_ref, artifact_path, assets["schema_documents"] + ) + except Exception as exc: # Keep all independent failures visible. + failures.append(f"artifact {artifact_path} -> {schema_ref}: {type(exc).__name__}: {exc}") + continue + failures.extend( + f"artifact {artifact_path} -> {schema_ref}: {message}" for message in errors + ) + + fixed_assets = { + "manifest/case_type_registry.yml": "schemas/binding_retrieval.schema.json#/$defs/case_type_registry", + "manifest/case_type_rule_registry.yml": "schemas/binding_retrieval.schema.json#/$defs/case_type_rule_registry", + "registry/binding/binding_signature_projection.yml": "schemas/binding_retrieval.schema.json#/$defs/binding_signature_projection_registry", + "registry/planning/option_disposition_policy.yml": "schemas/binding_retrieval.schema.json#/$defs/option_disposition_policy", + "registry/party/party_set_rules.yml": "schemas/binding_retrieval.schema.json#/$defs/party_set_rules", + "registry/corpus/requirement_fact_scope.yml": "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_scope_registry", + "registry/calculations/calculation_activation_registry.yml": "schemas/calculation.schema.json#/$defs/calculation_activation_registry", + } + for descriptor_path in sorted((ASSET_ROOT / "registry/calculations").glob("CE-*.yml")): + fixed_assets[descriptor_path.relative_to(ASSET_ROOT).as_posix()] = ( + "schemas/calculation.schema.json#/$defs/calculation_descriptor" + ) + + self.assertEqual( + len([path for path in fixed_assets if path.startswith("registry/calculations/CE-")]), + 17, + "the canonical calculation descriptor set must contain exactly 17 files", + ) + for relative_path, schema_ref in sorted(fixed_assets.items()): + try: + value = load_document(ASSET_ROOT / relative_path) + errors = validator.validate_ref(value, schema_ref) + except Exception as exc: # Keep all independent failures visible. + failures.append(f"fixed asset {relative_path} -> {schema_ref}: {type(exc).__name__}: {exc}") + continue + failures.extend( + f"fixed asset {relative_path} -> {schema_ref}: {message}" for message in errors + ) + + self.assertFalse( + failures, + "S2_20 canonical schema validation failures:\n" + "\n".join(failures), + ) + + def test_inline_validator_is_release_bound_and_fail_closed(self) -> None: + namespace, code = load_inline_namespace() + inputs, assets, request = pending_inputs_and_assets(namespace, code) + result = namespace["execute_core"](inputs, assets, request, HEX_C) + payloads = namespace["artifact_payloads"](result, request) + payload, schema_ref, _producer = payloads["exhibit_register.json"] + + namespace["validate_artifact_payload"]( + payload, schema_ref, "exhibit_register.json", assets["schema_documents"] + ) + mutated = json.loads(payload.decode("utf-8")) + mutated["schema_version"] = "wrong" + with self.assertRaises(Exception) as raised: + namespace["validate_artifact_payload"]( + namespace["canonical_bytes"](mutated), + schema_ref, + "exhibit_register.json", + assets["schema_documents"], + ) + self.assertEqual(getattr(raised.exception, "code", None), "ARTIFACT_SCHEMA_VALIDATION_FAILED") + mutated = json.loads(payload.decode("utf-8")) + mutated["rows"][0]["unexpected"] = True + with self.assertRaises(Exception) as raised: + namespace["validate_artifact_payload"]( + namespace["canonical_bytes"](mutated), + schema_ref, + "exhibit_register.json", + assets["schema_documents"], + ) + self.assertEqual(getattr(raised.exception, "code", None), "ARTIFACT_SCHEMA_VALIDATION_FAILED") + + def test_inline_evidence_register_and_exists_contract(self) -> None: + namespace, code = load_inline_namespace() + inputs, assets, request = pending_inputs_and_assets(namespace, code) + result = namespace["execute_core"](inputs, assets, request, HEX_C) + self.assertEqual( + result["exhibit_register"]["rows"], + [ + { + "evidence_ref": "EVIDENCE-001", + "source_key": "EVIDENCE-001", + "existing_label": None, + "assigned_label": "갑 제1호증", + "assignment_status": "DETERMINISTICALLY_ASSIGNED", + }, + { + "evidence_ref": "EVIDENCE-002", + "source_key": "EVIDENCE-002", + "existing_label": None, + "assigned_label": "갑 제2호증", + "assignment_status": "DETERMINISTICALLY_ASSIGNED", + }, + ], + ) + signature = {"claim_family": "CONTRACT"} + self.assertTrue( + namespace["eval_predicate"]( + signature, {"operator": "EXISTS", "field": "claim_family", "value": True} + ) + ) + self.assertFalse( + namespace["eval_predicate"]( + signature, {"operator": "EXISTS", "field": "claim_family", "value": False} + ) + ) + self.assertTrue( + namespace["eval_predicate"]( + {}, {"operator": "EXISTS", "field": "claim_family", "value": False} + ) + ) + + def test_inline_receipt_and_retrieval_contract_markers(self) -> None: + namespace, code = load_inline_namespace() + self.assertIn('canonical_code.get("code_sha256")', code) + self.assertNotIn('canonical_code.get("sha256")', code) + self.assertIn('publication_guard["external_trust_verified"]', code) + + class RetryClient: + def __init__(self) -> None: + self.calls = 0 + + def call(self, _tool: str, _arguments: Mapping[str, Any]) -> Mapping[str, Any]: + self.calls += 1 + if self.calls < 3: + raise namespace["S220Error"]("MCP_TRANSPORT", "transient") + return {"content": []} + + retry_client = RetryClient() + _result, attempts = namespace["call_with_transient_retry"]( + retry_client, "search_hybrid", {}, max_attempts=3 + ) + self.assertEqual(attempts, 3) + self.assertEqual(retry_client.calls, 3) + + hits, drops = namespace["canonicalize_retrieval_hits"]( + [ + { + "chunk_id": "CHUNK-B", + "chunk_sha256": HEX_B, + "post_rerank_rank": 1, + "hybrid_score": 0.4, + "reranker_score": None, + }, + { + "chunk_id": "CHUNK-A", + "chunk_sha256": HEX_A, + "post_rerank_rank": 2, + "hybrid_score": 0.9, + "reranker_score": None, + }, + { + "chunk_id": "CHUNK-A", + "chunk_sha256": HEX_C, + "post_rerank_rank": 1, + "hybrid_score": 0.8, + "reranker_score": None, + }, + ] + ) + self.assertEqual([row["chunk_id"] for row in hits], ["CHUNK-A", "CHUNK-B"]) + self.assertEqual(drops, [{"chunk_id": "CHUNK-A", "chunk_sha256": HEX_A, "drop_reason_code": "DUPLICATE_CHUNK"}]) + + def test_request_trust_paths_binding_gate_and_partial_publish_manifest(self) -> None: + namespace, _code = load_inline_namespace() + namespace["INLINE_USER_HASH"] = HEX_A + namespace["INLINE_WORKSPACE_HASH"] = HEX_B + run_digest = HEX_C + request = { + "schema_version": "stage2_s2_20_request.v1", + "request_id": "REQ-001", + "attempt_id": "ATTEMPT-001", + "run_binding_digest": run_digest, + "user_identity_hash": HEX_A, + "workspace_identity_hash": HEX_B, + "stage2_run_root_ref": f"stage2_runs/by-binding/{run_digest}", + "plan_output_root_ref": f"stage2_runs/by-binding/{run_digest}/plan", + "host_cas_receipt_ref": f"stage2_control/host_cas/{run_digest}/S2_20.json", + "host_cas_receipt_sha256": HEX_A, + "detached_admission_receipt_ref": "Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json", + "detached_admission_receipt_sha256": HEX_B, + "outer_execution_receipt_target_ref": f"stage2_runs/by-binding/{run_digest}/control/s2_20_outer_execution_receipt.json", + "expected_s2_10_publish_status_sha256": HEX_A, + "expected_parent_stage2_release_sha256": HEX_B, + "expected_s2_10_release_sha256": HEX_C, + "expected_s2_20_agent_sha256": HEX_A, + "expected_s2_20_executor_binding_sha256": HEX_B, + } + self.assertEqual(namespace["validate_request"](namespace["canonical_bytes"](request)), request) + for key, invalid in ( + ("host_cas_receipt_ref", "attacker/cas.json"), + ("detached_admission_receipt_ref", "attacker/admission.json"), + ("outer_execution_receipt_target_ref", "attacker/outer.json"), + ): + mutated = dict(request) + mutated[key] = invalid + with self.assertRaises(Exception) as raised: + namespace["validate_request"](namespace["canonical_bytes"](mutated)) + self.assertEqual(getattr(raised.exception, "code", None), "REQUEST_TRUST_ANCHOR_PATH") + + admission = { + "schema_version": "stage2_deterministic_admission_receipt.v1", + "admission_status": "CANARY_ADMITTED", + "parent_release_sha256": HEX_A, + "executor_binding_sha256": HEX_B, + "present_deterministic_stage_ids": ["S2_20"], + "stage_receipts": [], + "backend_capability_receipt_sha256": HEX_C, + "signature": "agentbackend-signature", + "signature_verification_status": "BACKEND_VERIFIED", + "signed_payload_sha256": HEX_A, + } + signed_before = namespace["digest"](namespace["_admission_signed_material"](admission)) + mutated_admission = dict(admission) + mutated_admission["backend_capability_receipt_sha256"] = HEX_B + signed_after = namespace["digest"](namespace["_admission_signed_material"](mutated_admission)) + self.assertNotEqual(signed_before, signed_after) + + pending_binding = (ASSET_ROOT / "deployment/stage2_code_executor_binding.yml").read_bytes() + with self.assertRaises(Exception) as raised: + namespace["_verify_binding_text"](pending_binding, request) + self.assertEqual(getattr(raised.exception, "code", None), "EXECUTOR_BINDING_NOT_ADMITTED") + + inputs, assets, core_request = pending_inputs_and_assets(namespace, _code) + core_request.update( + { + "attempt_id": "ATTEMPT-PARTIAL", + "plan_output_root_ref": f"stage2_runs/by-binding/{core_request['run_binding_digest']}/plan", + "expected_s2_20_agent_sha256": HEX_A, + } + ) + result = namespace["execute_core"](inputs, assets, core_request, HEX_C) + + class PartialWriter: + def __init__(self) -> None: + self.files: dict[str, bytes] = {} + self.calls = 0 + self.fail_at: int | None = 2 + + def write_immutable(self, path: str, payload: bytes) -> str: + self.calls += 1 + if self.fail_at is not None and self.calls == self.fail_at: + raise RuntimeError("injected write failure") + self.files[path] = payload + return namespace["digest"](payload) + + writer = PartialWriter() + with self.assertRaises(Exception) as raised: + namespace["publish"](writer, core_request, result) + self.assertEqual(getattr(raised.exception, "code", None), "PUBLISH_PARTIAL_WRITE") + error = raised.exception.as_dict() + self.assertEqual(len(error["details"]["written_artifact_rows"]), 1) + writer.fail_at = None + namespace["publish_diagnostic"](writer, core_request, error) + barrier_path = f"{core_request['plan_output_root_ref']}/plan_publish_status.json" + barrier = json.loads(writer.files[barrier_path]) + self.assertFalse(barrier["consumer_authorized"]) + self.assertEqual(len(barrier["artifact_rows"]), 2) + self.assertIn("plan/technical_diagnostic.json", [row["path"] for row in barrier["artifact_rows"]]) + + class BarrierWriter: + def __init__(self) -> None: + self.files: dict[str, bytes] = {} + self.failed_once = False + + def write_immutable(self, path: str, payload: bytes) -> str: + if path.endswith("/plan_publish_status.json") and not self.failed_once: + self.failed_once = True + raise RuntimeError("injected barrier failure") + self.files[path] = payload + return namespace["digest"](payload) + + barrier_writer = BarrierWriter() + expected_payload_count = len(namespace["artifact_payloads"](result, core_request)) + with self.assertRaises(Exception) as raised: + namespace["publish"](barrier_writer, core_request, result) + self.assertEqual(getattr(raised.exception, "code", None), "PUBLISH_PARTIAL_WRITE") + barrier_error = raised.exception.as_dict() + self.assertEqual( + len(barrier_error["details"]["written_artifact_rows"]), expected_payload_count + ) + namespace["publish_diagnostic"](barrier_writer, core_request, barrier_error) + barrier = json.loads(barrier_writer.files[barrier_path]) + self.assertEqual(len(barrier["artifact_rows"]), expected_payload_count + 1) + self.assertFalse(barrier["consumer_authorized"]) + + def test_upstream_schema_closure_self_hashes_and_special_assets(self) -> None: + namespace, code = load_inline_namespace() + fixed = { + "schema_relief_plan": str(ASSET_ROOT / "schemas/relief_plan.schema.json"), + "schema_binding_retrieval": str(ASSET_ROOT / "schemas/binding_retrieval.schema.json"), + "schema_calculation": str(ASSET_ROOT / "schemas/calculation.schema.json"), + "schema_corpus_collection": str(ASSET_ROOT / "corpus/weaviate_collection.schema.json"), + "schema_ingress": str(ASSET_ROOT / "schemas/ingress.schema.json"), + "schema_context": str(ASSET_ROOT / "schemas/context.schema.json"), + "schema_s2_10": str(ASSET_ROOT / "schemas/s2_10.schema.json"), + "schema_review_status": str(ASSET_ROOT / "schemas/review_status.schema.json"), + "schema_deployment": str(ASSET_ROOT / "schemas/deployment.schema.json"), + } + all_raw = {path: Path(path).read_bytes() for path in fixed.values()} + documents = namespace["build_schema_documents"](all_raw, fixed) + self.assertEqual( + set(documents), + { + "schemas/relief_plan.schema.json", + "schemas/binding_retrieval.schema.json", + "schemas/calculation.schema.json", + "schemas/ingress.schema.json", + "schemas/context.schema.json", + "schemas/s2_10.schema.json", + "schemas/review_status.schema.json", + "schemas/deployment.schema.json", + }, + ) + + def refs(value: Any) -> list[str]: + found: list[str] = [] + if isinstance(value, dict): + if isinstance(value.get("$ref"), str): + found.append(value["$ref"]) + for child in value.values(): + found.extend(refs(child)) + elif isinstance(value, list): + for child in value: + found.extend(refs(child)) + return found + + for schema_path in ( + "schemas/context.schema.json", + "schemas/review_status.schema.json", + "schemas/binding_retrieval.schema.json", + ): + self.assertFalse( + [ref for ref in refs(documents[schema_path]) if ".schema.json" in ref], + f"inline schema closure retains an external ref: {schema_path}", + ) + + body = {"schema_version": "test.v1", "value": 1} + sealed = {**body, "receipt_sha256": namespace["digest"](body)} + self.assertEqual( + namespace["_require_object_self_hash"]( + sealed, "receipt_sha256", code="TEST_SELF_HASH", label="test" + ), + sealed["receipt_sha256"], + ) + mutated = dict(sealed) + mutated["value"] = 2 + with self.assertRaises(Exception) as raised: + namespace["_require_object_self_hash"]( + mutated, "receipt_sha256", code="TEST_SELF_HASH", label="test" + ) + self.assertEqual(getattr(raised.exception, "code", None), "TEST_SELF_HASH") + + special_contracts = { + "registry/temporal/inheritance_reserved_share.yml": ( + "stage2.s2_20.inheritance_reserved_share_temporal.v1", + "registry_id", "S2-20-INHERITANCE-RESERVED-SHARE-TEMPORAL", "branch_rows", + ), + "registry/drafting/forbidden_relief_rules.yml": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-FORBIDDEN-RELIEF-RULES", "rule_rows", + ), + "registry/drafting/counter_performance_rules.yml": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-COUNTER-PERFORMANCE-RULES", "rule_rows", + ), + "registry/drafting/procedural_declaration_rules.yml": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-PROCEDURAL-DECLARATION-RULES", "rule_rows", + ), + "routing/actio_pauliana_route_registry.yml": ( + "stage2.s2_20.actio_pauliana_route_registry.v1", + "registry_id", "S2-20-ACTIO-PAULIANA-ROUTES", "route_rows", + ), + "routing/actio_pauliana_mortgage_route.yml": ( + "stage2.s2_20.actio_pauliana_mortgage_route.v1", + "registry_id", "S2-20-ACTIO-MORTGAGE-ROUTES", "route_rows", + ), + "validators/actio_value_compensation_invariants.yml": ( + "stage2.s2_20.actio_value_compensation_invariants.v1", + "validator_id", "S2-20-ACTIO-VALUE-COMPENSATION-INVARIANTS", "invariant_rows", + ), + } + for relative, contract in special_contracts.items(): + value = namespace["_validate_special_legal_asset"]( + load_document(ASSET_ROOT / relative), + schema_version=contract[0], + identity_field=contract[1], + identity=contract[2], + rows_field=contract[3], + label=relative, + ) + self.assertFalse(value["execution_eligible"]) + self.assertEqual(value[contract[3]], []) + + for marker in ( + '"ingress_status": ingress', + '"bundle_plan": bundle_plan', + '"cluster_slices": cluster_slices', + '"issue_patches": issue_parts', + '"assumption_parts": assumption_parts', + '"usage_parts": usage_parts', + '"wave_receipts": wave_receipts', + 'CLUSTER_ID.fullmatch(item)', + 'item.get("domain_verdict_sha256") != verdict_hash', + 'status.get("terminal_wave_receipt_sha256s") != [', + ): + self.assertIn(marker, code) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_inline_output_schema.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_inline_output_schema.txt new file mode 100644 index 00000000..5b50589d --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_inline_output_schema.txt @@ -0,0 +1,924 @@ +#!/usr/bin/env python3 +"""Execute the S2_20 inline core and validate every emitted JSON artifact. + +This test deliberately uses the checked-in PENDING registries. It verifies +their representability and the shape of the resulting review-only plan; it +does not promote any legal, corpus, model, adapter, or admission status. +""" + +from __future__ import annotations + +import json +from pathlib import Path +import re +from typing import Any, Mapping, Sequence +from urllib.parse import unquote, urljoin, urlparse +import unittest + +import yaml + + +ASSET_ROOT = Path(__file__).resolve().parents[2] +STAGE2_ROOT = ASSET_ROOT.parents[1] +AUTHORING_AGENT = STAGE2_ROOT / "Stage_2_S2_20.yml" +HEX_A = "a" * 64 +HEX_B = "b" * 64 +HEX_C = "c" * 64 +CLAIM_OPTION_ID = "CO-" + ("1" * 64) + + +class SchemaResolutionError(AssertionError): + """Raised when a schema reference cannot be resolved deterministically.""" + + +def load_document(path: Path) -> Any: + """Load JSON or YAML without changing the source document.""" + text = path.read_text(encoding="utf-8") + if path.suffix == ".json": + return json.loads(text) + return yaml.safe_load(text) + + +def json_pointer(document: Any, fragment: str) -> Any: + """Resolve an RFC 6901 JSON pointer fragment.""" + if fragment in {"", "#"}: + return document + pointer = unquote(fragment[1:] if fragment.startswith("#") else fragment) + if not pointer.startswith("/"): + raise SchemaResolutionError(f"unsupported non-pointer fragment: {fragment}") + current = document + for raw_token in pointer[1:].split("/"): + token = raw_token.replace("~1", "/").replace("~0", "~") + try: + current = current[int(token)] if isinstance(current, list) else current[token] + except (KeyError, IndexError, TypeError, ValueError) as exc: + raise SchemaResolutionError(f"missing JSON pointer token {token!r}: {fragment}") from exc + return current + + +class Draft2020Subset: + """Small, dependency-free validator for the keywords used by S2_20. + + The implementation intentionally rejects an unresolved reference. It + supports local and external ``$ref`` plus the structural/applicator + keywords needed by the canonical S2_20 contracts. + """ + + def __init__(self, asset_root: Path) -> None: + self.asset_root = asset_root.resolve() + self.documents: dict[Path, Any] = {} + self.id_index: dict[str, Path] = {} + for path in sorted( + list((self.asset_root / "schemas").glob("*.json")) + + list((self.asset_root / "corpus").glob("*.json")) + ): + try: + document = self._load(path) + except (OSError, json.JSONDecodeError): + continue + schema_id = document.get("$id") if isinstance(document, dict) else None + if isinstance(schema_id, str): + self.id_index[schema_id] = path.resolve() + + def _load(self, path: Path) -> Any: + canonical = path.resolve() + if canonical not in self.documents: + self.documents[canonical] = json.loads(canonical.read_text(encoding="utf-8")) + return self.documents[canonical] + + def resolve(self, ref: str, current_path: Path) -> tuple[Any, Path]: + """Resolve a JSON Schema reference using its base ``$id`` or file path.""" + current_path = current_path.resolve() + current_document = self._load(current_path) + target_part, separator, fragment_part = ref.partition("#") + fragment = f"#{fragment_part}" if separator else "" + if not target_part: + return json_pointer(current_document, fragment), current_path + + parsed = urlparse(target_part) + target_path: Path | None = None + if parsed.scheme in {"http", "https"}: + target_path = self.id_index.get(target_part) + else: + base_id = current_document.get("$id") if isinstance(current_document, dict) else None + if isinstance(base_id, str): + resolved_id = urljoin(base_id, target_part) + target_path = self.id_index.get(resolved_id) + if target_path is None: + file_candidate = (current_path.parent / target_part).resolve() + if file_candidate.is_file(): + target_path = file_candidate + if target_path is None: + raise SchemaResolutionError( + f"unresolved external $ref {ref!r} from {current_path.relative_to(self.asset_root)}" + ) + target_document = self._load(target_path) + return json_pointer(target_document, fragment), target_path + + @staticmethod + def _type_matches(instance: Any, expected: str) -> bool: + if expected == "null": + return instance is None + if expected == "boolean": + return isinstance(instance, bool) + if expected == "object": + return isinstance(instance, dict) + if expected == "array": + return isinstance(instance, list) + if expected == "string": + return isinstance(instance, str) + if expected == "integer": + return isinstance(instance, int) and not isinstance(instance, bool) + if expected == "number": + return isinstance(instance, (int, float)) and not isinstance(instance, bool) + raise SchemaResolutionError(f"unsupported JSON Schema type: {expected}") + + @staticmethod + def _json_identity(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + def validate_ref(self, instance: Any, schema_ref: str) -> list[str]: + target_part, separator, fragment_part = schema_ref.partition("#") + if not target_part: + raise SchemaResolutionError(f"top-level schema_ref needs a path: {schema_ref}") + schema_path = (self.asset_root / target_part).resolve() + if not schema_path.is_file() or self.asset_root not in schema_path.parents: + raise SchemaResolutionError(f"schema path is unavailable or outside asset root: {target_part}") + document = self._load(schema_path) + fragment = f"#{fragment_part}" if separator else "" + schema = json_pointer(document, fragment) + return self._validate(instance, schema, schema_path, "$", set()) + + def _branch_errors( + self, + instance: Any, + schemas: Sequence[Any], + current_path: Path, + instance_path: str, + stack: set[tuple[Path, int, int]], + ) -> list[list[str]]: + return [ + self._validate(instance, branch, current_path, instance_path, set(stack)) + for branch in schemas + ] + + def _validate( + self, + instance: Any, + schema: Any, + current_path: Path, + instance_path: str, + stack: set[tuple[Path, int, int]], + ) -> list[str]: + if schema is True: + return [] + if schema is False: + return [f"{instance_path}: boolean schema false"] + if not isinstance(schema, dict): + raise SchemaResolutionError(f"schema at {instance_path} is not an object or boolean") + + marker = (current_path.resolve(), id(schema), id(instance)) + if marker in stack: + return [] + stack = set(stack) + stack.add(marker) + errors: list[str] = [] + + if "$ref" in schema: + target, target_path = self.resolve(str(schema["$ref"]), current_path) + errors.extend(self._validate(instance, target, target_path, instance_path, stack)) + + if "allOf" in schema: + for index, branch_errors in enumerate( + self._branch_errors(instance, schema["allOf"], current_path, instance_path, stack) + ): + errors.extend(f"{message} [allOf[{index}]]" for message in branch_errors) + + if "anyOf" in schema: + branches = self._branch_errors(instance, schema["anyOf"], current_path, instance_path, stack) + if not any(not branch for branch in branches): + details = " | ".join( + f"branch {index}: {branch[0] if branch else 'matched'}" + for index, branch in enumerate(branches) + ) + errors.append(f"{instance_path}: anyOf matched no branch ({details})") + + if "oneOf" in schema: + branches = self._branch_errors(instance, schema["oneOf"], current_path, instance_path, stack) + matched = [index for index, branch in enumerate(branches) if not branch] + if len(matched) != 1: + details = " | ".join( + f"branch {index}: {branch[0] if branch else 'matched'}" + for index, branch in enumerate(branches) + ) + errors.append( + f"{instance_path}: oneOf expected exactly one match, got {matched} ({details})" + ) + + if "not" in schema and not self._validate( + instance, schema["not"], current_path, instance_path, set(stack) + ): + errors.append(f"{instance_path}: not subschema matched") + + if "if" in schema: + condition_matches = not self._validate( + instance, schema["if"], current_path, instance_path, set(stack) + ) + chosen = "then" if condition_matches else "else" + if chosen in schema: + errors.extend( + f"{message} [{chosen}]" + for message in self._validate( + instance, schema[chosen], current_path, instance_path, set(stack) + ) + ) + + expected_type = schema.get("type") + if expected_type is not None: + expected_types = [expected_type] if isinstance(expected_type, str) else expected_type + if not isinstance(expected_types, list) or not all(isinstance(item, str) for item in expected_types): + raise SchemaResolutionError(f"invalid type declaration at {instance_path}") + if not any(self._type_matches(instance, item) for item in expected_types): + errors.append( + f"{instance_path}: expected type {expected_types}, got {type(instance).__name__}" + ) + return errors + + if "const" in schema and instance != schema["const"]: + errors.append(f"{instance_path}: expected const {schema['const']!r}, got {instance!r}") + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{instance_path}: value {instance!r} is outside enum {schema['enum']!r}") + + if isinstance(instance, str): + if "pattern" in schema and re.search(str(schema["pattern"]), instance) is None: + errors.append(f"{instance_path}: string does not match {schema['pattern']!r}") + if len(instance) < int(schema.get("minLength", 0)): + errors.append(f"{instance_path}: string is shorter than minLength {schema['minLength']}") + if "maxLength" in schema and len(instance) > int(schema["maxLength"]): + errors.append(f"{instance_path}: string is longer than maxLength {schema['maxLength']}") + + if isinstance(instance, (int, float)) and not isinstance(instance, bool): + if "minimum" in schema and instance < schema["minimum"]: + errors.append(f"{instance_path}: {instance} is below minimum {schema['minimum']}") + if "maximum" in schema and instance > schema["maximum"]: + errors.append(f"{instance_path}: {instance} exceeds maximum {schema['maximum']}") + if "exclusiveMinimum" in schema and instance <= schema["exclusiveMinimum"]: + errors.append( + f"{instance_path}: {instance} is not above exclusiveMinimum {schema['exclusiveMinimum']}" + ) + if "exclusiveMaximum" in schema and instance >= schema["exclusiveMaximum"]: + errors.append( + f"{instance_path}: {instance} is not below exclusiveMaximum {schema['exclusiveMaximum']}" + ) + + if isinstance(instance, dict): + required = schema.get("required", []) + for key in required: + if key not in instance: + errors.append(f"{instance_path}: missing required property {key!r}") + properties = schema.get("properties", {}) + for key, value in instance.items(): + child_path = f"{instance_path}.{key}" + if key in properties: + errors.extend( + self._validate(value, properties[key], current_path, child_path, stack) + ) + elif schema.get("additionalProperties") is False: + errors.append(f"{child_path}: additional property is forbidden") + elif isinstance(schema.get("additionalProperties"), dict): + errors.extend( + self._validate( + value, + schema["additionalProperties"], + current_path, + child_path, + stack, + ) + ) + if len(instance) < int(schema.get("minProperties", 0)): + errors.append( + f"{instance_path}: object has fewer than minProperties {schema['minProperties']}" + ) + if "maxProperties" in schema and len(instance) > int(schema["maxProperties"]): + errors.append( + f"{instance_path}: object has more than maxProperties {schema['maxProperties']}" + ) + + if isinstance(instance, list): + if len(instance) < int(schema.get("minItems", 0)): + errors.append(f"{instance_path}: array has fewer than minItems {schema['minItems']}") + if "maxItems" in schema and len(instance) > int(schema["maxItems"]): + errors.append(f"{instance_path}: array has more than maxItems {schema['maxItems']}") + if schema.get("uniqueItems") is True: + identities = [self._json_identity(item) for item in instance] + if len(identities) != len(set(identities)): + errors.append(f"{instance_path}: array items are not unique") + prefix_items = schema.get("prefixItems", []) + if isinstance(prefix_items, list): + for index, child_schema in enumerate(prefix_items[: len(instance)]): + errors.extend( + self._validate( + instance[index], child_schema, current_path, f"{instance_path}[{index}]", stack + ) + ) + items_schema = schema.get("items") + if items_schema is not None: + start = len(prefix_items) if isinstance(prefix_items, list) else 0 + for index in range(start, len(instance)): + errors.extend( + self._validate( + instance[index], items_schema, current_path, f"{instance_path}[{index}]", stack + ) + ) + if "contains" in schema: + matched = sum( + not self._validate(item, schema["contains"], current_path, f"{instance_path}[{index}]", set(stack)) + for index, item in enumerate(instance) + ) + minimum = int(schema.get("minContains", 1)) + maximum = schema.get("maxContains") + if matched < minimum or (maximum is not None and matched > int(maximum)): + errors.append( + f"{instance_path}: contains matched {matched}, expected {minimum}..{maximum or 'unbounded'}" + ) + return errors + + +def load_inline_namespace() -> tuple[Mapping[str, Any], str]: + """Parse the Agent YAML and execute only its inline definitions.""" + agent = yaml.safe_load(AUTHORING_AGENT.read_text(encoding="utf-8")) + tasks = [ + task + for stage in agent["Agent"]["Stages"] + for task in stage.get("tasks", []) + if task.get("mcp") == "code-executor" and task.get("tool_name") == "run_code" + ] + if len(tasks) != 1: + raise AssertionError(f"expected exactly one inline run_code task, got {len(tasks)}") + code = tasks[0].get("parameters", {}).get("code") + if not isinstance(code, str) or not code.strip(): + raise AssertionError("inline Python code is missing") + namespace: dict[str, Any] = {"__name__": "s2_20_inline_offline_schema_test"} + exec(compile(code, str(AUTHORING_AGENT), "exec"), namespace) + return namespace, code + + +def pending_inputs_and_assets(namespace: Mapping[str, Any], code: str) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]: + """Create one minimal S2_10-like selected claim with checked-in PENDING assets.""" + digest = namespace["digest"] + option = { + "claim_option_id": CLAIM_OPTION_ID, + "source_local_ref": "option-minimal-001", + "normalized_claim_signature": { + "vocabulary_version": "stage2.claim_signature_vocab.v1", + "legal_basis_family": "CONTRACT", + "right_holder_refs": ["PARTY-P-001"], + "obligor_refs": ["PARTY-D-001"], + "performance_kind": "MONEY_PAYMENT", + "object_refs": [], + "legal_effect": "PERFORMANCE", + "source_occurrence_refs": ["FACT-001"], + }, + "relation": "primary", + "decision": "SUPPORTED", + "client_disposition": "CANDIDATE", + "client_instruction_refs": [], + "authority_refs": [], + "incompatible_with": [], + "precondition_refs": [], + "same_underlying_recovery_key": "RECOVERY-MINIMAL-001", + "element_statuses": [], + "defense_statuses": [], + "remedy_candidates": [], + "limitation": { + "urgency": "NONE_IDENTIFIED", + "basis_refs": [], + "authority_refs": [], + "unresolved_reason_codes": [], + }, + "impact_scope": "OPTION_ONLY", + "risk_level": "UNASSESSED", + "forbidden_outputs": [], + "review_resolutions": [], + "typed_provenance": [], + "missing_inputs": [], + "review_flags": [], + } + verdict = {"cluster_id": "CL-MINIMAL-001", "claim_options": [option]} + inputs = { + "verdicts": [verdict], + "verdict_sources": { + "CL-MINIMAL-001": { + "path": "map_s2_10/domain_verdicts/CL-MINIMAL-001.json", + "sha256": digest(verdict), + } + }, + "party_context": { + "parties": [{"party_id": "PARTY-P-001"}, {"party_id": "PARTY-D-001"}], + "title_chains": [], + "identity_issues": [], + }, + "evidence_inventory": { + "items": [ + {"evidence_id": "EVIDENCE-002"}, + {"evidence_id": "EVIDENCE-001"}, + ] + }, + "issue_ledger": {"schema_version": "stage2_issue_ledger.v1", "issues": []}, + "issue_ledger_raw": b'{"issues":[],"schema_version":"stage2_issue_ledger.v1"}\n', + } + + asset_paths = { + "case_registry": "manifest/case_type_registry.yml", + "rule_registry": "manifest/case_type_rule_registry.yml", + "activation_registry": "registry/calculations/calculation_activation_registry.yml", + "corpus_release": "manifest/corpus_release.json", + "corpus_scope": "registry/corpus/requirement_fact_scope.yml", + "signature_projection_registry": "registry/binding/binding_signature_projection.yml", + "party_rules": "registry/party/party_set_rules.yml", + "disposition_policy": "registry/planning/option_disposition_policy.yml", + "authority_release": "manifest/authority_release.json", + } + loaded = {key: load_document(ASSET_ROOT / path) for key, path in asset_paths.items()} + descriptor_paths = sorted((ASSET_ROOT / "registry/calculations").glob("CE-*.yml")) + descriptor_hashes = { + str(load_document(path)["calculator_id"]): digest(path.read_bytes()) + for path in descriptor_paths + } + assets = { + "case_registry": loaded["case_registry"], + "rule_registry": loaded["rule_registry"], + "activation_registry": loaded["activation_registry"], + "corpus_release": loaded["corpus_release"], + "corpus_scope": loaded["corpus_scope"], + "signature_projection_registry": loaded["signature_projection_registry"], + "disposition_policy": loaded["disposition_policy"], + "party_rules_eligible": loaded["party_rules"].get("registry_status") == "APPROVED", + "signature_projection_eligible": loaded["signature_projection_registry"].get("registry_status") == "APPROVED", + "disposition_policy_eligible": loaded["disposition_policy"].get("policy_status") == "APPROVED", + "authority_admitted": loaded["authority_release"].get("status") == "ADMITTED", + "asset_hashes": { + "case_registry": digest((ASSET_ROOT / asset_paths["case_registry"]).read_bytes()), + "rule_registry": digest((ASSET_ROOT / asset_paths["rule_registry"]).read_bytes()), + "signature_projection": digest((ASSET_ROOT / asset_paths["signature_projection_registry"]).read_bytes()), + "corpus_release": digest((ASSET_ROOT / asset_paths["corpus_release"]).read_bytes()), + }, + "calculator_descriptor_hashes": descriptor_hashes, + "schema_documents": { + path: load_document(ASSET_ROOT / path) + for path in ( + "schemas/relief_plan.schema.json", + "schemas/binding_retrieval.schema.json", + "schemas/calculation.schema.json", + ) + }, + "s2_20_code_sha256": digest(code.encode("utf-8")), + } + request = { + "run_binding_digest": HEX_A, + "expected_parent_stage2_release_sha256": HEX_B, + "expected_s2_10_publish_status_sha256": HEX_C, + } + return inputs, assets, request + + +class InlineOutputSchemaTest(unittest.TestCase): + maxDiff = None + + def test_inline_outputs_and_fixed_assets_match_canonical_defs(self) -> None: + namespace, code = load_inline_namespace() + inputs, assets, request = pending_inputs_and_assets(namespace, code) + result = namespace["execute_core"](inputs, assets, request, HEX_C) + payloads = namespace["artifact_payloads"](result, request) + validator = Draft2020Subset(ASSET_ROOT) + + failures: list[str] = [] + canonical_option = inputs["verdicts"][0]["claim_options"][0] + failures.extend( + "minimal canonical S2_10 option -> schemas/s2_10.schema.json#/$defs/canonical_claim_option: " + + message + for message in validator.validate_ref( + canonical_option, + "schemas/s2_10.schema.json#/$defs/canonical_claim_option", + ) + ) + for artifact_path, (payload, schema_ref, _producer) in sorted(payloads.items()): + try: + value = json.loads(payload.decode("utf-8")) + errors = validator.validate_ref(value, schema_ref) + namespace["validate_artifact_payload"]( + payload, schema_ref, artifact_path, assets["schema_documents"] + ) + except Exception as exc: # Keep all independent failures visible. + failures.append(f"artifact {artifact_path} -> {schema_ref}: {type(exc).__name__}: {exc}") + continue + failures.extend( + f"artifact {artifact_path} -> {schema_ref}: {message}" for message in errors + ) + + fixed_assets = { + "manifest/case_type_registry.yml": "schemas/binding_retrieval.schema.json#/$defs/case_type_registry", + "manifest/case_type_rule_registry.yml": "schemas/binding_retrieval.schema.json#/$defs/case_type_rule_registry", + "registry/binding/binding_signature_projection.yml": "schemas/binding_retrieval.schema.json#/$defs/binding_signature_projection_registry", + "registry/planning/option_disposition_policy.yml": "schemas/binding_retrieval.schema.json#/$defs/option_disposition_policy", + "registry/party/party_set_rules.yml": "schemas/binding_retrieval.schema.json#/$defs/party_set_rules", + "registry/corpus/requirement_fact_scope.yml": "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_scope_registry", + "registry/calculations/calculation_activation_registry.yml": "schemas/calculation.schema.json#/$defs/calculation_activation_registry", + } + for descriptor_path in sorted((ASSET_ROOT / "registry/calculations").glob("CE-*.yml")): + fixed_assets[descriptor_path.relative_to(ASSET_ROOT).as_posix()] = ( + "schemas/calculation.schema.json#/$defs/calculation_descriptor" + ) + + self.assertEqual( + len([path for path in fixed_assets if path.startswith("registry/calculations/CE-")]), + 17, + "the canonical calculation descriptor set must contain exactly 17 files", + ) + for relative_path, schema_ref in sorted(fixed_assets.items()): + try: + value = load_document(ASSET_ROOT / relative_path) + errors = validator.validate_ref(value, schema_ref) + except Exception as exc: # Keep all independent failures visible. + failures.append(f"fixed asset {relative_path} -> {schema_ref}: {type(exc).__name__}: {exc}") + continue + failures.extend( + f"fixed asset {relative_path} -> {schema_ref}: {message}" for message in errors + ) + + self.assertFalse( + failures, + "S2_20 canonical schema validation failures:\n" + "\n".join(failures), + ) + + def test_inline_validator_is_release_bound_and_fail_closed(self) -> None: + namespace, code = load_inline_namespace() + inputs, assets, request = pending_inputs_and_assets(namespace, code) + result = namespace["execute_core"](inputs, assets, request, HEX_C) + payloads = namespace["artifact_payloads"](result, request) + payload, schema_ref, _producer = payloads["exhibit_register.json"] + + namespace["validate_artifact_payload"]( + payload, schema_ref, "exhibit_register.json", assets["schema_documents"] + ) + mutated = json.loads(payload.decode("utf-8")) + mutated["schema_version"] = "wrong" + with self.assertRaises(Exception) as raised: + namespace["validate_artifact_payload"]( + namespace["canonical_bytes"](mutated), + schema_ref, + "exhibit_register.json", + assets["schema_documents"], + ) + self.assertEqual(getattr(raised.exception, "code", None), "ARTIFACT_SCHEMA_VALIDATION_FAILED") + mutated = json.loads(payload.decode("utf-8")) + mutated["rows"][0]["unexpected"] = True + with self.assertRaises(Exception) as raised: + namespace["validate_artifact_payload"]( + namespace["canonical_bytes"](mutated), + schema_ref, + "exhibit_register.json", + assets["schema_documents"], + ) + self.assertEqual(getattr(raised.exception, "code", None), "ARTIFACT_SCHEMA_VALIDATION_FAILED") + + def test_inline_evidence_register_and_exists_contract(self) -> None: + namespace, code = load_inline_namespace() + inputs, assets, request = pending_inputs_and_assets(namespace, code) + result = namespace["execute_core"](inputs, assets, request, HEX_C) + self.assertEqual( + result["exhibit_register"]["rows"], + [ + { + "evidence_ref": "EVIDENCE-001", + "source_key": "EVIDENCE-001", + "existing_label": None, + "assigned_label": "갑 제1호증", + "assignment_status": "DETERMINISTICALLY_ASSIGNED", + }, + { + "evidence_ref": "EVIDENCE-002", + "source_key": "EVIDENCE-002", + "existing_label": None, + "assigned_label": "갑 제2호증", + "assignment_status": "DETERMINISTICALLY_ASSIGNED", + }, + ], + ) + signature = {"claim_family": "CONTRACT"} + self.assertTrue( + namespace["eval_predicate"]( + signature, {"operator": "EXISTS", "field": "claim_family", "value": True} + ) + ) + self.assertFalse( + namespace["eval_predicate"]( + signature, {"operator": "EXISTS", "field": "claim_family", "value": False} + ) + ) + self.assertTrue( + namespace["eval_predicate"]( + {}, {"operator": "EXISTS", "field": "claim_family", "value": False} + ) + ) + + def test_inline_receipt_and_retrieval_contract_markers(self) -> None: + namespace, code = load_inline_namespace() + self.assertIn('canonical_code.get("code_sha256")', code) + self.assertNotIn('canonical_code.get("sha256")', code) + self.assertIn('publication_guard["external_trust_verified"]', code) + + class RetryClient: + def __init__(self) -> None: + self.calls = 0 + + def call(self, _tool: str, _arguments: Mapping[str, Any]) -> Mapping[str, Any]: + self.calls += 1 + if self.calls < 3: + raise namespace["S220Error"]("MCP_TRANSPORT", "transient") + return {"content": []} + + retry_client = RetryClient() + _result, attempts = namespace["call_with_transient_retry"]( + retry_client, "search_hybrid", {}, max_attempts=3 + ) + self.assertEqual(attempts, 3) + self.assertEqual(retry_client.calls, 3) + + hits, drops = namespace["canonicalize_retrieval_hits"]( + [ + { + "chunk_id": "CHUNK-B", + "chunk_sha256": HEX_B, + "post_rerank_rank": 1, + "hybrid_score": 0.4, + "reranker_score": None, + }, + { + "chunk_id": "CHUNK-A", + "chunk_sha256": HEX_A, + "post_rerank_rank": 2, + "hybrid_score": 0.9, + "reranker_score": None, + }, + { + "chunk_id": "CHUNK-A", + "chunk_sha256": HEX_C, + "post_rerank_rank": 1, + "hybrid_score": 0.8, + "reranker_score": None, + }, + ] + ) + self.assertEqual([row["chunk_id"] for row in hits], ["CHUNK-A", "CHUNK-B"]) + self.assertEqual(drops, [{"chunk_id": "CHUNK-A", "chunk_sha256": HEX_A, "drop_reason_code": "DUPLICATE_CHUNK"}]) + + def test_request_trust_paths_binding_gate_and_partial_publish_manifest(self) -> None: + namespace, _code = load_inline_namespace() + namespace["INLINE_USER_HASH"] = HEX_A + namespace["INLINE_WORKSPACE_HASH"] = HEX_B + run_digest = HEX_C + request = { + "schema_version": "stage2_s2_20_request.v1", + "request_id": "REQ-001", + "attempt_id": "ATTEMPT-001", + "run_binding_digest": run_digest, + "user_identity_hash": HEX_A, + "workspace_identity_hash": HEX_B, + "stage2_run_root_ref": f"stage2_runs/by-binding/{run_digest}", + "plan_output_root_ref": f"stage2_runs/by-binding/{run_digest}/plan", + "host_cas_receipt_ref": f"stage2_control/host_cas/{run_digest}/S2_20.json", + "host_cas_receipt_sha256": HEX_A, + "detached_admission_receipt_ref": "Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json", + "detached_admission_receipt_sha256": HEX_B, + "outer_execution_receipt_target_ref": f"stage2_runs/by-binding/{run_digest}/control/s2_20_outer_execution_receipt.json", + "expected_s2_10_publish_status_sha256": HEX_A, + "expected_parent_stage2_release_sha256": HEX_B, + "expected_s2_10_release_sha256": HEX_C, + "expected_s2_20_agent_sha256": HEX_A, + "expected_s2_20_executor_binding_sha256": HEX_B, + } + self.assertEqual(namespace["validate_request"](namespace["canonical_bytes"](request)), request) + for key, invalid in ( + ("host_cas_receipt_ref", "attacker/cas.json"), + ("detached_admission_receipt_ref", "attacker/admission.json"), + ("outer_execution_receipt_target_ref", "attacker/outer.json"), + ): + mutated = dict(request) + mutated[key] = invalid + with self.assertRaises(Exception) as raised: + namespace["validate_request"](namespace["canonical_bytes"](mutated)) + self.assertEqual(getattr(raised.exception, "code", None), "REQUEST_TRUST_ANCHOR_PATH") + + admission = { + "schema_version": "stage2_deterministic_admission_receipt.v1", + "admission_status": "CANARY_ADMITTED", + "parent_release_sha256": HEX_A, + "executor_binding_sha256": HEX_B, + "present_deterministic_stage_ids": ["S2_20"], + "stage_receipts": [], + "backend_capability_receipt_sha256": HEX_C, + "signature": "agentbackend-signature", + "signature_verification_status": "BACKEND_VERIFIED", + "signed_payload_sha256": HEX_A, + } + signed_before = namespace["digest"](namespace["_admission_signed_material"](admission)) + mutated_admission = dict(admission) + mutated_admission["backend_capability_receipt_sha256"] = HEX_B + signed_after = namespace["digest"](namespace["_admission_signed_material"](mutated_admission)) + self.assertNotEqual(signed_before, signed_after) + + pending_binding = (ASSET_ROOT / "deployment/stage2_code_executor_binding.yml").read_bytes() + with self.assertRaises(Exception) as raised: + namespace["_verify_binding_text"](pending_binding, request) + self.assertEqual(getattr(raised.exception, "code", None), "EXECUTOR_BINDING_NOT_ADMITTED") + + inputs, assets, core_request = pending_inputs_and_assets(namespace, _code) + core_request.update( + { + "attempt_id": "ATTEMPT-PARTIAL", + "plan_output_root_ref": f"stage2_runs/by-binding/{core_request['run_binding_digest']}/plan", + "expected_s2_20_agent_sha256": HEX_A, + } + ) + result = namespace["execute_core"](inputs, assets, core_request, HEX_C) + + class PartialWriter: + def __init__(self) -> None: + self.files: dict[str, bytes] = {} + self.calls = 0 + self.fail_at: int | None = 2 + + def write_immutable(self, path: str, payload: bytes) -> str: + self.calls += 1 + if self.fail_at is not None and self.calls == self.fail_at: + raise RuntimeError("injected write failure") + self.files[path] = payload + return namespace["digest"](payload) + + writer = PartialWriter() + with self.assertRaises(Exception) as raised: + namespace["publish"](writer, core_request, result) + self.assertEqual(getattr(raised.exception, "code", None), "PUBLISH_PARTIAL_WRITE") + error = raised.exception.as_dict() + self.assertEqual(len(error["details"]["written_artifact_rows"]), 1) + writer.fail_at = None + namespace["publish_diagnostic"](writer, core_request, error) + barrier_path = f"{core_request['plan_output_root_ref']}/plan_publish_status.json" + barrier = json.loads(writer.files[barrier_path]) + self.assertFalse(barrier["consumer_authorized"]) + self.assertEqual(len(barrier["artifact_rows"]), 2) + self.assertIn("plan/technical_diagnostic.json", [row["path"] for row in barrier["artifact_rows"]]) + + class BarrierWriter: + def __init__(self) -> None: + self.files: dict[str, bytes] = {} + self.failed_once = False + + def write_immutable(self, path: str, payload: bytes) -> str: + if path.endswith("/plan_publish_status.json") and not self.failed_once: + self.failed_once = True + raise RuntimeError("injected barrier failure") + self.files[path] = payload + return namespace["digest"](payload) + + barrier_writer = BarrierWriter() + expected_payload_count = len(namespace["artifact_payloads"](result, core_request)) + with self.assertRaises(Exception) as raised: + namespace["publish"](barrier_writer, core_request, result) + self.assertEqual(getattr(raised.exception, "code", None), "PUBLISH_PARTIAL_WRITE") + barrier_error = raised.exception.as_dict() + self.assertEqual( + len(barrier_error["details"]["written_artifact_rows"]), expected_payload_count + ) + namespace["publish_diagnostic"](barrier_writer, core_request, barrier_error) + barrier = json.loads(barrier_writer.files[barrier_path]) + self.assertEqual(len(barrier["artifact_rows"]), expected_payload_count + 1) + self.assertFalse(barrier["consumer_authorized"]) + + def test_upstream_schema_closure_self_hashes_and_special_assets(self) -> None: + namespace, code = load_inline_namespace() + fixed = { + "schema_relief_plan": str(ASSET_ROOT / "schemas/relief_plan.schema.json"), + "schema_binding_retrieval": str(ASSET_ROOT / "schemas/binding_retrieval.schema.json"), + "schema_calculation": str(ASSET_ROOT / "schemas/calculation.schema.json"), + "schema_corpus_collection": str(ASSET_ROOT / "corpus/weaviate_collection.schema.json"), + "schema_ingress": str(ASSET_ROOT / "schemas/ingress.schema.json"), + "schema_context": str(ASSET_ROOT / "schemas/context.schema.json"), + "schema_s2_10": str(ASSET_ROOT / "schemas/s2_10.schema.json"), + "schema_review_status": str(ASSET_ROOT / "schemas/review_status.schema.json"), + "schema_deployment": str(ASSET_ROOT / "schemas/deployment.schema.json"), + } + all_raw = {path: Path(path).read_bytes() for path in fixed.values()} + documents = namespace["build_schema_documents"](all_raw, fixed) + self.assertEqual( + set(documents), + { + "schemas/relief_plan.schema.json", + "schemas/binding_retrieval.schema.json", + "schemas/calculation.schema.json", + "schemas/ingress.schema.json", + "schemas/context.schema.json", + "schemas/s2_10.schema.json", + "schemas/review_status.schema.json", + "schemas/deployment.schema.json", + }, + ) + + def refs(value: Any) -> list[str]: + found: list[str] = [] + if isinstance(value, dict): + if isinstance(value.get("$ref"), str): + found.append(value["$ref"]) + for child in value.values(): + found.extend(refs(child)) + elif isinstance(value, list): + for child in value: + found.extend(refs(child)) + return found + + for schema_path in ( + "schemas/context.schema.json", + "schemas/review_status.schema.json", + "schemas/binding_retrieval.schema.json", + ): + self.assertFalse( + [ref for ref in refs(documents[schema_path]) if ".schema.json" in ref], + f"inline schema closure retains an external ref: {schema_path}", + ) + + body = {"schema_version": "test.v1", "value": 1} + sealed = {**body, "receipt_sha256": namespace["digest"](body)} + self.assertEqual( + namespace["_require_object_self_hash"]( + sealed, "receipt_sha256", code="TEST_SELF_HASH", label="test" + ), + sealed["receipt_sha256"], + ) + mutated = dict(sealed) + mutated["value"] = 2 + with self.assertRaises(Exception) as raised: + namespace["_require_object_self_hash"]( + mutated, "receipt_sha256", code="TEST_SELF_HASH", label="test" + ) + self.assertEqual(getattr(raised.exception, "code", None), "TEST_SELF_HASH") + + special_contracts = { + "registry/temporal/inheritance_reserved_share.yml": ( + "stage2.s2_20.inheritance_reserved_share_temporal.v1", + "registry_id", "S2-20-INHERITANCE-RESERVED-SHARE-TEMPORAL", "branch_rows", + ), + "registry/drafting/forbidden_relief_rules.yml": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-FORBIDDEN-RELIEF-RULES", "rule_rows", + ), + "registry/drafting/counter_performance_rules.yml": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-COUNTER-PERFORMANCE-RULES", "rule_rows", + ), + "registry/drafting/procedural_declaration_rules.yml": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-PROCEDURAL-DECLARATION-RULES", "rule_rows", + ), + "routing/actio_pauliana_route_registry.yml": ( + "stage2.s2_20.actio_pauliana_route_registry.v1", + "registry_id", "S2-20-ACTIO-PAULIANA-ROUTES", "route_rows", + ), + "routing/actio_pauliana_mortgage_route.yml": ( + "stage2.s2_20.actio_pauliana_mortgage_route.v1", + "registry_id", "S2-20-ACTIO-MORTGAGE-ROUTES", "route_rows", + ), + "validators/actio_value_compensation_invariants.yml": ( + "stage2.s2_20.actio_value_compensation_invariants.v1", + "validator_id", "S2-20-ACTIO-VALUE-COMPENSATION-INVARIANTS", "invariant_rows", + ), + } + for relative, contract in special_contracts.items(): + value = namespace["_validate_special_legal_asset"]( + load_document(ASSET_ROOT / relative), + schema_version=contract[0], + identity_field=contract[1], + identity=contract[2], + rows_field=contract[3], + label=relative, + ) + self.assertFalse(value["execution_eligible"]) + self.assertEqual(value[contract[3]], []) + + for marker in ( + '"ingress_status": ingress', + '"bundle_plan": bundle_plan', + '"cluster_slices": cluster_slices', + '"issue_patches": issue_parts', + '"assumption_parts": assumption_parts', + '"usage_parts": usage_parts', + '"wave_receipts": wave_receipts', + 'CLUSTER_ID.fullmatch(item)', + 'item.get("domain_verdict_sha256") != verdict_hash', + 'status.get("terminal_wave_receipt_sha256s") != [', + ): + self.assertIn(marker, code) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_offline_compilers.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_offline_compilers.py new file mode 100644 index 00000000..51510926 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_offline_compilers.py @@ -0,0 +1,397 @@ +#!/usr/bin/env python3 +"""Tests for the strict S2_20 projection and registry compilers.""" + +from __future__ import annotations + +import hashlib +import importlib.util +import json +from pathlib import Path +import tempfile +import unittest +from unittest import mock + +import yaml + + +ROOT = Path(__file__).resolve().parents[2] +MAIN = ROOT.parent.parent +PROJECT = MAIN.parent.parent + + +def load(name: str, path: Path): + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +BUILDER = load("s2_20_strict_builder", ROOT / "offline_build/build_s2_20_inline_projection.py") +CASE_COMPILER = load("s2_20_case_compiler", ROOT / "offline_build/compile_case_type_registry.py") +RULE_COMPILER = load("s2_20_rule_compiler", ROOT / "offline_build/compile_relief_rule_projection.py") +AUTHORITY_BUILDER = load( + "s2_20_authority_builder", + ROOT / "offline_build/build_authority_law_value_release.py", +) +CORPUS_BUILDER = load("s2_20_corpus_builder", ROOT / "offline_build/build_corpus_snapshot.py") + + +class OfflineCompilerTest(unittest.TestCase): + def test_legal_review_receipt_path_family_is_closed(self) -> None: + self.assertEqual( + RULE_COMPILER._safe_legal_review_path("legal_review/CT-001-SR-01.json"), + "legal_review/CT-001-SR-01.json", + ) + for invalid in ("review/CT-001.json", "legal_review/nested/CT-001.json", "rules/relief/CT-001.md"): + with self.assertRaisesRegex( + RULE_COMPILER.RuleCompileError, "LEGAL_REVIEW_RECEIPT_PATH_FAMILY" + ): + RULE_COMPILER._safe_legal_review_path(invalid) + + def test_approved_registry_invariants_are_closed_in_schema(self) -> None: + schema = json.loads((ROOT / "schemas/binding_retrieval.schema.json").read_text(encoding="utf-8")) + definitions = schema["$defs"] + self.assertEqual(definitions["case_type_catalog_row"]["properties"]["case_type_predicates"]["maxItems"], 1) + self.assertEqual(definitions["rule_branch"]["properties"]["sub_rule_predicates"]["maxItems"], 1) + option_approved = definitions["option_disposition_policy"]["allOf"][0]["then"]["properties"]["rules"] + party_approved = definitions["party_set_rules"]["allOf"][0]["then"]["properties"]["rules"] + scope_approved = definitions["requirement_fact_scope_registry"]["allOf"][0]["then"]["properties"]["rows"] + self.assertEqual(option_approved["minItems"], 1) + self.assertEqual(party_approved["minItems"], 1) + self.assertEqual(scope_approved["minItems"], 1) + + def test_current_authoring_has_exact_task_and_compilable_parser_bytes(self) -> None: + raw = (MAIN / "Stage_2_S2_20.yml").read_bytes() + code = BUILDER.extract_code(raw) + validation = BUILDER.validate_agent(raw, code) + self.assertEqual(validation["compile_status"], "PASS") + self.assertEqual(validation["code_sha256"], hashlib.sha256(code).hexdigest()) + + def test_duplicate_yaml_key_and_extra_task_procedure_key_are_rejected(self) -> None: + raw = (MAIN / "Stage_2_S2_20.yml").read_bytes() + duplicate = raw.replace(b"Agent:\n", b"Agent:\n name: duplicate\n", 1) + with self.assertRaisesRegex(BUILDER.ProjectionError, "YAML_DUPLICATE_KEY"): + BUILDER.parse_authoring_bytes(duplicate) + document = BUILDER.parse_authoring_bytes(raw) + document["Agent"]["Stages"][0]["task_procedure"]["internal_dag"] = "forbidden" + with self.assertRaisesRegex( + BUILDER.ProjectionError, "TASK_PROCEDURE_EXACT_IN_TASK_OUT_REQUIRED" + ): + BUILDER.extract_run_code_task(document) + + def test_case_catalog_compiler_reproduces_current_canonical_registry(self) -> None: + source = PROJECT / "case_kinds.md" + expected = (ROOT / "manifest/case_type_registry.yml").read_bytes() + compiled = CASE_COMPILER.canonical_json_bytes(CASE_COMPILER.compile_rows(source)) + self.assertEqual(compiled, expected) + document = json.loads(compiled) + self.assertEqual(document["registry_id"], "STAGE2_CASE_TYPE_137_V1") + self.assertEqual(len(document["rows"]), 137) + self.assertEqual( + [row["source_ordinal"] for row in document["rows"]], + list(range(1, 138)), + ) + self.assertEqual( + document["pending_legal_classification_case_type_ids"], + [f"CT-{ordinal:03d}" for ordinal in range(1, 138)], + ) + self.assertEqual(document["catalog_row_ids"], [f"CAT-{i:03d}" for i in range(1, 138)]) + self.assertEqual(document["registry_status"], "PENDING_LEGAL_CLASSIFICATION") + + def test_pending_rule_registry_is_exact_and_non_executable(self) -> None: + case_path = ROOT / "manifest/case_type_registry.yml" + compiled = RULE_COMPILER.compile_pending_rule_registry(case_path) + self.assertEqual(compiled["registry_id"], "STAGE2_CASE_TYPE_RULE_REGISTRY_V1") + self.assertEqual(compiled["case_type_registry_sha256"], hashlib.sha256(case_path.read_bytes()).hexdigest()) + self.assertEqual(len(compiled["rows"]), 137) + self.assertEqual( + [row["case_type_id"] for row in compiled["rows"]], + [f"CT-{ordinal:03d}" for ordinal in range(1, 138)], + ) + self.assertTrue(all(not row["rule_branches"] for row in compiled["rows"])) + self.assertEqual(compiled["registry_status"], "PENDING_LEGAL_CONTENT") + + def test_all_pending_relief_documents_compile_without_legal_inference(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + rule_root = Path(temporary) / "rules" + rule_root.mkdir() + for ordinal in range(1, 138): + metadata = { + "schema_version": RULE_COMPILER.METADATA_SCHEMA, + "case_type_id": f"CT-{ordinal:03d}", + "document_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + } + (rule_root / f"CT-{ordinal:03d}.md").write_text( + "---\n" + + yaml.safe_dump(metadata, allow_unicode=True, sort_keys=False) + + "---\n\n# PENDING_LEGAL_CONTENT\n", + encoding="utf-8", + ) + projection = RULE_COMPILER.compile_projection(rule_root, deployment_root=ROOT) + self.assertEqual(projection["schema_version"], "stage2_case_type_rule_registry.v1") + self.assertEqual(projection["registry_status"], "PENDING_LEGAL_CONTENT") + self.assertEqual(len(projection["rows"]), 137) + self.assertTrue(all(row["rule_branches"] == [] for row in projection["rows"])) + + def test_approved_branch_requires_exact_fk_hashes_and_bound_lawyer_receipt(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + deployment = Path(temporary) + receipt_ref = "legal_review/CT-001-SR-01.json" + receipt_path = deployment / receipt_ref + receipt_path.parent.mkdir(parents=True) + branch = { + "sub_rule_id": "SR-01", + "predicate": { + "predicate_id": "PRED-SR-01", + "clauses": [{"field": "claim_family", "operator": "EQ", "value": "MONEY"}], + "priority": 10, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": "0" * 64, + }, + "structured_relief_rule_id": "RR-CT-001-SR-01", + "renderer_ids": ["R01"], + "required_slot_ids": ["principal_amount"], + "corpus_scope_id": "CS-01", + "element_set_id": "ES-01", + "ce13_branch_id": "CE13-BR-01", + "party_set_rule_id": "PS-01", + "review_policy_id": "RP-01", + "authority_refs": ["AUTH-01"], + "review_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": "0" * 64, + } + metadata = { + "schema_version": RULE_COMPILER.METADATA_SCHEMA, + "case_type_id": "CT-001", + "document_status": "APPROVED", + "rule_branches": [branch], + } + body = b"\n# Reviewed rule body\n" + review_scope = RULE_COMPILER._review_scope_sha256(metadata, body) + signature_ref = "signatures/CT-001-SR-01.sig" + signature_path = deployment / signature_ref + signature_path.parent.mkdir(parents=True) + signature_raw = b"fixture-detached-signature\n" + signature_path.write_bytes(signature_raw) + receipt = { + "schema_version": RULE_COMPILER.RECEIPT_SCHEMA, + "status": "APPROVED", + "reviewer_role": "KOREAN_LAWYER", + "signed_by": "reviewer-001", + "signature_ref": signature_ref, + "signature_sha256": hashlib.sha256(signature_raw).hexdigest(), + "review_scope_sha256": review_scope, + "approved_rule_branch_keys": ["CT-001::SR-01"], + } + receipt_raw = RULE_COMPILER.canonical_json_bytes(receipt) + receipt_path.write_bytes(receipt_raw) + receipt_sha256 = hashlib.sha256(receipt_raw).hexdigest() + branch["legal_review_receipt_sha256"] = receipt_sha256 + branch["predicate"]["legal_review_receipt_sha256"] = receipt_sha256 + rule_path = deployment / "CT-001.md" + rule_path.write_bytes( + ( + "---\n" + + yaml.safe_dump(metadata, allow_unicode=True, sort_keys=False) + + "---\n" + ).encode("utf-8") + + body + ) + ref = {"path": "fixture.yml", "sha256": "1" * 64} + indexes = { + "case_type": {"CT-001": [{"path": "case.yml", "sha256": "2" * 64}]}, + "renderer": {"R01": [ref]}, + "corpus_scope": {"CS-01": [ref]}, + "element_set": {"ES-01": [ref]}, + "ce_13_branch": {"CE13-BR-01": [ref]}, + "party_set_rule": {"PS-01": [ref]}, + "review_policy": {"RP-01": [ref]}, + "authority": {"AUTH-01": [ref]}, + } + compiled = RULE_COMPILER.compile_document( + rule_path, + deployment_root=deployment, + foreign_key_indexes=indexes, + ) + compiled_branch = compiled["rule_branches"][0] + self.assertEqual(compiled_branch["structured_relief_rule_id"], "RR-CT-001-SR-01") + self.assertEqual(compiled_branch["legal_review_receipt_sha256"], receipt_sha256) + self.assertEqual(compiled_branch["renderer_ids"], ["R01"]) + self.assertEqual( + set(compiled_branch), + { + "sub_rule_id", "sub_rule_predicates", "rule_branch_key", + "structured_relief_rule_id", "markdown_source_path", + "markdown_source_sha256", "renderer_ids", "required_slot_ids", + "corpus_scope_id", "element_set_id", "ce13_branch_id", + "party_set_rule_id", "review_policy_id", "legal_content_status", + "legal_review_receipt_ref", "legal_review_receipt_sha256", + }, + ) + + invalid_indexes = dict(indexes) + invalid_indexes["renderer"] = {} + with self.assertRaisesRegex( + RULE_COMPILER.RuleCompileError, "RULE_BRANCH_FK_EXACT_ONE_REQUIRED" + ): + RULE_COMPILER.compile_document( + rule_path, + deployment_root=deployment, + foreign_key_indexes=invalid_indexes, + ) + + def test_normal_mode_emits_approved_canonical_rule_registry(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + deployment = Path(temporary) + rule_root = deployment / "rules/relief" + rule_root.mkdir(parents=True) + case_rows = [] + for ordinal in range(1, 138): + case_type_id = f"CT-{ordinal:03d}" + (rule_root / f"{case_type_id}.md").write_text("fixture\n", encoding="utf-8") + case_rows.append( + { + "case_type_id": case_type_id, + "claim_capable_disposition": "CLAIM_CAPABLE", + "legal_classification_status": "APPROVED", + "legal_content_status": "APPROVED", + "companion_case_type_ids": [], + } + ) + case_registry = { + "schema_version": "stage2_case_type_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_137_V1", + "rows": case_rows, + } + case_path = deployment / "manifest/case_type_registry.yml" + case_path.parent.mkdir(parents=True) + case_path.write_bytes(RULE_COMPILER.canonical_json_bytes(case_registry)) + + def compiled_document(path, **_kwargs): + case_type_id = path.stem + receipt_ref = f"legal_review/{case_type_id}.json" + receipt_sha256 = hashlib.sha256(case_type_id.encode()).hexdigest() + return { + "case_type_id": case_type_id, + "document_status": "APPROVED", + "markdown_source_path": f"rules/relief/{path.name}", + "markdown_source_sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "rule_branches": [ + { + "sub_rule_id": "SR-01", + "sub_rule_predicates": [ + { + "predicate_id": f"PRED-{case_type_id}", + "clauses": [{"field": "claim_family", "operator": "EQ", "value": "FIXTURE"}], + "priority": 1, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": receipt_sha256, + } + ], + "rule_branch_key": f"{case_type_id}::SR-01", + "structured_relief_rule_id": f"RR-{case_type_id}", + "markdown_source_path": f"rules/relief/{path.name}", + "markdown_source_sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "renderer_ids": ["R01"], + "required_slot_ids": ["principal_amount"], + "corpus_scope_id": f"CS-{case_type_id}", + "element_set_id": f"ES-{case_type_id}", + "ce13_branch_id": "CE13-BR-01", + "party_set_rule_id": "PS-01", + "review_policy_id": "RP-01", + "legal_content_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": receipt_sha256, + } + ], + } + + with mock.patch.object(RULE_COMPILER, "build_foreign_key_indexes", return_value={}), mock.patch.object( + RULE_COMPILER, "compile_document", side_effect=compiled_document + ): + registry = RULE_COMPILER.compile_projection( + rule_root, deployment_root=deployment + ) + self.assertEqual(registry["schema_version"], "stage2_case_type_rule_registry.v1") + self.assertEqual(registry["registry_status"], "APPROVED") + self.assertEqual(len(registry["rows"]), 137) + self.assertTrue(all(len(row["rule_branches"]) == 1 for row in registry["rows"])) + + def test_corpus_builder_emits_exact_receipt_shape_without_claiming_admission(self) -> None: + required = { + "schema_version", "receipt_id", "build_status", "build_executed", "created_at", + "collection_contract_status", "collection_schema_ref", "collection_schema_sha256", + "collection_name", "tenant", "snapshot_id", "snapshot_sha256", + "source_document_count", "chunk_count", "approved_case_type_ids", "components", + "legal_review_status", "live_mcp_admission_status", "production_eligible", + "unresolved_reason_codes", "guards", + } + with tempfile.TemporaryDirectory() as temporary: + raw_root = Path(temporary) / "raw" + raw_root.mkdir() + pending = CORPUS_BUILDER.build_receipt(raw_root, deployment_root=ROOT) + self.assertEqual(set(pending), required) + self.assertFalse(pending["build_executed"]) + source_dir = raw_root / "CT-001" + source_dir.mkdir() + (source_dir / "source.md").write_text("# Heading\nbody\n", encoding="utf-8") + built = CORPUS_BUILDER.build_receipt(raw_root, deployment_root=ROOT) + self.assertEqual(set(built), required) + self.assertTrue(built["build_executed"]) + self.assertFalse(built["production_eligible"]) + self.assertEqual(built["legal_review_status"], "PENDING_KOREAN_LAWYER_REVIEW") + + def test_pending_authority_and_corpus_outputs_are_byte_reproducible(self) -> None: + authority = AUTHORITY_BUILDER.build_release(ROOT, []) + self.assertEqual( + AUTHORITY_BUILDER._output_bytes(authority), + (ROOT / "manifest/authority_release.json").read_bytes(), + ) + corpus = CORPUS_BUILDER.build_receipt(ROOT / "corpus/raw", deployment_root=ROOT) + self.assertEqual( + CORPUS_BUILDER._output_bytes(corpus), + (ROOT / "corpus/build_receipt.json").read_bytes(), + ) + self.assertEqual(authority["status"], "DEV_UNAVAILABLE") + self.assertFalse(authority["executable"]) + self.assertEqual(corpus["build_status"], "PENDING_SOURCE_AND_OPERATOR_BUILD") + self.assertFalse(corpus["production_eligible"]) + + def test_authority_inventory_cannot_self_promote_to_legal_approval(self) -> None: + release = AUTHORITY_BUILDER.build_release( + ROOT, + [{"path": "law_values/general_law_values.yml", "approval_status": "APPROVED"}], + ) + self.assertEqual(release["status"], "DEV_UNAVAILABLE") + self.assertFalse(release["sealed"]) + self.assertFalse(release["signed"]) + self.assertFalse(release["executable"]) + self.assertEqual(len(release["captures"]), 1) + self.assertIn("RELEASE_SIGNATURE_MISSING", release["unresolved"]) + + def test_builder_check_modes_are_read_only_and_detect_exact_byte_drift(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + base = Path(temporary) + expected = b'{"fixture":true}\n' + for builder in (AUTHORITY_BUILDER, CORPUS_BUILDER): + output = base / builder.__name__ / "receipt.json" + self.assertEqual(builder.write_or_check(output, expected, check=True), 1) + self.assertFalse(output.exists()) + output.parent.mkdir(parents=True) + output.write_bytes(expected) + self.assertEqual(builder.write_or_check(output, expected, check=True), 0) + self.assertEqual(output.read_bytes(), expected) + output.write_bytes(b"drift\n") + before = output.read_bytes() + self.assertEqual(builder.write_or_check(output, expected, check=True), 1) + self.assertEqual(output.read_bytes(), before) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_offline_compilers.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_offline_compilers.txt new file mode 100644 index 00000000..51510926 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_offline_compilers.txt @@ -0,0 +1,397 @@ +#!/usr/bin/env python3 +"""Tests for the strict S2_20 projection and registry compilers.""" + +from __future__ import annotations + +import hashlib +import importlib.util +import json +from pathlib import Path +import tempfile +import unittest +from unittest import mock + +import yaml + + +ROOT = Path(__file__).resolve().parents[2] +MAIN = ROOT.parent.parent +PROJECT = MAIN.parent.parent + + +def load(name: str, path: Path): + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +BUILDER = load("s2_20_strict_builder", ROOT / "offline_build/build_s2_20_inline_projection.py") +CASE_COMPILER = load("s2_20_case_compiler", ROOT / "offline_build/compile_case_type_registry.py") +RULE_COMPILER = load("s2_20_rule_compiler", ROOT / "offline_build/compile_relief_rule_projection.py") +AUTHORITY_BUILDER = load( + "s2_20_authority_builder", + ROOT / "offline_build/build_authority_law_value_release.py", +) +CORPUS_BUILDER = load("s2_20_corpus_builder", ROOT / "offline_build/build_corpus_snapshot.py") + + +class OfflineCompilerTest(unittest.TestCase): + def test_legal_review_receipt_path_family_is_closed(self) -> None: + self.assertEqual( + RULE_COMPILER._safe_legal_review_path("legal_review/CT-001-SR-01.json"), + "legal_review/CT-001-SR-01.json", + ) + for invalid in ("review/CT-001.json", "legal_review/nested/CT-001.json", "rules/relief/CT-001.md"): + with self.assertRaisesRegex( + RULE_COMPILER.RuleCompileError, "LEGAL_REVIEW_RECEIPT_PATH_FAMILY" + ): + RULE_COMPILER._safe_legal_review_path(invalid) + + def test_approved_registry_invariants_are_closed_in_schema(self) -> None: + schema = json.loads((ROOT / "schemas/binding_retrieval.schema.json").read_text(encoding="utf-8")) + definitions = schema["$defs"] + self.assertEqual(definitions["case_type_catalog_row"]["properties"]["case_type_predicates"]["maxItems"], 1) + self.assertEqual(definitions["rule_branch"]["properties"]["sub_rule_predicates"]["maxItems"], 1) + option_approved = definitions["option_disposition_policy"]["allOf"][0]["then"]["properties"]["rules"] + party_approved = definitions["party_set_rules"]["allOf"][0]["then"]["properties"]["rules"] + scope_approved = definitions["requirement_fact_scope_registry"]["allOf"][0]["then"]["properties"]["rows"] + self.assertEqual(option_approved["minItems"], 1) + self.assertEqual(party_approved["minItems"], 1) + self.assertEqual(scope_approved["minItems"], 1) + + def test_current_authoring_has_exact_task_and_compilable_parser_bytes(self) -> None: + raw = (MAIN / "Stage_2_S2_20.yml").read_bytes() + code = BUILDER.extract_code(raw) + validation = BUILDER.validate_agent(raw, code) + self.assertEqual(validation["compile_status"], "PASS") + self.assertEqual(validation["code_sha256"], hashlib.sha256(code).hexdigest()) + + def test_duplicate_yaml_key_and_extra_task_procedure_key_are_rejected(self) -> None: + raw = (MAIN / "Stage_2_S2_20.yml").read_bytes() + duplicate = raw.replace(b"Agent:\n", b"Agent:\n name: duplicate\n", 1) + with self.assertRaisesRegex(BUILDER.ProjectionError, "YAML_DUPLICATE_KEY"): + BUILDER.parse_authoring_bytes(duplicate) + document = BUILDER.parse_authoring_bytes(raw) + document["Agent"]["Stages"][0]["task_procedure"]["internal_dag"] = "forbidden" + with self.assertRaisesRegex( + BUILDER.ProjectionError, "TASK_PROCEDURE_EXACT_IN_TASK_OUT_REQUIRED" + ): + BUILDER.extract_run_code_task(document) + + def test_case_catalog_compiler_reproduces_current_canonical_registry(self) -> None: + source = PROJECT / "case_kinds.md" + expected = (ROOT / "manifest/case_type_registry.yml").read_bytes() + compiled = CASE_COMPILER.canonical_json_bytes(CASE_COMPILER.compile_rows(source)) + self.assertEqual(compiled, expected) + document = json.loads(compiled) + self.assertEqual(document["registry_id"], "STAGE2_CASE_TYPE_137_V1") + self.assertEqual(len(document["rows"]), 137) + self.assertEqual( + [row["source_ordinal"] for row in document["rows"]], + list(range(1, 138)), + ) + self.assertEqual( + document["pending_legal_classification_case_type_ids"], + [f"CT-{ordinal:03d}" for ordinal in range(1, 138)], + ) + self.assertEqual(document["catalog_row_ids"], [f"CAT-{i:03d}" for i in range(1, 138)]) + self.assertEqual(document["registry_status"], "PENDING_LEGAL_CLASSIFICATION") + + def test_pending_rule_registry_is_exact_and_non_executable(self) -> None: + case_path = ROOT / "manifest/case_type_registry.yml" + compiled = RULE_COMPILER.compile_pending_rule_registry(case_path) + self.assertEqual(compiled["registry_id"], "STAGE2_CASE_TYPE_RULE_REGISTRY_V1") + self.assertEqual(compiled["case_type_registry_sha256"], hashlib.sha256(case_path.read_bytes()).hexdigest()) + self.assertEqual(len(compiled["rows"]), 137) + self.assertEqual( + [row["case_type_id"] for row in compiled["rows"]], + [f"CT-{ordinal:03d}" for ordinal in range(1, 138)], + ) + self.assertTrue(all(not row["rule_branches"] for row in compiled["rows"])) + self.assertEqual(compiled["registry_status"], "PENDING_LEGAL_CONTENT") + + def test_all_pending_relief_documents_compile_without_legal_inference(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + rule_root = Path(temporary) / "rules" + rule_root.mkdir() + for ordinal in range(1, 138): + metadata = { + "schema_version": RULE_COMPILER.METADATA_SCHEMA, + "case_type_id": f"CT-{ordinal:03d}", + "document_status": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + } + (rule_root / f"CT-{ordinal:03d}.md").write_text( + "---\n" + + yaml.safe_dump(metadata, allow_unicode=True, sort_keys=False) + + "---\n\n# PENDING_LEGAL_CONTENT\n", + encoding="utf-8", + ) + projection = RULE_COMPILER.compile_projection(rule_root, deployment_root=ROOT) + self.assertEqual(projection["schema_version"], "stage2_case_type_rule_registry.v1") + self.assertEqual(projection["registry_status"], "PENDING_LEGAL_CONTENT") + self.assertEqual(len(projection["rows"]), 137) + self.assertTrue(all(row["rule_branches"] == [] for row in projection["rows"])) + + def test_approved_branch_requires_exact_fk_hashes_and_bound_lawyer_receipt(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + deployment = Path(temporary) + receipt_ref = "legal_review/CT-001-SR-01.json" + receipt_path = deployment / receipt_ref + receipt_path.parent.mkdir(parents=True) + branch = { + "sub_rule_id": "SR-01", + "predicate": { + "predicate_id": "PRED-SR-01", + "clauses": [{"field": "claim_family", "operator": "EQ", "value": "MONEY"}], + "priority": 10, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": "0" * 64, + }, + "structured_relief_rule_id": "RR-CT-001-SR-01", + "renderer_ids": ["R01"], + "required_slot_ids": ["principal_amount"], + "corpus_scope_id": "CS-01", + "element_set_id": "ES-01", + "ce13_branch_id": "CE13-BR-01", + "party_set_rule_id": "PS-01", + "review_policy_id": "RP-01", + "authority_refs": ["AUTH-01"], + "review_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": "0" * 64, + } + metadata = { + "schema_version": RULE_COMPILER.METADATA_SCHEMA, + "case_type_id": "CT-001", + "document_status": "APPROVED", + "rule_branches": [branch], + } + body = b"\n# Reviewed rule body\n" + review_scope = RULE_COMPILER._review_scope_sha256(metadata, body) + signature_ref = "signatures/CT-001-SR-01.sig" + signature_path = deployment / signature_ref + signature_path.parent.mkdir(parents=True) + signature_raw = b"fixture-detached-signature\n" + signature_path.write_bytes(signature_raw) + receipt = { + "schema_version": RULE_COMPILER.RECEIPT_SCHEMA, + "status": "APPROVED", + "reviewer_role": "KOREAN_LAWYER", + "signed_by": "reviewer-001", + "signature_ref": signature_ref, + "signature_sha256": hashlib.sha256(signature_raw).hexdigest(), + "review_scope_sha256": review_scope, + "approved_rule_branch_keys": ["CT-001::SR-01"], + } + receipt_raw = RULE_COMPILER.canonical_json_bytes(receipt) + receipt_path.write_bytes(receipt_raw) + receipt_sha256 = hashlib.sha256(receipt_raw).hexdigest() + branch["legal_review_receipt_sha256"] = receipt_sha256 + branch["predicate"]["legal_review_receipt_sha256"] = receipt_sha256 + rule_path = deployment / "CT-001.md" + rule_path.write_bytes( + ( + "---\n" + + yaml.safe_dump(metadata, allow_unicode=True, sort_keys=False) + + "---\n" + ).encode("utf-8") + + body + ) + ref = {"path": "fixture.yml", "sha256": "1" * 64} + indexes = { + "case_type": {"CT-001": [{"path": "case.yml", "sha256": "2" * 64}]}, + "renderer": {"R01": [ref]}, + "corpus_scope": {"CS-01": [ref]}, + "element_set": {"ES-01": [ref]}, + "ce_13_branch": {"CE13-BR-01": [ref]}, + "party_set_rule": {"PS-01": [ref]}, + "review_policy": {"RP-01": [ref]}, + "authority": {"AUTH-01": [ref]}, + } + compiled = RULE_COMPILER.compile_document( + rule_path, + deployment_root=deployment, + foreign_key_indexes=indexes, + ) + compiled_branch = compiled["rule_branches"][0] + self.assertEqual(compiled_branch["structured_relief_rule_id"], "RR-CT-001-SR-01") + self.assertEqual(compiled_branch["legal_review_receipt_sha256"], receipt_sha256) + self.assertEqual(compiled_branch["renderer_ids"], ["R01"]) + self.assertEqual( + set(compiled_branch), + { + "sub_rule_id", "sub_rule_predicates", "rule_branch_key", + "structured_relief_rule_id", "markdown_source_path", + "markdown_source_sha256", "renderer_ids", "required_slot_ids", + "corpus_scope_id", "element_set_id", "ce13_branch_id", + "party_set_rule_id", "review_policy_id", "legal_content_status", + "legal_review_receipt_ref", "legal_review_receipt_sha256", + }, + ) + + invalid_indexes = dict(indexes) + invalid_indexes["renderer"] = {} + with self.assertRaisesRegex( + RULE_COMPILER.RuleCompileError, "RULE_BRANCH_FK_EXACT_ONE_REQUIRED" + ): + RULE_COMPILER.compile_document( + rule_path, + deployment_root=deployment, + foreign_key_indexes=invalid_indexes, + ) + + def test_normal_mode_emits_approved_canonical_rule_registry(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + deployment = Path(temporary) + rule_root = deployment / "rules/relief" + rule_root.mkdir(parents=True) + case_rows = [] + for ordinal in range(1, 138): + case_type_id = f"CT-{ordinal:03d}" + (rule_root / f"{case_type_id}.md").write_text("fixture\n", encoding="utf-8") + case_rows.append( + { + "case_type_id": case_type_id, + "claim_capable_disposition": "CLAIM_CAPABLE", + "legal_classification_status": "APPROVED", + "legal_content_status": "APPROVED", + "companion_case_type_ids": [], + } + ) + case_registry = { + "schema_version": "stage2_case_type_registry.v1", + "registry_id": "STAGE2_CASE_TYPE_137_V1", + "rows": case_rows, + } + case_path = deployment / "manifest/case_type_registry.yml" + case_path.parent.mkdir(parents=True) + case_path.write_bytes(RULE_COMPILER.canonical_json_bytes(case_registry)) + + def compiled_document(path, **_kwargs): + case_type_id = path.stem + receipt_ref = f"legal_review/{case_type_id}.json" + receipt_sha256 = hashlib.sha256(case_type_id.encode()).hexdigest() + return { + "case_type_id": case_type_id, + "document_status": "APPROVED", + "markdown_source_path": f"rules/relief/{path.name}", + "markdown_source_sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "rule_branches": [ + { + "sub_rule_id": "SR-01", + "sub_rule_predicates": [ + { + "predicate_id": f"PRED-{case_type_id}", + "clauses": [{"field": "claim_family", "operator": "EQ", "value": "FIXTURE"}], + "priority": 1, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": receipt_sha256, + } + ], + "rule_branch_key": f"{case_type_id}::SR-01", + "structured_relief_rule_id": f"RR-{case_type_id}", + "markdown_source_path": f"rules/relief/{path.name}", + "markdown_source_sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "renderer_ids": ["R01"], + "required_slot_ids": ["principal_amount"], + "corpus_scope_id": f"CS-{case_type_id}", + "element_set_id": f"ES-{case_type_id}", + "ce13_branch_id": "CE13-BR-01", + "party_set_rule_id": "PS-01", + "review_policy_id": "RP-01", + "legal_content_status": "APPROVED", + "legal_review_receipt_ref": receipt_ref, + "legal_review_receipt_sha256": receipt_sha256, + } + ], + } + + with mock.patch.object(RULE_COMPILER, "build_foreign_key_indexes", return_value={}), mock.patch.object( + RULE_COMPILER, "compile_document", side_effect=compiled_document + ): + registry = RULE_COMPILER.compile_projection( + rule_root, deployment_root=deployment + ) + self.assertEqual(registry["schema_version"], "stage2_case_type_rule_registry.v1") + self.assertEqual(registry["registry_status"], "APPROVED") + self.assertEqual(len(registry["rows"]), 137) + self.assertTrue(all(len(row["rule_branches"]) == 1 for row in registry["rows"])) + + def test_corpus_builder_emits_exact_receipt_shape_without_claiming_admission(self) -> None: + required = { + "schema_version", "receipt_id", "build_status", "build_executed", "created_at", + "collection_contract_status", "collection_schema_ref", "collection_schema_sha256", + "collection_name", "tenant", "snapshot_id", "snapshot_sha256", + "source_document_count", "chunk_count", "approved_case_type_ids", "components", + "legal_review_status", "live_mcp_admission_status", "production_eligible", + "unresolved_reason_codes", "guards", + } + with tempfile.TemporaryDirectory() as temporary: + raw_root = Path(temporary) / "raw" + raw_root.mkdir() + pending = CORPUS_BUILDER.build_receipt(raw_root, deployment_root=ROOT) + self.assertEqual(set(pending), required) + self.assertFalse(pending["build_executed"]) + source_dir = raw_root / "CT-001" + source_dir.mkdir() + (source_dir / "source.md").write_text("# Heading\nbody\n", encoding="utf-8") + built = CORPUS_BUILDER.build_receipt(raw_root, deployment_root=ROOT) + self.assertEqual(set(built), required) + self.assertTrue(built["build_executed"]) + self.assertFalse(built["production_eligible"]) + self.assertEqual(built["legal_review_status"], "PENDING_KOREAN_LAWYER_REVIEW") + + def test_pending_authority_and_corpus_outputs_are_byte_reproducible(self) -> None: + authority = AUTHORITY_BUILDER.build_release(ROOT, []) + self.assertEqual( + AUTHORITY_BUILDER._output_bytes(authority), + (ROOT / "manifest/authority_release.json").read_bytes(), + ) + corpus = CORPUS_BUILDER.build_receipt(ROOT / "corpus/raw", deployment_root=ROOT) + self.assertEqual( + CORPUS_BUILDER._output_bytes(corpus), + (ROOT / "corpus/build_receipt.json").read_bytes(), + ) + self.assertEqual(authority["status"], "DEV_UNAVAILABLE") + self.assertFalse(authority["executable"]) + self.assertEqual(corpus["build_status"], "PENDING_SOURCE_AND_OPERATOR_BUILD") + self.assertFalse(corpus["production_eligible"]) + + def test_authority_inventory_cannot_self_promote_to_legal_approval(self) -> None: + release = AUTHORITY_BUILDER.build_release( + ROOT, + [{"path": "law_values/general_law_values.yml", "approval_status": "APPROVED"}], + ) + self.assertEqual(release["status"], "DEV_UNAVAILABLE") + self.assertFalse(release["sealed"]) + self.assertFalse(release["signed"]) + self.assertFalse(release["executable"]) + self.assertEqual(len(release["captures"]), 1) + self.assertIn("RELEASE_SIGNATURE_MISSING", release["unresolved"]) + + def test_builder_check_modes_are_read_only_and_detect_exact_byte_drift(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + base = Path(temporary) + expected = b'{"fixture":true}\n' + for builder in (AUTHORITY_BUILDER, CORPUS_BUILDER): + output = base / builder.__name__ / "receipt.json" + self.assertEqual(builder.write_or_check(output, expected, check=True), 1) + self.assertFalse(output.exists()) + output.parent.mkdir(parents=True) + output.write_bytes(expected) + self.assertEqual(builder.write_or_check(output, expected, check=True), 0) + self.assertEqual(output.read_bytes(), expected) + output.write_bytes(b"drift\n") + before = output.read_bytes() + self.assertEqual(builder.write_or_check(output, expected, check=True), 1) + self.assertEqual(output.read_bytes(), before) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_option_group_and_binding.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_option_group_and_binding.py new file mode 100644 index 00000000..03e719df --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_option_group_and_binding.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +"""Signature projection and exact C25/C26 binding tests.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[2] + + +def load(name: str, relative: str): + spec = importlib.util.spec_from_file_location(name, ROOT / relative) + if spec is None or spec.loader is None: + raise RuntimeError(relative) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +projection = load("projection", "runtime/binding_signature_projection.py") +c25 = load("c25", "runtime/c25_case_type_bind.py") +c26 = load("c26", "runtime/c26_rule_branch_bind.py") + + +class OptionAndBindingTest(unittest.TestCase): + def setUp(self) -> None: + self.source = { + "vocabulary_version": "stage2.claim_signature_vocab.v1", + "legal_basis_family": "CONTRACT", + "right_holder_refs": ["P-1"], + "obligor_refs": ["D-1"], + "performance_kind": "MONEY_PAYMENT", + "object_refs": [], + "legal_effect": "PERFORMANCE", + "source_occurrence_refs": ["O-1"], + } + self.registry = { + "target_vocabulary_version": "stage2.case_type_signature_vocab.v1", + "registry_status": "APPROVED", + "projection_rules": [ + { + "projection_rule_id": f"BSP-{field}", + "target_field": field, + "source_fields": [ + { + "claim_family": "legal_basis_family", + "legal_effect": "legal_effect", + "performance_kind": "performance_kind", + "object_kind": "c21_object_projection", + "party_role_pattern": "c21_party_projection", + "source_obligation_kind": "legal_basis_family", + "remedy_mode": "c20_remedy_projection", + "registry_action": "performance_kind", + "procedural_posture": "c20_dependency_projection", + "special_statute_tags": "legal_basis_family", + }[field] + ], + "derivation_operator": "SET_UNION" if field == "special_statute_tags" else "DIRECT_COPY", + "allowed_output_values": { + "claim_family": ["CONTRACT"], + "legal_effect": ["PERFORMANCE"], + "performance_kind": ["MONEY_PAYMENT"], + }.get(field, []), + "unknown_value": "UNKNOWN", + "implementation_status": "IMPLEMENTED", + "semantic_review_status": "APPROVED", + } + for field in projection.TARGET_FIELDS + ], + } + + def test_projection_preserves_source_and_unknown(self) -> None: + result = projection.project_signature(self.source, {}, {}, {}, self.registry) + self.assertEqual(result["signature"]["claim_family"], "CONTRACT") + self.assertEqual(result["signature"]["object_kind"], "UNKNOWN") + self.assertEqual(result["signature"]["special_statute_tags"], []) + + def test_c25_zero_one_multi_without_fuzzy_fallback(self) -> None: + signature = {"claim_family": "CONTRACT", "performance_kind": "MONEY_PAYMENT"} + predicate = { + "predicate_id": "P-1", + "clauses": [{"field": "claim_family", "operator": "EQ", "value": "CONTRACT"}], + "priority": 1, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": "review/P-1.json", + "legal_review_receipt_sha256": "a" * 64, + } + row = { + "case_type_id": "CT-001", + "row_kind": "CLAIM", + "claim_capable_disposition": "CLAIM_CAPABLE", + "legal_classification_status": "APPROVED", + "legal_content_status": "APPROVED", + "case_type_predicates": [predicate], + } + self.assertEqual(c25.bind_case_type(signature, [row])["binding_status"], "BOUND") + self.assertEqual(c25.bind_case_type(signature, [])["binding_issue_codes"], ["CASE_TYPE_BINDING_ZERO_MATCH"]) + second_predicate = dict(predicate, predicate_id="P-2") + second = dict(row, case_type_id="CT-002", case_type_predicates=[second_predicate]) + self.assertEqual(c25.bind_case_type(signature, [row, second])["binding_issue_codes"], ["CASE_TYPE_BINDING_MULTI_MATCH"]) + + def test_c26_checks_every_foreign_key_hash(self) -> None: + hashes = {"rules/relief/CT-001.md": "a" * 64} + predicate = { + "predicate_id": "SP-1", + "clauses": [{"field": "claim_family", "operator": "EQ", "value": "CONTRACT"}], + "priority": 1, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": "review/SP-1.json", + "legal_review_receipt_sha256": "b" * 64, + } + branch = { + "sub_rule_id": "SR-1", + "rule_branch_key": "CT-001::SR-1", + "sub_rule_predicates": [predicate], + "markdown_source_path": "rules/relief/CT-001.md", + "markdown_source_sha256": "a" * 64, + "renderer_ids": ["R01"], + "corpus_scope_id": "CS-01", + "element_set_id": "ES-01", + "party_set_rule_id": "PS-01", + "ce13_branch_id": "CE13-BR-01", + "legal_content_status": "APPROVED", + } + row = { + "case_type_id": "CT-001", + "claim_capable_disposition": "CLAIM_CAPABLE", + "legal_classification_status": "APPROVED", + "legal_content_status": "APPROVED", + "rule_branches": [branch], + } + result = c26.bind_rule_branch({"claim_family": "CONTRACT"}, {"binding_status": "BOUND", "case_type_id": "CT-001"}, [row], hashes) + self.assertEqual(result["binding_status"], "BOUND") + self.assertEqual(result["rule_branch_key"], "CT-001::SR-1") + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_option_group_and_binding.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_option_group_and_binding.txt new file mode 100644 index 00000000..03e719df --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_option_group_and_binding.txt @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +"""Signature projection and exact C25/C26 binding tests.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[2] + + +def load(name: str, relative: str): + spec = importlib.util.spec_from_file_location(name, ROOT / relative) + if spec is None or spec.loader is None: + raise RuntimeError(relative) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +projection = load("projection", "runtime/binding_signature_projection.py") +c25 = load("c25", "runtime/c25_case_type_bind.py") +c26 = load("c26", "runtime/c26_rule_branch_bind.py") + + +class OptionAndBindingTest(unittest.TestCase): + def setUp(self) -> None: + self.source = { + "vocabulary_version": "stage2.claim_signature_vocab.v1", + "legal_basis_family": "CONTRACT", + "right_holder_refs": ["P-1"], + "obligor_refs": ["D-1"], + "performance_kind": "MONEY_PAYMENT", + "object_refs": [], + "legal_effect": "PERFORMANCE", + "source_occurrence_refs": ["O-1"], + } + self.registry = { + "target_vocabulary_version": "stage2.case_type_signature_vocab.v1", + "registry_status": "APPROVED", + "projection_rules": [ + { + "projection_rule_id": f"BSP-{field}", + "target_field": field, + "source_fields": [ + { + "claim_family": "legal_basis_family", + "legal_effect": "legal_effect", + "performance_kind": "performance_kind", + "object_kind": "c21_object_projection", + "party_role_pattern": "c21_party_projection", + "source_obligation_kind": "legal_basis_family", + "remedy_mode": "c20_remedy_projection", + "registry_action": "performance_kind", + "procedural_posture": "c20_dependency_projection", + "special_statute_tags": "legal_basis_family", + }[field] + ], + "derivation_operator": "SET_UNION" if field == "special_statute_tags" else "DIRECT_COPY", + "allowed_output_values": { + "claim_family": ["CONTRACT"], + "legal_effect": ["PERFORMANCE"], + "performance_kind": ["MONEY_PAYMENT"], + }.get(field, []), + "unknown_value": "UNKNOWN", + "implementation_status": "IMPLEMENTED", + "semantic_review_status": "APPROVED", + } + for field in projection.TARGET_FIELDS + ], + } + + def test_projection_preserves_source_and_unknown(self) -> None: + result = projection.project_signature(self.source, {}, {}, {}, self.registry) + self.assertEqual(result["signature"]["claim_family"], "CONTRACT") + self.assertEqual(result["signature"]["object_kind"], "UNKNOWN") + self.assertEqual(result["signature"]["special_statute_tags"], []) + + def test_c25_zero_one_multi_without_fuzzy_fallback(self) -> None: + signature = {"claim_family": "CONTRACT", "performance_kind": "MONEY_PAYMENT"} + predicate = { + "predicate_id": "P-1", + "clauses": [{"field": "claim_family", "operator": "EQ", "value": "CONTRACT"}], + "priority": 1, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": "review/P-1.json", + "legal_review_receipt_sha256": "a" * 64, + } + row = { + "case_type_id": "CT-001", + "row_kind": "CLAIM", + "claim_capable_disposition": "CLAIM_CAPABLE", + "legal_classification_status": "APPROVED", + "legal_content_status": "APPROVED", + "case_type_predicates": [predicate], + } + self.assertEqual(c25.bind_case_type(signature, [row])["binding_status"], "BOUND") + self.assertEqual(c25.bind_case_type(signature, [])["binding_issue_codes"], ["CASE_TYPE_BINDING_ZERO_MATCH"]) + second_predicate = dict(predicate, predicate_id="P-2") + second = dict(row, case_type_id="CT-002", case_type_predicates=[second_predicate]) + self.assertEqual(c25.bind_case_type(signature, [row, second])["binding_issue_codes"], ["CASE_TYPE_BINDING_MULTI_MATCH"]) + + def test_c26_checks_every_foreign_key_hash(self) -> None: + hashes = {"rules/relief/CT-001.md": "a" * 64} + predicate = { + "predicate_id": "SP-1", + "clauses": [{"field": "claim_family", "operator": "EQ", "value": "CONTRACT"}], + "priority": 1, + "legal_review_status": "APPROVED", + "legal_review_receipt_ref": "review/SP-1.json", + "legal_review_receipt_sha256": "b" * 64, + } + branch = { + "sub_rule_id": "SR-1", + "rule_branch_key": "CT-001::SR-1", + "sub_rule_predicates": [predicate], + "markdown_source_path": "rules/relief/CT-001.md", + "markdown_source_sha256": "a" * 64, + "renderer_ids": ["R01"], + "corpus_scope_id": "CS-01", + "element_set_id": "ES-01", + "party_set_rule_id": "PS-01", + "ce13_branch_id": "CE13-BR-01", + "legal_content_status": "APPROVED", + } + row = { + "case_type_id": "CT-001", + "claim_capable_disposition": "CLAIM_CAPABLE", + "legal_classification_status": "APPROVED", + "legal_content_status": "APPROVED", + "rule_branches": [branch], + } + result = c26.bind_rule_branch({"claim_family": "CONTRACT"}, {"binding_status": "BOUND", "case_type_id": "CT-001"}, [row], hashes) + self.assertEqual(result["binding_status"], "BOUND") + self.assertEqual(result["rule_branch_key"], "CT-001::SR-1") + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_plan_publish_and_release.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_plan_publish_and_release.py new file mode 100644 index 00000000..6a2a0162 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_plan_publish_and_release.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +"""S2_20 release builder/validator and non-cycle contract tests.""" + +from __future__ import annotations + +import copy +import hashlib +import importlib.util +import json +from pathlib import Path +import tempfile +import unittest + + +ROOT = Path(__file__).resolve().parents[2] + + +def _load_module(name: str, path: Path): + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +BUILDER = _load_module("s2_20_release_builder", ROOT / "offline_build/build_release_manifests.py") +VALIDATOR = _load_module("s2_20_release_validator", ROOT / "release_ops/release_validator.py") + + +def _canonical_bytes(document: object) -> bytes: + return ( + json.dumps(document, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) + + "\n" + ).encode("utf-8") + + +def _write_json(path: Path, document: object) -> bytes: + raw = _canonical_bytes(document) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(raw) + return raw + + +def _digest(document: dict[str, object], field: str) -> str: + payload = copy.deepcopy(document) + payload.pop(field, None) + return hashlib.sha256(_canonical_bytes(payload)).hexdigest() + + +def _module_template(existing_sha: str) -> dict[str, object]: + return { + "schema_version": "stage2_module_manifest.v1", + "manifest_id": "TEST-MANIFEST", + "manifest_digest": "0" * 64, + "manifest_digest_contract": copy.deepcopy(VALIDATOR.EXPECTED_MANIFEST_DIGEST_CONTRACT), + "closure_summary": { + "module_count": 1, + "documentation_mirror_count": 0, + "test_module_count": 0, + "build_tool_module_count": 0, + "executable_agent_hash_included": False, + "executor_binding_hash_included": False, + "self_hash_included": False, + "stage2_release_hash_included": False, + }, + "documentation_mirrors": [], + "modules": [ + { + "asset_version": "existing.1", + "authority_ids": [], + "consumed_schema_ids": [], + "dependency_module_ids": [], + "implementation_status": "DEV_HASH_BOUND", + "incompatible_module_ids": [], + "inputs": [], + "module_id": "EXISTING", + "module_kind": "JSON_DATA", + "outputs": [], + "owner": "test", + "path": "data/existing.json", + "produced_schema_ids": [], + "schema_version": "test.v1", + "scope_predicate": "test", + "semantic_review_status": "PENDING_CROSS_AUDIT", + "sha256": existing_sha, + } + ], + } + + +def _parent_template() -> dict[str, object]: + document: dict[str, object] = { + "schema_version": "stage2_release.v2", + "release_id": "TEST-PARENT", + "release_class": "DEV_FIXTURE_RELEASE", + "release_status": "DRAFT_NOT_EXECUTABLE", + "authorization_status": "DEV_VALIDATION_ONLY", + "signature": "PENDING_SEQUENTIAL_BIND", + "module_manifest_ref": { + "path": "manifest/module_manifest.json", + "sha256": "0" * 64, + "binding_status": "BOUND", + }, + "executor_binding_ref": { + "path": "deployment/stage2_code_executor_binding.yml", + "schema_id": "stage2_code_executor_binding.v2", + "binding_status": "EXTERNAL_TRUST_ROOT_PENDING_LIVE_ADMISSION", + }, + "release_evidence": [{"status": "PENDING_SEQUENTIAL_BIND"}], + "bundle": {}, + "release_digest_contract": copy.deepcopy(VALIDATOR.EXPECTED_RELEASE_DIGEST_CONTRACT), + "release_digest": "0" * 64, + } + document["release_digest"] = _digest(document, "release_digest") + return document + + +def _case_registry() -> dict[str, object]: + rows = [] + for index in range(1, 138): + rows.append( + { + "catalog_row_id": f"CAT-{index:03d}", + "case_type_id": f"CT-{index:03d}", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + } + ) + return { + "schema_version": "stage2_case_type_registry.v1", + "registry_status": "PENDING_LEGAL_CLASSIFICATION", + "catalog_row_ids": [f"CAT-{index:03d}" for index in range(1, 138)], + "rows": rows, + } + + +def _rule_registry(case_sha: str) -> dict[str, object]: + return { + "schema_version": "stage2_case_type_rule_registry.v1", + "case_type_registry_ref": "manifest/case_type_registry.yml", + "case_type_registry_sha256": case_sha, + "registry_status": "PENDING_LEGAL_CONTENT", + "rows": [ + { + "case_type_id": f"CT-{index:03d}", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + } + for index in range(1, 138) + ], + } + + +def _coverage() -> dict[str, object]: + return { + "schema_version": "stage2.case_type_coverage.v1", + "status": "DEV_UNAVAILABLE", + "release_eligible": False, + "registry_dependency": {"path": "manifest/case_type_registry.yml", "sha256": None}, + "coverage_rows": [ + { + "case_type_id": f"CT-{index:03d}", + "release_status": "NOT_IMPLEMENTED", + "legal_signoff": "UNVERIFIED", + } + for index in range(1, 138) + ], + } + + +class PublishAndReleaseTest(unittest.TestCase): + def test_shared_binding_is_v3_and_has_unique_stage_rows(self) -> None: + text = (ROOT / "deployment/stage2_code_executor_binding.yml").read_text(encoding="utf-8") + self.assertIn("schema_version: stage2_code_executor_binding.v3", text) + self.assertIn("stage_bindings:", text) + self.assertEqual(text.count("stage_id: S2_00"), 1) + self.assertEqual(text.count("stage_id: S2_20"), 1) + + def test_deployment_schema_allocates_s2_20_contracts(self) -> None: + schema = json.loads((ROOT / "schemas/deployment.schema.json").read_text(encoding="utf-8")) + for name in ( + "stage2_code_executor_binding_v3", + "stage_code_executor_binding", + "s2_20_request", + "s2_20_execution_receipt", + "s2_20_inline_code_receipt", + ): + self.assertIn(name, schema["$defs"]) + + def test_builder_preserves_canonical_rows_and_reseals_parent(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + existing_raw = _write_json(root / "data/existing.json", {"value": 1}) + source_raw = b"print('release test')\n" + (root / "runtime").mkdir() + (root / "runtime/new.py").write_bytes(source_raw) + (root / "runtime/new.txt").write_bytes(source_raw) + module, parent = BUILDER.build_release_package( + root, + ["runtime/new.py", "runtime/new.txt"], + _module_template(hashlib.sha256(existing_raw).hexdigest()), + _parent_template(), + ) + self.assertEqual(module["schema_version"], "stage2_module_manifest.v1") + self.assertEqual(module["modules"][0]["module_id"], "EXISTING") + self.assertEqual(module["modules"][0]["size_bytes"], len(existing_raw)) + self.assertEqual(len(module["modules"]), 2) + new_row = module["modules"][1] + self.assertEqual(new_row["path"], "runtime/new.py") + self.assertEqual(new_row["mirror_path"], "runtime/new.txt") + self.assertTrue(new_row["mirror_byte_parity"]) + self.assertEqual(module["manifest_digest"], _digest(module, "manifest_digest")) + module_raw = _canonical_bytes(module) + self.assertEqual(parent["module_manifest_ref"]["sha256"], hashlib.sha256(module_raw).hexdigest()) + self.assertEqual(parent["release_digest"], _digest(parent, "release_digest")) + self.assertEqual(parent["executor_binding_ref"]["schema_id"], "stage2_code_executor_binding.v3") + self.assertNotIn("sha256", parent["executor_binding_ref"]) + self.assertEqual(parent["release_status"], "DRAFT_NOT_EXECUTABLE") + self.assertEqual(parent["signature"], "PENDING_SEQUENTIAL_BIND") + + def test_builder_rejects_every_downstream_cycle_member(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + existing_raw = _write_json(root / "data/existing.json", {"value": 1}) + template = _module_template(hashlib.sha256(existing_raw).hexdigest()) + for relative in sorted(BUILDER.FORBIDDEN_PARENT_MEMBERS): + with self.subTest(relative=relative): + with self.assertRaises(BUILDER.ReleaseBuildError): + BUILDER.build_module_manifest(root, template, [relative]) + + def test_validator_passes_offline_closure_and_reports_honest_pending(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + existing_raw = _write_json(root / "data/existing.json", {"value": 1}) + module, parent = BUILDER.build_release_package( + root, + [], + _module_template(hashlib.sha256(existing_raw).hexdigest()), + _parent_template(), + ) + module_raw = _write_json(root / "manifest/module_manifest.json", module) + parent["module_manifest_ref"]["sha256"] = hashlib.sha256(module_raw).hexdigest() + parent["release_digest"] = _digest(parent, "release_digest") + _write_json(root / "manifest/stage2_release.json", parent) + case_raw = _write_json(root / "manifest/case_type_registry.yml", _case_registry()) + _write_json(root / "manifest/case_type_rule_registry.yml", _rule_registry(hashlib.sha256(case_raw).hexdigest())) + _write_json(root / "manifest/case_type_coverage.json", _coverage()) + report = VALIDATOR.validate_package( + root, + root / "manifest/module_manifest.json", + root / "manifest/stage2_release.json", + root / "manifest/case_type_registry.yml", + root / "manifest/case_type_rule_registry.yml", + root / "manifest/case_type_coverage.json", + ) + self.assertEqual(report["status"], "PASS", report) + self.assertEqual(report["counts"]["case_type_count"], 137) + self.assertEqual(report["counts"]["relief_rule_document_count"], 0) + self.assertIn("FULL_137_LEGAL_COVERAGE_PENDING", {row["code"] for row in report["pending"]}) + + def test_validator_rejects_tamper_and_false_full_137_claim(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + existing_raw = _write_json(root / "data/existing.json", {"value": 1}) + module, parent = BUILDER.build_release_package( + root, + [], + _module_template(hashlib.sha256(existing_raw).hexdigest()), + _parent_template(), + ) + module_raw = _write_json(root / "manifest/module_manifest.json", module) + parent["module_manifest_ref"]["sha256"] = hashlib.sha256(module_raw).hexdigest() + parent["release_digest"] = _digest(parent, "release_digest") + _write_json(root / "manifest/stage2_release.json", parent) + case_raw = _write_json(root / "manifest/case_type_registry.yml", _case_registry()) + _write_json(root / "manifest/case_type_rule_registry.yml", _rule_registry(hashlib.sha256(case_raw).hexdigest())) + coverage = _coverage() + coverage["status"] = "FULL_137_PRODUCTION_READY" + coverage["release_eligible"] = True + _write_json(root / "manifest/case_type_coverage.json", coverage) + (root / "data/existing.json").write_text("tampered\n", encoding="utf-8") + report = VALIDATOR.validate_package( + root, + root / "manifest/module_manifest.json", + root / "manifest/stage2_release.json", + root / "manifest/case_type_registry.yml", + root / "manifest/case_type_rule_registry.yml", + root / "manifest/case_type_coverage.json", + ) + codes = {row["code"] for row in report["errors"]} + self.assertEqual(report["status"], "FAIL") + self.assertIn("MODULE_HASH_MISMATCH", codes) + self.assertIn("DISHONEST_FULL_137_READY_CLAIM", codes) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_plan_publish_and_release.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_plan_publish_and_release.txt new file mode 100644 index 00000000..6a2a0162 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_plan_publish_and_release.txt @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +"""S2_20 release builder/validator and non-cycle contract tests.""" + +from __future__ import annotations + +import copy +import hashlib +import importlib.util +import json +from pathlib import Path +import tempfile +import unittest + + +ROOT = Path(__file__).resolve().parents[2] + + +def _load_module(name: str, path: Path): + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +BUILDER = _load_module("s2_20_release_builder", ROOT / "offline_build/build_release_manifests.py") +VALIDATOR = _load_module("s2_20_release_validator", ROOT / "release_ops/release_validator.py") + + +def _canonical_bytes(document: object) -> bytes: + return ( + json.dumps(document, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) + + "\n" + ).encode("utf-8") + + +def _write_json(path: Path, document: object) -> bytes: + raw = _canonical_bytes(document) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(raw) + return raw + + +def _digest(document: dict[str, object], field: str) -> str: + payload = copy.deepcopy(document) + payload.pop(field, None) + return hashlib.sha256(_canonical_bytes(payload)).hexdigest() + + +def _module_template(existing_sha: str) -> dict[str, object]: + return { + "schema_version": "stage2_module_manifest.v1", + "manifest_id": "TEST-MANIFEST", + "manifest_digest": "0" * 64, + "manifest_digest_contract": copy.deepcopy(VALIDATOR.EXPECTED_MANIFEST_DIGEST_CONTRACT), + "closure_summary": { + "module_count": 1, + "documentation_mirror_count": 0, + "test_module_count": 0, + "build_tool_module_count": 0, + "executable_agent_hash_included": False, + "executor_binding_hash_included": False, + "self_hash_included": False, + "stage2_release_hash_included": False, + }, + "documentation_mirrors": [], + "modules": [ + { + "asset_version": "existing.1", + "authority_ids": [], + "consumed_schema_ids": [], + "dependency_module_ids": [], + "implementation_status": "DEV_HASH_BOUND", + "incompatible_module_ids": [], + "inputs": [], + "module_id": "EXISTING", + "module_kind": "JSON_DATA", + "outputs": [], + "owner": "test", + "path": "data/existing.json", + "produced_schema_ids": [], + "schema_version": "test.v1", + "scope_predicate": "test", + "semantic_review_status": "PENDING_CROSS_AUDIT", + "sha256": existing_sha, + } + ], + } + + +def _parent_template() -> dict[str, object]: + document: dict[str, object] = { + "schema_version": "stage2_release.v2", + "release_id": "TEST-PARENT", + "release_class": "DEV_FIXTURE_RELEASE", + "release_status": "DRAFT_NOT_EXECUTABLE", + "authorization_status": "DEV_VALIDATION_ONLY", + "signature": "PENDING_SEQUENTIAL_BIND", + "module_manifest_ref": { + "path": "manifest/module_manifest.json", + "sha256": "0" * 64, + "binding_status": "BOUND", + }, + "executor_binding_ref": { + "path": "deployment/stage2_code_executor_binding.yml", + "schema_id": "stage2_code_executor_binding.v2", + "binding_status": "EXTERNAL_TRUST_ROOT_PENDING_LIVE_ADMISSION", + }, + "release_evidence": [{"status": "PENDING_SEQUENTIAL_BIND"}], + "bundle": {}, + "release_digest_contract": copy.deepcopy(VALIDATOR.EXPECTED_RELEASE_DIGEST_CONTRACT), + "release_digest": "0" * 64, + } + document["release_digest"] = _digest(document, "release_digest") + return document + + +def _case_registry() -> dict[str, object]: + rows = [] + for index in range(1, 138): + rows.append( + { + "catalog_row_id": f"CAT-{index:03d}", + "case_type_id": f"CT-{index:03d}", + "legal_classification_status": "PENDING_LEGAL_CLASSIFICATION", + } + ) + return { + "schema_version": "stage2_case_type_registry.v1", + "registry_status": "PENDING_LEGAL_CLASSIFICATION", + "catalog_row_ids": [f"CAT-{index:03d}" for index in range(1, 138)], + "rows": rows, + } + + +def _rule_registry(case_sha: str) -> dict[str, object]: + return { + "schema_version": "stage2_case_type_rule_registry.v1", + "case_type_registry_ref": "manifest/case_type_registry.yml", + "case_type_registry_sha256": case_sha, + "registry_status": "PENDING_LEGAL_CONTENT", + "rows": [ + { + "case_type_id": f"CT-{index:03d}", + "claim_capable_disposition": "PENDING_LEGAL_CONTENT", + "rule_branches": [], + } + for index in range(1, 138) + ], + } + + +def _coverage() -> dict[str, object]: + return { + "schema_version": "stage2.case_type_coverage.v1", + "status": "DEV_UNAVAILABLE", + "release_eligible": False, + "registry_dependency": {"path": "manifest/case_type_registry.yml", "sha256": None}, + "coverage_rows": [ + { + "case_type_id": f"CT-{index:03d}", + "release_status": "NOT_IMPLEMENTED", + "legal_signoff": "UNVERIFIED", + } + for index in range(1, 138) + ], + } + + +class PublishAndReleaseTest(unittest.TestCase): + def test_shared_binding_is_v3_and_has_unique_stage_rows(self) -> None: + text = (ROOT / "deployment/stage2_code_executor_binding.yml").read_text(encoding="utf-8") + self.assertIn("schema_version: stage2_code_executor_binding.v3", text) + self.assertIn("stage_bindings:", text) + self.assertEqual(text.count("stage_id: S2_00"), 1) + self.assertEqual(text.count("stage_id: S2_20"), 1) + + def test_deployment_schema_allocates_s2_20_contracts(self) -> None: + schema = json.loads((ROOT / "schemas/deployment.schema.json").read_text(encoding="utf-8")) + for name in ( + "stage2_code_executor_binding_v3", + "stage_code_executor_binding", + "s2_20_request", + "s2_20_execution_receipt", + "s2_20_inline_code_receipt", + ): + self.assertIn(name, schema["$defs"]) + + def test_builder_preserves_canonical_rows_and_reseals_parent(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + existing_raw = _write_json(root / "data/existing.json", {"value": 1}) + source_raw = b"print('release test')\n" + (root / "runtime").mkdir() + (root / "runtime/new.py").write_bytes(source_raw) + (root / "runtime/new.txt").write_bytes(source_raw) + module, parent = BUILDER.build_release_package( + root, + ["runtime/new.py", "runtime/new.txt"], + _module_template(hashlib.sha256(existing_raw).hexdigest()), + _parent_template(), + ) + self.assertEqual(module["schema_version"], "stage2_module_manifest.v1") + self.assertEqual(module["modules"][0]["module_id"], "EXISTING") + self.assertEqual(module["modules"][0]["size_bytes"], len(existing_raw)) + self.assertEqual(len(module["modules"]), 2) + new_row = module["modules"][1] + self.assertEqual(new_row["path"], "runtime/new.py") + self.assertEqual(new_row["mirror_path"], "runtime/new.txt") + self.assertTrue(new_row["mirror_byte_parity"]) + self.assertEqual(module["manifest_digest"], _digest(module, "manifest_digest")) + module_raw = _canonical_bytes(module) + self.assertEqual(parent["module_manifest_ref"]["sha256"], hashlib.sha256(module_raw).hexdigest()) + self.assertEqual(parent["release_digest"], _digest(parent, "release_digest")) + self.assertEqual(parent["executor_binding_ref"]["schema_id"], "stage2_code_executor_binding.v3") + self.assertNotIn("sha256", parent["executor_binding_ref"]) + self.assertEqual(parent["release_status"], "DRAFT_NOT_EXECUTABLE") + self.assertEqual(parent["signature"], "PENDING_SEQUENTIAL_BIND") + + def test_builder_rejects_every_downstream_cycle_member(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + existing_raw = _write_json(root / "data/existing.json", {"value": 1}) + template = _module_template(hashlib.sha256(existing_raw).hexdigest()) + for relative in sorted(BUILDER.FORBIDDEN_PARENT_MEMBERS): + with self.subTest(relative=relative): + with self.assertRaises(BUILDER.ReleaseBuildError): + BUILDER.build_module_manifest(root, template, [relative]) + + def test_validator_passes_offline_closure_and_reports_honest_pending(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + existing_raw = _write_json(root / "data/existing.json", {"value": 1}) + module, parent = BUILDER.build_release_package( + root, + [], + _module_template(hashlib.sha256(existing_raw).hexdigest()), + _parent_template(), + ) + module_raw = _write_json(root / "manifest/module_manifest.json", module) + parent["module_manifest_ref"]["sha256"] = hashlib.sha256(module_raw).hexdigest() + parent["release_digest"] = _digest(parent, "release_digest") + _write_json(root / "manifest/stage2_release.json", parent) + case_raw = _write_json(root / "manifest/case_type_registry.yml", _case_registry()) + _write_json(root / "manifest/case_type_rule_registry.yml", _rule_registry(hashlib.sha256(case_raw).hexdigest())) + _write_json(root / "manifest/case_type_coverage.json", _coverage()) + report = VALIDATOR.validate_package( + root, + root / "manifest/module_manifest.json", + root / "manifest/stage2_release.json", + root / "manifest/case_type_registry.yml", + root / "manifest/case_type_rule_registry.yml", + root / "manifest/case_type_coverage.json", + ) + self.assertEqual(report["status"], "PASS", report) + self.assertEqual(report["counts"]["case_type_count"], 137) + self.assertEqual(report["counts"]["relief_rule_document_count"], 0) + self.assertIn("FULL_137_LEGAL_COVERAGE_PENDING", {row["code"] for row in report["pending"]}) + + def test_validator_rejects_tamper_and_false_full_137_claim(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + existing_raw = _write_json(root / "data/existing.json", {"value": 1}) + module, parent = BUILDER.build_release_package( + root, + [], + _module_template(hashlib.sha256(existing_raw).hexdigest()), + _parent_template(), + ) + module_raw = _write_json(root / "manifest/module_manifest.json", module) + parent["module_manifest_ref"]["sha256"] = hashlib.sha256(module_raw).hexdigest() + parent["release_digest"] = _digest(parent, "release_digest") + _write_json(root / "manifest/stage2_release.json", parent) + case_raw = _write_json(root / "manifest/case_type_registry.yml", _case_registry()) + _write_json(root / "manifest/case_type_rule_registry.yml", _rule_registry(hashlib.sha256(case_raw).hexdigest())) + coverage = _coverage() + coverage["status"] = "FULL_137_PRODUCTION_READY" + coverage["release_eligible"] = True + _write_json(root / "manifest/case_type_coverage.json", coverage) + (root / "data/existing.json").write_text("tampered\n", encoding="utf-8") + report = VALIDATOR.validate_package( + root, + root / "manifest/module_manifest.json", + root / "manifest/stage2_release.json", + root / "manifest/case_type_registry.yml", + root / "manifest/case_type_rule_registry.yml", + root / "manifest/case_type_coverage.json", + ) + codes = {row["code"] for row in report["errors"]} + self.assertEqual(report["status"], "FAIL") + self.assertIn("MODULE_HASH_MISMATCH", codes) + self.assertIn("DISHONEST_FULL_137_READY_CLAIM", codes) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_regression_manifest_closure.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_regression_manifest_closure.py new file mode 100644 index 00000000..540a1190 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_regression_manifest_closure.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +"""S2_20 physical/logical regression-manifest closure tests.""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path +import re +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +FIXTURE_ROOT = ROOT / "tests/fixtures/s2_20" +MANIFEST_PATH = FIXTURE_ROOT / "regression_manifest.json" +GLOBAL_MANIFEST_PATH = ROOT / "tests/fixtures/regression_manifest.json" +SHA256 = re.compile(r"^[a-f0-9]{64}$") +EXPECTED_FIXTURE_IDS = { + "S20-F01-SINGLE-OPTION-GROUP", + "S20-F02-MIXED-PORTFOLIO", + "S20-F03-CLIENT-NO-SUE", + "S20-F04-CASE-RULE-EXACTNESS", + "S20-F05-ROW-KIND-BRANCH", + "S20-F06-PARTY-TITLE", + "S20-F07-CE01-INTEREST", + "S20-F08-CE03-LIMITATION", + "S20-F09-CE08-RESERVED-SHARE", + "S20-F10-ACTIO-ROUTE-CAP", + "S20-F11-CE13-COURT-VALUE", + "S20-F12-RETRIEVAL-BOUNDARY", + "S20-F13-CORPUS-INTEGRITY", + "S20-F14-EXHIBIT-LABEL", + "S20-F15-OPTION-SILENT-LOSS", + "S20-F16-PUBLISH-BARRIER", + "S20-F17-LEGACY-PATH", +} +ALLOWED_ORACLE_STATUSES = { + "STRUCTURAL_ORACLE", + "STRUCTURAL_ORACLE_LEGAL_CONTENT_PENDING", + "PENDING_LEGAL_ORACLE", +} + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def canonical_sha256(value: object) -> str: + raw = json.dumps( + value, + ensure_ascii=False, + sort_keys=True, + separators=(",", ":"), + ).encode("utf-8") + return hashlib.sha256(raw).hexdigest() + + +def resolve_pointer(document: object, pointer: str) -> object: + if pointer == "": + return document + if not pointer.startswith("/"): + raise AssertionError(f"non-absolute JSON pointer: {pointer}") + current = document + for raw_token in pointer[1:].split("/"): + token = raw_token.replace("~1", "/").replace("~0", "~") + if isinstance(current, list): + if not token.isdigit(): + raise AssertionError(f"non-numeric array selector: {pointer}") + current = current[int(token)] + elif isinstance(current, dict): + current = current[token] + else: + raise AssertionError(f"pointer crosses scalar: {pointer}") + return current + + +class RegressionManifestClosureTest(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.manifest = json.loads(MANIFEST_PATH.read_text(encoding="utf-8")) + + def test_nine_physical_json_files_and_eight_payloads(self) -> None: + physical = sorted(path.name for path in FIXTURE_ROOT.glob("*.json")) + self.assertEqual(len(physical), 9) + self.assertIn("regression_manifest.json", physical) + self.assertEqual(self.manifest["payload_file_count"], 8) + declared = {entry["payload_path"] for entry in self.manifest["entries"]} + self.assertEqual(len(declared), 8) + expected_names = {Path(path).name for path in declared} | { + "regression_manifest.json" + } + self.assertEqual(set(physical), expected_names) + + def test_seventeen_logical_fixtures_are_unique_and_complete(self) -> None: + entries = self.manifest["entries"] + self.assertEqual(self.manifest["logical_fixture_count"], 17) + self.assertEqual(len(entries), 17) + identifiers = [entry["fixture_case_id"] for entry in entries] + self.assertEqual(len(identifiers), len(set(identifiers))) + self.assertEqual(set(identifiers), EXPECTED_FIXTURE_IDS) + + def test_payload_hash_selectors_and_oracles(self) -> None: + mutation_ids: set[str] = set() + for entry in self.manifest["entries"]: + with self.subTest(fixture_case_id=entry["fixture_case_id"]): + relative = Path(entry["payload_path"]) + self.assertFalse(relative.is_absolute()) + self.assertNotIn("..", relative.parts) + payload_path = ROOT / relative + self.assertTrue(payload_path.is_file()) + self.assertRegex(entry["payload_sha256"], SHA256) + self.assertEqual(sha256(payload_path), entry["payload_sha256"]) + + payload = json.loads(payload_path.read_text(encoding="utf-8")) + selected_case = resolve_pointer(payload, entry["case_selector"]) + self.assertIsInstance(selected_case, dict) + self.assertEqual( + selected_case["fixture_case_id"], + entry["fixture_case_id"], + ) + self.assertTrue(selected_case.get("source_refs")) + + oracles = entry["mutation_oracles"] + self.assertIsInstance(oracles, list) + self.assertGreaterEqual(len(oracles), 1) + self.assertRegex(entry["oracle_sha256"], SHA256) + self.assertEqual( + canonical_sha256(oracles), + entry["oracle_sha256"], + ) + self.assertIn( + entry["oracle_status"], + ALLOWED_ORACLE_STATUSES, + ) + + for oracle in oracles: + mutation_id = oracle["mutation_id"] + self.assertNotIn(mutation_id, mutation_ids) + mutation_ids.add(mutation_id) + selected_mutation = resolve_pointer( + payload, + oracle["mutation_selector"], + ) + self.assertIsInstance(selected_mutation, dict) + self.assertEqual( + selected_mutation["mutation_id"], + mutation_id, + ) + expected = oracle["expected"] + self.assertIsInstance(expected, dict) + self.assertTrue(expected) + issue_codes = expected.get("expected_issue_codes") + self.assertIsInstance(issue_codes, list) + self.assertEqual( + len(issue_codes), + len(set(issue_codes)), + ) + for key, value in expected.items(): + self.assertIn(key, selected_mutation) + self.assertEqual(selected_mutation[key], value) + + def test_legacy_path_is_only_a_negative_fixture(self) -> None: + legacy_entries = [ + entry + for entry in self.manifest["entries"] + if entry["fixture_case_id"] == "S20-F17-LEGACY-PATH" + ] + self.assertEqual(len(legacy_entries), 1) + entry = legacy_entries[0] + payload = json.loads((ROOT / entry["payload_path"]).read_text(encoding="utf-8")) + case = resolve_pointer(payload, entry["case_selector"]) + requested_path = case["input"]["requested_path"] + self.assertRegex(requested_path, r"/v\.[0-3]/") + expected = entry["mutation_oracles"][0]["expected"] + self.assertIn( + "LEGACY_STAGE2_RUNTIME_DEPENDENCY_FORBIDDEN", + expected["expected_issue_codes"], + ) + self.assertEqual( + self.manifest["closure"]["legacy_v0_v3_path_allowed_only_as_negative_fixture"], + entry["fixture_case_id"], + ) + + def test_global_manifest_binds_s2_20_manifest_and_all_payload_bytes(self) -> None: + global_manifest = json.loads(GLOBAL_MANIFEST_PATH.read_text(encoding="utf-8")) + binding = global_manifest.get("s2_20_fixture_binding") + self.assertIsInstance(binding, dict) + self.assertEqual( + binding["binding_status"], + "OFFLINE_BYTES_BOUND_LEGAL_AND_LIVE_PENDING", + ) + self.assertEqual(binding["logical_fixture_count"], 17) + self.assertEqual(binding["payload_file_count"], 8) + manifest_relative = Path(binding["fixture_manifest_path"]) + self.assertEqual(ROOT / manifest_relative, MANIFEST_PATH) + self.assertEqual(binding["fixture_manifest_sha256"], sha256(MANIFEST_PATH)) + payload_rows = binding["payloads"] + self.assertEqual(len(payload_rows), 8) + self.assertEqual( + {row["path"] for row in payload_rows}, + {entry["payload_path"] for entry in self.manifest["entries"]}, + ) + for row in payload_rows: + with self.subTest(path=row["path"]): + physical = ROOT / row["path"] + self.assertTrue(physical.is_file()) + self.assertEqual(row["sha256"], sha256(physical)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_regression_manifest_closure.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_regression_manifest_closure.txt new file mode 100644 index 00000000..540a1190 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_regression_manifest_closure.txt @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +"""S2_20 physical/logical regression-manifest closure tests.""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path +import re +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +FIXTURE_ROOT = ROOT / "tests/fixtures/s2_20" +MANIFEST_PATH = FIXTURE_ROOT / "regression_manifest.json" +GLOBAL_MANIFEST_PATH = ROOT / "tests/fixtures/regression_manifest.json" +SHA256 = re.compile(r"^[a-f0-9]{64}$") +EXPECTED_FIXTURE_IDS = { + "S20-F01-SINGLE-OPTION-GROUP", + "S20-F02-MIXED-PORTFOLIO", + "S20-F03-CLIENT-NO-SUE", + "S20-F04-CASE-RULE-EXACTNESS", + "S20-F05-ROW-KIND-BRANCH", + "S20-F06-PARTY-TITLE", + "S20-F07-CE01-INTEREST", + "S20-F08-CE03-LIMITATION", + "S20-F09-CE08-RESERVED-SHARE", + "S20-F10-ACTIO-ROUTE-CAP", + "S20-F11-CE13-COURT-VALUE", + "S20-F12-RETRIEVAL-BOUNDARY", + "S20-F13-CORPUS-INTEGRITY", + "S20-F14-EXHIBIT-LABEL", + "S20-F15-OPTION-SILENT-LOSS", + "S20-F16-PUBLISH-BARRIER", + "S20-F17-LEGACY-PATH", +} +ALLOWED_ORACLE_STATUSES = { + "STRUCTURAL_ORACLE", + "STRUCTURAL_ORACLE_LEGAL_CONTENT_PENDING", + "PENDING_LEGAL_ORACLE", +} + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def canonical_sha256(value: object) -> str: + raw = json.dumps( + value, + ensure_ascii=False, + sort_keys=True, + separators=(",", ":"), + ).encode("utf-8") + return hashlib.sha256(raw).hexdigest() + + +def resolve_pointer(document: object, pointer: str) -> object: + if pointer == "": + return document + if not pointer.startswith("/"): + raise AssertionError(f"non-absolute JSON pointer: {pointer}") + current = document + for raw_token in pointer[1:].split("/"): + token = raw_token.replace("~1", "/").replace("~0", "~") + if isinstance(current, list): + if not token.isdigit(): + raise AssertionError(f"non-numeric array selector: {pointer}") + current = current[int(token)] + elif isinstance(current, dict): + current = current[token] + else: + raise AssertionError(f"pointer crosses scalar: {pointer}") + return current + + +class RegressionManifestClosureTest(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.manifest = json.loads(MANIFEST_PATH.read_text(encoding="utf-8")) + + def test_nine_physical_json_files_and_eight_payloads(self) -> None: + physical = sorted(path.name for path in FIXTURE_ROOT.glob("*.json")) + self.assertEqual(len(physical), 9) + self.assertIn("regression_manifest.json", physical) + self.assertEqual(self.manifest["payload_file_count"], 8) + declared = {entry["payload_path"] for entry in self.manifest["entries"]} + self.assertEqual(len(declared), 8) + expected_names = {Path(path).name for path in declared} | { + "regression_manifest.json" + } + self.assertEqual(set(physical), expected_names) + + def test_seventeen_logical_fixtures_are_unique_and_complete(self) -> None: + entries = self.manifest["entries"] + self.assertEqual(self.manifest["logical_fixture_count"], 17) + self.assertEqual(len(entries), 17) + identifiers = [entry["fixture_case_id"] for entry in entries] + self.assertEqual(len(identifiers), len(set(identifiers))) + self.assertEqual(set(identifiers), EXPECTED_FIXTURE_IDS) + + def test_payload_hash_selectors_and_oracles(self) -> None: + mutation_ids: set[str] = set() + for entry in self.manifest["entries"]: + with self.subTest(fixture_case_id=entry["fixture_case_id"]): + relative = Path(entry["payload_path"]) + self.assertFalse(relative.is_absolute()) + self.assertNotIn("..", relative.parts) + payload_path = ROOT / relative + self.assertTrue(payload_path.is_file()) + self.assertRegex(entry["payload_sha256"], SHA256) + self.assertEqual(sha256(payload_path), entry["payload_sha256"]) + + payload = json.loads(payload_path.read_text(encoding="utf-8")) + selected_case = resolve_pointer(payload, entry["case_selector"]) + self.assertIsInstance(selected_case, dict) + self.assertEqual( + selected_case["fixture_case_id"], + entry["fixture_case_id"], + ) + self.assertTrue(selected_case.get("source_refs")) + + oracles = entry["mutation_oracles"] + self.assertIsInstance(oracles, list) + self.assertGreaterEqual(len(oracles), 1) + self.assertRegex(entry["oracle_sha256"], SHA256) + self.assertEqual( + canonical_sha256(oracles), + entry["oracle_sha256"], + ) + self.assertIn( + entry["oracle_status"], + ALLOWED_ORACLE_STATUSES, + ) + + for oracle in oracles: + mutation_id = oracle["mutation_id"] + self.assertNotIn(mutation_id, mutation_ids) + mutation_ids.add(mutation_id) + selected_mutation = resolve_pointer( + payload, + oracle["mutation_selector"], + ) + self.assertIsInstance(selected_mutation, dict) + self.assertEqual( + selected_mutation["mutation_id"], + mutation_id, + ) + expected = oracle["expected"] + self.assertIsInstance(expected, dict) + self.assertTrue(expected) + issue_codes = expected.get("expected_issue_codes") + self.assertIsInstance(issue_codes, list) + self.assertEqual( + len(issue_codes), + len(set(issue_codes)), + ) + for key, value in expected.items(): + self.assertIn(key, selected_mutation) + self.assertEqual(selected_mutation[key], value) + + def test_legacy_path_is_only_a_negative_fixture(self) -> None: + legacy_entries = [ + entry + for entry in self.manifest["entries"] + if entry["fixture_case_id"] == "S20-F17-LEGACY-PATH" + ] + self.assertEqual(len(legacy_entries), 1) + entry = legacy_entries[0] + payload = json.loads((ROOT / entry["payload_path"]).read_text(encoding="utf-8")) + case = resolve_pointer(payload, entry["case_selector"]) + requested_path = case["input"]["requested_path"] + self.assertRegex(requested_path, r"/v\.[0-3]/") + expected = entry["mutation_oracles"][0]["expected"] + self.assertIn( + "LEGACY_STAGE2_RUNTIME_DEPENDENCY_FORBIDDEN", + expected["expected_issue_codes"], + ) + self.assertEqual( + self.manifest["closure"]["legacy_v0_v3_path_allowed_only_as_negative_fixture"], + entry["fixture_case_id"], + ) + + def test_global_manifest_binds_s2_20_manifest_and_all_payload_bytes(self) -> None: + global_manifest = json.loads(GLOBAL_MANIFEST_PATH.read_text(encoding="utf-8")) + binding = global_manifest.get("s2_20_fixture_binding") + self.assertIsInstance(binding, dict) + self.assertEqual( + binding["binding_status"], + "OFFLINE_BYTES_BOUND_LEGAL_AND_LIVE_PENDING", + ) + self.assertEqual(binding["logical_fixture_count"], 17) + self.assertEqual(binding["payload_file_count"], 8) + manifest_relative = Path(binding["fixture_manifest_path"]) + self.assertEqual(ROOT / manifest_relative, MANIFEST_PATH) + self.assertEqual(binding["fixture_manifest_sha256"], sha256(MANIFEST_PATH)) + payload_rows = binding["payloads"] + self.assertEqual(len(payload_rows), 8) + self.assertEqual( + {row["path"] for row in payload_rows}, + {entry["payload_path"] for entry in self.manifest["entries"]}, + ) + for row in payload_rows: + with self.subTest(path=row["path"]): + physical = ROOT / row["path"] + self.assertTrue(physical.is_file()) + self.assertEqual(row["sha256"], sha256(physical)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_retrieval_and_pack.py b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_retrieval_and_pack.py new file mode 100644 index 00000000..62883b2e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_retrieval_and_pack.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +"""C27-C29 canonical schema-shape and no-fallback tests.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +RUN_DIGEST = "a" * 64 +SNAPSHOT_SHA256 = "b" * 64 +ATOMIC_CLAIM_ID = "AC-" + "1" * 16 +CLAIM_GROUP_ID = "CG-" + "2" * 16 + + +def load(name: str, rel: str): + spec = importlib.util.spec_from_file_location(name, ROOT / rel) + if spec is None or spec.loader is None: + raise RuntimeError(rel) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +c27 = load("c27", "runtime/c27_query_plan.py") +c28 = load("c28", "runtime/c28_weaviate_retrieve.py") +c29 = load("c29", "runtime/c29_pack_verify.py") + + +class RetrievalTest(unittest.TestCase): + def setUp(self) -> None: + self.binding = { + "case_type_binding_status": "BOUND", + "sub_rule_binding_status": "BOUND", + "legal_content_status": "APPROVED", + "case_type_id": "CT-001", + "sub_rule_id": "SR-1", + "rule_branch_key": "CT-001::SR-1", + "corpus_scope_id": "CS-1", + "element_set_id": "ES-1", + } + self.scope = { + "corpus_scope_id": "CS-1", + "element_set_id": "ES-1", + "corpus_element_ids": ["E-1"], + "snapshot_id": "SNAP-1", + "query_contract": { + "alpha": 0.5, + "top_k": 5, + "embedding_configuration_digest": "c" * 64, + "reranker_configuration_digest": "d" * 64, + "index_configuration_digest": "e" * 64, + }, + } + self.collection = { + "collection_name": "RequirementFact", + "tenant": "release-1", + "snapshot_id": "SNAP-1", + "snapshot_sha256": SNAPSHOT_SHA256, + } + + def plan(self): + return c27.build_query_plan( + ATOMIC_CLAIM_ID, + self.binding, + self.scope, + {"element": ["E-1"]}, + run_binding_digest=RUN_DIGEST, + effective_on="2026-08-31", + ) + + def hit(self) -> dict: + metadata = { + "schema_version": "stage2_requirement_fact_chunk_metadata.v1", + "document_id": "DOC-1", + "document_sha256": "f" * 64, + "chunk_id": "CH-1", + "chunk_sha256": "0" * 64, + "source_path": "CT-001/source.md", + "source_locator": "section-1", + "case_type_ids": ["CT-001"], + "sub_rule_ids": ["SR-1"], + "rule_branch_keys": ["CT-001::SR-1"], + "corpus_scope_ids": ["CS-1"], + "element_set_ids": ["ES-1"], + "corpus_element_ids": ["E-1"], + "doctrine_roles": ["element"], + "jurisdiction": "KR", + "effective_from": None, + "effective_to": None, + "authority_ids": ["AUTH-1"], + "proposition_ids": ["PROP-1"], + "approval_status": "APPROVED", + "legal_reviewer_receipt_ref": "legal_review/corpus.json", + "legal_reviewer_receipt_sha256": "9" * 64, + } + return { + "chunk_id": "CH-1", + "chunk_sha256": "0" * 64, + "source_document_id": "DOC-1", + "source_document_sha256": "f" * 64, + "source_locator": "section-1", + "pre_rerank_rank": 1, + "hybrid_score": 0.9, + "post_rerank_rank": 1, + "reranker_score": 0.95, + "metadata": metadata, + } + + def test_query_is_canonical_and_bound_to_exact_case_and_rule(self) -> None: + plan = self.plan() + self.assertEqual(plan["schema_version"], "stage2_requirement_fact_query_plan.v1") + self.assertEqual(set(plan), {"schema_version", "run_binding_digest", "atomic_claim_id", "rows", "query_plan_sha256"}) + row = plan["rows"][0] + self.assertEqual(row["filter"]["case_type_id"], "CT-001") + self.assertEqual(row["filter"]["sub_rule_id"], "SR-1") + self.assertEqual(row["filter"]["rule_branch_key"], "CT-001::SR-1") + + def test_unresolved_binding_produces_no_fabricated_query_row(self) -> None: + plan = c27.build_query_plan( + ATOMIC_CLAIM_ID, + {}, + {}, + {}, + run_binding_digest=RUN_DIGEST, + effective_on="2026-08-31", + ) + self.assertEqual(plan["rows"], []) + + def test_retrieval_emits_canonical_result_and_receipts_without_fallback(self) -> None: + plan = self.plan() + + def fake(request): + hits = [self.hit()] if request["filter"]["doctrine_role"] == "element" else [] + return { + "schema_version": "stage2_weaviate_hybrid_search_response.v1", + "request_id": request["request_id"], + "request_sha256": c28._digest(request), + "response_status": "COMPLETE" if hits else "NO_HIT", + "raw_hit_count": len(hits), + "selected_hits": hits, + "dropped_hits": [], + "deterministic_order_digest": c28._digest(hits) if hits else None, + "retry_count": 0, + } + + result, receipts = c28.retrieve_exact( + plan, + self.collection, + fake, + credential_ref="secret://weaviate/read-only", + ) + self.assertEqual(result["schema_version"], "stage2_requirement_fact_retrieval_result.v1") + self.assertEqual(result["result_status"], "COMPLETE") + self.assertEqual(len(result["selected_hits"]), 1) + self.assertEqual(len(receipts), 5) + self.assertTrue(all(row["credential_material_persisted"] is False for row in receipts)) + + pack = c29.verify_pack( + self.binding, + result, + { + "snapshot_id": "SNAP-1", + "snapshot_sha256": SNAPSHOT_SHA256, + "corpus_release_sha256": "8" * 64, + }, + [{"corpus_element_id": "E-1", "doctrine_role": "element", "stage1_slot_refs": ["SL-1"], "mapping_status": "EXACT"}], + claim_group_id=CLAIM_GROUP_ID, + retrieval_receipt_refs=[ + {"path": "receipt.json", "sha256": receipts[0]["receipt_sha256"], "json_pointer": ""} + ], + ) + self.assertEqual(pack["schema_version"], "stage2_requirement_fact_pack.v1") + self.assertEqual(pack["authority_class"], "CAUSE_AND_ELEMENT_CHECK") + self.assertFalse(pack["items"][0]["fact_creation_allowed"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_retrieval_and_pack.txt b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_retrieval_and_pack.txt new file mode 100644 index 00000000..62883b2e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/tests/s2_20/test_retrieval_and_pack.txt @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +"""C27-C29 canonical schema-shape and no-fallback tests.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +RUN_DIGEST = "a" * 64 +SNAPSHOT_SHA256 = "b" * 64 +ATOMIC_CLAIM_ID = "AC-" + "1" * 16 +CLAIM_GROUP_ID = "CG-" + "2" * 16 + + +def load(name: str, rel: str): + spec = importlib.util.spec_from_file_location(name, ROOT / rel) + if spec is None or spec.loader is None: + raise RuntimeError(rel) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +c27 = load("c27", "runtime/c27_query_plan.py") +c28 = load("c28", "runtime/c28_weaviate_retrieve.py") +c29 = load("c29", "runtime/c29_pack_verify.py") + + +class RetrievalTest(unittest.TestCase): + def setUp(self) -> None: + self.binding = { + "case_type_binding_status": "BOUND", + "sub_rule_binding_status": "BOUND", + "legal_content_status": "APPROVED", + "case_type_id": "CT-001", + "sub_rule_id": "SR-1", + "rule_branch_key": "CT-001::SR-1", + "corpus_scope_id": "CS-1", + "element_set_id": "ES-1", + } + self.scope = { + "corpus_scope_id": "CS-1", + "element_set_id": "ES-1", + "corpus_element_ids": ["E-1"], + "snapshot_id": "SNAP-1", + "query_contract": { + "alpha": 0.5, + "top_k": 5, + "embedding_configuration_digest": "c" * 64, + "reranker_configuration_digest": "d" * 64, + "index_configuration_digest": "e" * 64, + }, + } + self.collection = { + "collection_name": "RequirementFact", + "tenant": "release-1", + "snapshot_id": "SNAP-1", + "snapshot_sha256": SNAPSHOT_SHA256, + } + + def plan(self): + return c27.build_query_plan( + ATOMIC_CLAIM_ID, + self.binding, + self.scope, + {"element": ["E-1"]}, + run_binding_digest=RUN_DIGEST, + effective_on="2026-08-31", + ) + + def hit(self) -> dict: + metadata = { + "schema_version": "stage2_requirement_fact_chunk_metadata.v1", + "document_id": "DOC-1", + "document_sha256": "f" * 64, + "chunk_id": "CH-1", + "chunk_sha256": "0" * 64, + "source_path": "CT-001/source.md", + "source_locator": "section-1", + "case_type_ids": ["CT-001"], + "sub_rule_ids": ["SR-1"], + "rule_branch_keys": ["CT-001::SR-1"], + "corpus_scope_ids": ["CS-1"], + "element_set_ids": ["ES-1"], + "corpus_element_ids": ["E-1"], + "doctrine_roles": ["element"], + "jurisdiction": "KR", + "effective_from": None, + "effective_to": None, + "authority_ids": ["AUTH-1"], + "proposition_ids": ["PROP-1"], + "approval_status": "APPROVED", + "legal_reviewer_receipt_ref": "legal_review/corpus.json", + "legal_reviewer_receipt_sha256": "9" * 64, + } + return { + "chunk_id": "CH-1", + "chunk_sha256": "0" * 64, + "source_document_id": "DOC-1", + "source_document_sha256": "f" * 64, + "source_locator": "section-1", + "pre_rerank_rank": 1, + "hybrid_score": 0.9, + "post_rerank_rank": 1, + "reranker_score": 0.95, + "metadata": metadata, + } + + def test_query_is_canonical_and_bound_to_exact_case_and_rule(self) -> None: + plan = self.plan() + self.assertEqual(plan["schema_version"], "stage2_requirement_fact_query_plan.v1") + self.assertEqual(set(plan), {"schema_version", "run_binding_digest", "atomic_claim_id", "rows", "query_plan_sha256"}) + row = plan["rows"][0] + self.assertEqual(row["filter"]["case_type_id"], "CT-001") + self.assertEqual(row["filter"]["sub_rule_id"], "SR-1") + self.assertEqual(row["filter"]["rule_branch_key"], "CT-001::SR-1") + + def test_unresolved_binding_produces_no_fabricated_query_row(self) -> None: + plan = c27.build_query_plan( + ATOMIC_CLAIM_ID, + {}, + {}, + {}, + run_binding_digest=RUN_DIGEST, + effective_on="2026-08-31", + ) + self.assertEqual(plan["rows"], []) + + def test_retrieval_emits_canonical_result_and_receipts_without_fallback(self) -> None: + plan = self.plan() + + def fake(request): + hits = [self.hit()] if request["filter"]["doctrine_role"] == "element" else [] + return { + "schema_version": "stage2_weaviate_hybrid_search_response.v1", + "request_id": request["request_id"], + "request_sha256": c28._digest(request), + "response_status": "COMPLETE" if hits else "NO_HIT", + "raw_hit_count": len(hits), + "selected_hits": hits, + "dropped_hits": [], + "deterministic_order_digest": c28._digest(hits) if hits else None, + "retry_count": 0, + } + + result, receipts = c28.retrieve_exact( + plan, + self.collection, + fake, + credential_ref="secret://weaviate/read-only", + ) + self.assertEqual(result["schema_version"], "stage2_requirement_fact_retrieval_result.v1") + self.assertEqual(result["result_status"], "COMPLETE") + self.assertEqual(len(result["selected_hits"]), 1) + self.assertEqual(len(receipts), 5) + self.assertTrue(all(row["credential_material_persisted"] is False for row in receipts)) + + pack = c29.verify_pack( + self.binding, + result, + { + "snapshot_id": "SNAP-1", + "snapshot_sha256": SNAPSHOT_SHA256, + "corpus_release_sha256": "8" * 64, + }, + [{"corpus_element_id": "E-1", "doctrine_role": "element", "stage1_slot_refs": ["SL-1"], "mapping_status": "EXACT"}], + claim_group_id=CLAIM_GROUP_ID, + retrieval_receipt_refs=[ + {"path": "receipt.json", "sha256": receipts[0]["receipt_sha256"], "json_pointer": ""} + ], + ) + self.assertEqual(pack["schema_version"], "stage2_requirement_fact_pack.v1") + self.assertEqual(pack["authority_class"], "CAUSE_AND_ELEMENT_CHECK") + self.assertFalse(pack["items"][0]["fact_creation_allowed"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/validators/actio_value_compensation_invariants.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/validators/actio_value_compensation_invariants.yml new file mode 100644 index 00000000..4719f4dc --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/validators/actio_value_compensation_invariants.yml @@ -0,0 +1,32 @@ +{ + "schema_version": "stage2.s2_20.actio_value_compensation_invariants.v1", + "validator_id": "S2-20-ACTIO-VALUE-COMPENSATION-INVARIANTS", + "status": "PENDING_LEGAL_CONTENT", + "execution_eligible": false, + "invariant_rows": [], + "required_structural_dimensions": { + "temporal_intervals": 2, + "liable_party_roles": [ + "BENEFICIARY", + "SUBSEQUENT_TRANSFEREE" + ], + "restoration_recipient_role_required": true, + "cap_operand_roles": [ + "PRESERVED_CLAIM", + "COMMON_SECURITY_SHORTFALL", + "BENEFIT_VALUE" + ], + "cap_count": 3 + }, + "closure": { + "all_caps_require_approved_formula_and_operands": true, + "missing_operand_default_zero_forbidden": true, + "recipient_inference_forbidden": true, + "zero_match_issue_code": "ACTIO_VALUE_INVARIANT_ZERO_MATCH", + "multi_match_issue_code": "ACTIO_VALUE_INVARIANT_MULTI_MATCH" + }, + "review": { + "required_role": "KOREAN_LAWYER", + "status": "PENDING_KOREAN_LAWYER_REVIEW" + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/workflows/S2_20_canonical_relief_plan_reduce.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/workflows/S2_20_canonical_relief_plan_reduce.yml new file mode 100644 index 00000000..742c7a32 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/workflows/S2_20_canonical_relief_plan_reduce.yml @@ -0,0 +1,52 @@ +schema_version: stage2_workflow_contract.v1 +workflow_id: S2_20 +execution_class: NON-LLM-DETERMINISTIC +agent_task_count: 1 +agent_task_name: Task_S2_20_deterministic_relief_plan +agent_task_contract: + mcp: code-executor + tool_name: run_code + language: python + requirements: httpx==0.28.1 + network: agent-network + timeout_seconds: 300 +internal_component_dag: + expression: "(C20 || C21) -> C25 -> C26 -> (C22 || (C27 -> C28 -> C29)) -> C30 -> C35" + nodes: + - {component_id: C20, role: option_conservation_and_group_seed} + - {component_id: C21, role: party_title_facts_projection} + - {component_id: C25, role: exact_case_type_binding} + - {component_id: C26, role: exact_rule_renderer_party_corpus_binding} + - {component_id: C22, role: deterministic_calculation} + - {component_id: C27, role: role_aware_query_plan} + - {component_id: C28, role: exact_filtered_read_only_retrieval} + - {component_id: C29, role: provenance_and_slot_crosswalk_verification} + - {component_id: C30, role: completeness_calculation_filing_exhibit_reports} + - {component_id: C35, role: frozen_relief_plan_and_group_slices} +input_contract: + request_path: stage2_control/s2_20_request.json + request_schema_ref: schemas/deployment.schema.json#/$defs/s2_20_request + upstream_barriers: + - ingress/ingress_status.json + - map_s2_10/s2_10_publish_status.json + discovery_policy: EXACT_RELEASE_AND_BARRIER_ENUMERATION_ONLY +output_contract: + root_rule: stage2_runs/by-binding//plan/ + barrier_path: plan_publish_status.json + barrier_schema_ref: schemas/relief_plan.schema.json#/$defs/plan_publish_status + diagnostic_path: technical_diagnostic.json + diagnostic_schema_ref: schemas/relief_plan.schema.json#/$defs/technical_diagnostic + write_policy: IMMUTABLE_NON_BARRIER_THEN_STATUS_LAST +routes: + - {plan_status: PLAN_COMPLETE, route: TO_S2_30, consumer_authorized: true} + - {plan_status: PLAN_COMPLETE_WITH_REVIEW, route: TO_S2_30, consumer_authorized: true} + - {plan_status: TECHNICAL_INCOMPLETE, route: STOP_TECHNICAL_INCOMPLETE, consumer_authorized: false} +guards: + llm_task_count: 0 + direct_weaviate_agent_task_count: 0 + external_python_runtime_import_count: 0 + legacy_stage2_v0_v3_runtime_dependency_count: 0 + fuzzy_case_type_fallback_allowed: false + retrieval_filter_relaxation_allowed: false + status_written_last: true +implementation_status: IMPLEMENTED_DECLARATIVE_CONTRACT_LIVE_ADMISSION_PENDING diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/MEMORY.md b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/MEMORY.md index fb441b7b..56108673 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/MEMORY.md +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/MEMORY.md @@ -16,6 +16,18 @@ - 하위 에이전트(subagents)를 사용하여 핵심 테마와 교훈을 식별하고 MEMORY.md에 섹션으로 저장하라. - 향후 세션 시작 시 MEMORY.md의 이전 섹션을 참조하라. +## 2026-08-31 — S2_20 deterministic ReliefPlan package offline 구현 + +한 줄 요약: v5-1의 S2_20을 단일 MCP Code Executor inline Python task로 구현하고 C20∼C35·exact case/rule binding·계산·제한 retrieval·barrier-last publish를 115-path parent allowlist와 비순환 release chain에 결속했으며, offline 검증은 통과했지만 live·법률 admission은 의도적으로 대기 상태로 남겼다. + +- 핵심 산출물: `S2_20_SOW.md` 993행/SHA-256 `824dcc375579a358db4a69ffdce9eb54bbd6f42eca2ea54fd6ff9ea1ca9380b0`, `S2_20_assets.md` 468행/`8100ef8ac01cdd3971fb0716c41073af7c1a8f193909bfef02e4217100f4578b`, authoring `Stage_2_S2_20.yml` 3,975행/`4d7642bb8b63dc37fc0ca7808676c816ba43623384b0f745f9f4af17f0f8553b`이다. version-free projection·runtime mirror·schema·registry·renderer·builder·fixture·test·receipt는 `Default_Agent/Stage_2_Clean/` 아래에 배포했다. +- 실행 계약: Agent는 exactly-one stage/exactly-one `mcp: code-executor` `run_code` task만 두고 `httpx==0.28.1`, `agent-network`, 300초를 사용한다. authoring YAML의 `parameters.code` 바이트가 runtime 정본이고 `runtime/s2_20_reduce.py/.txt`는 byte-identical mirror다. 내부 순서는 `(C20 || C21) -> C25 -> C26 -> (C22 || (C27 -> C28 -> C29)) -> C30 -> C35`이며 LLM task·외부 Python import·fuzzy case-type/corpus fallback은 0이다. +- 신뢰·출력 경계: host CAS와 `manifest/stage2_deterministic_admission_receipt.json`을 첫 plan write 전에 검증하고, dynamic legal assets는 module-manifest membership을 요구한다. 모든 partial artifact row를 diagnostic에 보존하며 `plan_publish_status.json`을 마지막 barrier로 쓴다. admission signature material은 backend-capability receipt hash까지 포함한다. +- release 재봉인: module manifest raw/digest는 `fe406f0392b34d89b3070bae637ecda67f0572ffb59d0d86d30d2ca843c7cebe` / `27b8b3203965f70f1b139360e5d8d0e6c7872a3246f5974ad0d9f6b605225407`, parent raw/digest는 `70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92` / `0fec6e67baedd7dcc36591348facd769a9c5a8c8e7052bbc395bdb6d2b96f6bc`, shared Code Executor binding raw hash는 `d1e16314e81faea5e6a8dd826e81b9ae01e0536923480ba4fb710a4959385a7c`다. parent는 module manifest만 hash-bound하고 executor binding을 unhashed external trust-root로 지시하며, detached admission receipt가 parent+binding+S2_00/S2_20 receipt를 결속하여 순환을 피한다. +- 검증 이력: 요청된 문서 2회, 자산 2회, YAML 2회 독립 검증·증분개정을 정확히 종료했다. 최종 `tests/s2_00` 70/70, `tests/s2_10` 39/39, `tests/s2_20` 41/41, `.py/.txt` 42/42 parity, S2_00/S2_20 projection, S2_10 child, parent reproducibility·실파일 hash·비순환 semantic closure가 모두 PASS했다. 구 Stage 2 v.0∼v.3 runtime dependency는 0이다. +- 정직한 한계: 구조적 case-type catalog/coverage는 137행이지만 법률가 승인 `rules/relief/*.md`는 0/137, executable rule branch는 0개다. 계산 descriptor는 17개이나 admitted formula/law-value와 상류 typed operand ledger가 없어 `CALCULATED` E2E는 아직 불가하고 `MISSING_OPERAND`를 안전 보존한다. 상류에 기존 증거표목도 없어 신규 stable label만 검증했다. +- 상태 경계: `IMPLEMENTED_OFFLINE_VERIFIED / LIVE_AND_LEGAL_ADMISSION_PENDING`이다. `active_runtime_authority:false`, detached admission `PENDING`, runtime image·Weaviate collection/tenant·credential/egress·host CAS/backend signature는 미결속이다. live Code Executor/Weaviate, Stage 1→S2_00→S2_10→S2_20 E2E, authority/corpus/law-value admission, 대한민국 변호사 검수·production promotion을 수행하거나 증명하지 않았다. + ## 2026-08-31 — S2_10 추적 metadata 제거 및 S2_10→S2_00 hash 재봉인 한 줄 요약: S2_10 authoring YAML에서 runtime 비필수 문서추적 metadata 3개만 제거하고, S2_10의 10개 파생물과 S2_00의 6개 인계 파생물을 전부 재투영·재봉인했으며, 독립 검증에서 발견된 parent release 중복 BOUND hash drift까지 교정하여 전체 offline hash chain을 닫았다. diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/S2_20_SOW.md b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/S2_20_SOW.md new file mode 100644 index 00000000..f3da3f8d --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/S2_20_SOW.md @@ -0,0 +1,993 @@ +# S2_20 YAML·assets·sources 생성 작업명세서 + +> 기준 전략: `stage_2_optimal_update_strategy_v.5-1.md` +> +> 대상 단계: `S2_20 — deterministic ReliefPlan reduce / case-type binding / calculation / requirement-fact retrieval` +> +> canonical 배포 root: `Default_Agent/Stage_2_Clean/` +> +> 문서 상태: `IMPLEMENTED_OFFLINE_VERIFIED` +> +> 법률·운영 상태: `OFFLINE_IMPLEMENTATION_ALLOWED / LIVE_CODE_EXECUTOR_WEAVIATE_AND_LEGAL_ADMISSION_PENDING` + +--- + +### 구현 상태 스냅샷 + +본 SOW의 Agent·inline Python·projection·schema·registry·builder·fixture·test와 non-cyclic parent/child/binding/receipt 봉인은 구현되어 offline 검증을 통과했다. 다만 137개 catalog row는 구조적 registry일 뿐이며, 승인된 `rules/relief/*.md`와 실행 가능한 rule branch는 현재 각각 0개다. authority/corpus/law-value는 승인 전 `PENDING` 또는 `DEV_UNAVAILABLE`이고, live Code Executor·Weaviate·host CAS·전체 E2E·대한민국 변호사 승인은 수행되지 않았다. + +현행 Stage 1/S2_00 handoff에는 기존 갑호증 label 필드와 계산용 typed operand value ledger가 없다. 따라서 현 구현은 stable evidence 순서로 **신규 label만 결정적으로 부여**하며 기존 label 보존은 아직 실증할 수 없다. 또한 calculator는 누락 operand를 `MISSING_OPERAND`로 보존하지만, 상류 계약이 보강되기 전에는 `CALCULATED` 상태의 전체 E2E 도달을 주장하지 않는다. + +--- + +## 0. 집행 결론 + +S2_20은 S2_00의 검증·보존된 context와 S2_10의 canonical immutable legal-judgment parts를 직접 소비하여 claim option을 무손실 reduce하고, 당사자·권원·책임형태를 검사하며, atomic claim별 exact `case_type_id`·`sub_rule_id`·renderer·계산 branch를 결속한다. 이어 고정 calculator와 release-bound Weaviate 검색을 실행하여 claim group별 rule/corpus/calculation context를 만들고, **frozen ReliefPlan과 S2_30 group slice를 확정하는 단일 NON-LLM-DETERMINISTIC stage**다. + +S2_20 Agent YAML은 정확히 1개의 `mcp: code-executor`, `tool_name: run_code` task만 가진다. C20·C21·C25·C26·C22·C27·C28·C29·C30·C35는 별도 AgentBackend task가 아니라 그 1개 task의 완전한 static inline Python 안에서 실행되는 named pure component다. 별도 LLM task, direct Weaviate top-level task, map/reduce task, 외부 runtime `.py` import 또는 사건별 동적 code 생성은 금지한다. + +S2_20의 내부 DAG는 다음과 같이 고정한다. + +```text +S2_10 COMPLETE/TO_S2_20 barrier + exact immutable inputs + | + v + release/hash/schema preflight + | + +---------+---------+ + | | + v v + C20 option portfolio C21 party/title/ + conservation·relation liability·instruction + | | + +---------+---------+ + v + C25 exact case_type predicate binding + | + v + C26 exact sub_rule/renderer/CE-13 binding + | + +---------+------------------+ + | | + v v + C22 CE-01..CE-R4 C27 role-aware query + deterministic calculators | + v + C28 filtered Weaviate + | + v + C29 approved pack verify + | | + +-------------+--------------+ + v + C30 exhibit/filing/completeness + | + v + C35 frozen ReliefPlan/groups/slices + | + v + artifact write → read-back → plan barrier last +``` + +이 DAG는 Python 내부에서만 병렬화한다. C20과 C21은 같은 immutable input snapshot을 독립적으로 읽고 join하며, C22와 `C27→C28→C29` branch는 C26 완료 후 병렬 실행한다. 병렬 완료 순서는 output bytes에 영향을 주지 않아야 한다. 모든 join은 branch ID 순서, 모든 set-like collection은 schema가 지정한 stable key 순서로 canonicalize한다. + +### 0.1 Stage 2 전체 헌법과의 정합 + +```text +Stage 2 전체 = deterministic inline-Python 3개(S2_00, S2_20, S2_40) + + LLM-DIRECT 2개(S2_10, S2_30) +``` + +S2_20은 S2_10의 법률판단을 새로 수행하거나 수정하지 않는다. 법률상 `CONDITIONAL`·`UNRESOLVED`, binding gap, 증거·calculation operand 부족은 삭제하거나 전역 실패로 바꾸지 않고 review 가능한 plan에 보존한다. 반면 input/release/hash/schema 불일치, immutable output conflict, canonical serialization 불능처럼 일관된 plan artifact set을 만들 수 없는 경우에만 `TECHNICAL_INCOMPLETE`로 정지한다. + +### 0.2 우선계약 + +1. authoring `Stage_2_S2_20.yml`의 `parameters.code`가 실행 code의 유일한 편집 정본이다. +2. 배포 Agent, `runtime/s2_20_reduce.py/.txt`와 inline-code receipt는 authoring YAML에서 기계적으로 투영한다. +3. S2_20 runtime은 외부 `.py`, shell, subprocess, `exec`, `eval`, 동적 import 또는 구 Stage 2 v.0~v.3 자산을 읽지 않는다. +4. runtime branch는 release-bound YAML/JSON data contract와 사건 input에 의해서만 선택된다. 사건 결과를 본 뒤 code를 생성·수정하지 않는다. +5. `plan/plan_publish_status.json`은 S2_20의 유일한 downstream 소비 허가 barrier이며 모든 비-barrier artifact를 기록·read-back 검증한 뒤 마지막에 쓴다. +6. S2_20은 `plan/*`와 `issue_ledger.plan.json`의 single writer다. S2_10/S2_30/S2_40은 이를 덮어쓰거나 append하지 않는다. +7. Weaviate는 exact release-selected endpoint·collection·tenant·snapshot·filter·read-only operation만 사용한다. nearest case type, filter relaxation, 인접 corpus, raw web 또는 unapproved document fallback은 0이다. +8. S2_20은 `READY`, 최종 법률 readiness, final package, `control/run_status.json`, commit intent/result를 쓰지 않는다. + +--- + +## 1. 절대 범위와 비범위 + +### 1.1 S2_20이 수행한다 + +1. S2_10 canonical option의 exact conservation과 disposition partition을 검사한다. +2. dependency·precondition·accessory·incompatibility·same-underlying-recovery relation을 정규화한다. +3. 법률상 가능한 primary·alternative·cumulative·accessory·precondition portfolio를 보존한다. +4. 당사자 집합, 원고 권원, 피고별 책임형태, 목적물 specification, client instruction을 검사한다. +5. S2_10의 normalized claim signature를 closed predicate grammar로 exact `case_type_id`에 결속한다. +6. exact `sub_rule_id`, renderer, structured relief rule, Markdown source hash, required slot, corpus scope, CE-13 branch와 review policy를 결속한다. +7. CE-01~CE-13 및 CE-R1~CE-R4의 release-bound deterministic calculator를 실행하고 receipt를 작성한다. +8. role-aware requirement-fact query를 만들고 exact filtered Weaviate hybrid retrieval을 수행한다. +9. corpus source/hash/snapshot/approval/authority/locator를 검증하고 Stage 1 slot crosswalk와 대조한다. +10. exhibit register, party completeness, filing metrics와 option conservation report를 만든다. +11. stable `atomic_claim_id`와 `claim_group_id`를 결정적으로 발급하고 frozen ReliefPlan과 group slice를 만든다. +12. S2_30이 소비할 `P31_rule_and_pack`, `P32_common_authority`, frozen group slice를 결속한다. +13. plan artifact set을 canonical bytes로 쓰고 read-back한 뒤 status-last barrier를 발행한다. + +### 1.2 S2_20이 수행하지 않는다 + +- 새 청구권·항변·재항변의 실체법적 성립 판단 +- S2_10의 `SUPPORTED|CONDITIONAL|UNRESOLVED|EXCLUDED` decision 수정 +- 승소확률, 추정 금액·날짜·이율·시가 또는 존재하지 않는 fact/evidence 생성 +- 사건명·semantic similarity·directory filename으로 청구권 또는 case type 결정 +- zero/multi case-type 또는 sub-rule match를 복수 청구로 자동 증식 +- 청구취지·청구원인 자연어 drafting 또는 final rendering +- LLM 호출, prompt 작성, prompt cache 소유 또는 S2_30 model 설정 +- raw corpus 전체, 137개 Markdown 전체 또는 관련 없는 authority 전체를 사건 context에 삽입 +- S2_10 immutable part, S2_00 context 또는 Stage 1 source artifact 수정 +- 최종 review request/receipt, filing readiness, seal 또는 commit 작성 +- 구 Stage 2 `v.0~v.3` YAML·prompt·loader·중간파일 runtime load + +--- + +## 2. 경로·실행·신뢰경계 + +```text +M = Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/ +R = Default_Agent/Stage_2_Clean/ +Q = stage2_control/s2_20_request.json +O = stage2_runs/by-binding// +T = Code Executor 실행별 isolated temporary filesystem +``` + +`R`은 version-free canonical 배포 root다. version은 folder suffix가 아니라 schema/asset version, raw SHA-256, effective interval, manifest dependency와 release/admission receipt로 관리한다. + +`Q`는 outer orchestrator가 쓰는 closed runtime request다. caller의 `request_id`는 추적용일 뿐 canonical output root 선택권이 없다. canonical `run_id`와 `O`는 S2_00에서 정한 `run_binding_digest`를 그대로 사용하며 S2_20이 새 binding을 만들지 않는다. + +### 2.1 authoring·배포 자산 계약 + +| logical asset | canonical path | 책임 | +|---|---|---| +| authoring Agent | `M/Stage_2_S2_20.yml` | exactly-one inline `run_code` task의 유일 편집 정본 | +| deployment Agent | `R/agent_scripts/Stage_2_S2_20.yml` | authoring projection; canary/production 실행본 | +| declarative workflow | `R/workflows/S2_20_canonical_relief_plan_reduce.yml` | IO·component DAG·route·barrier·금지규칙 선언; 실행 code 없음 | +| full-code mirror | `R/runtime/s2_20_reduce.py/.txt` | YAML `parameters.code` 전체의 byte-identical offline test/parity oracle; runtime import 0 | +| pure-core oracle | `R/runtime/binding_signature_projection.py/.txt` | S2_10 source→C25 target signature projection 시험용; runtime에는 inline 완전 전개 | +| pure-core oracle | `R/runtime/c25_case_type_bind.py/.txt` | exact case-type predicate binding까지만 시험; runtime에는 inline 완전 전개 | +| pure-core oracle | `R/runtime/c26_rule_branch_bind.py/.txt` | sub-rule·renderer·party-set·corpus·CE-13 FK 시험용; runtime에는 inline 완전 전개 | +| pure-core oracle | `R/runtime/c27_query_plan.py/.txt` | query-plan 시험용; runtime에는 inline 완전 전개 | +| pure-core oracle | `R/runtime/c28_weaviate_retrieve.py/.txt` | retrieval transport/ordering 시험용; runtime에는 inline 완전 전개 | +| pure-core oracle | `R/runtime/c29_pack_verify.py/.txt` | pack provenance/crosswalk 시험용; runtime에는 inline 완전 전개 | +| pure-core oracle | `R/runtime/calculators/registry_engine.py/.txt` | formula data interpreter 시험용; runtime에는 inline 완전 전개 | +| pure-core oracle | `R/runtime/authority/authority_preflight.py/.txt` | sealed authority/law-value temporal dependency 시험용; runtime에는 inline 완전 전개 | +| projection builder | `R/offline_build/build_s2_20_inline_projection.py/.txt` | authoring→deployment/full mirror/receipt 투영·check; 사건 runtime 실행 0 | +| executor binding | `R/deployment/stage2_code_executor_binding.yml`의 `stage2_code_executor_binding.v3/stage_bindings[stage_id=S2_20]` row | 기존 S2_00 row를 보존하는 multi-stage Agent/code/release/MCP/egress/timeout/secret binding | +| inline receipt | `R/manifest/s2_20_inline_code_receipt.json` | authoring/deployment/code/mirror/hash/task semantics 결속 | +| detached admission | `R/manifest/stage2_deterministic_admission_receipt.json` | parent와 present deterministic stages를 외부 서명 계층에서 결속; live evidence 전 PENDING | +| parent member allowlist | `R/manifest/s2_20_parent_member_paths.json` | parent raw closure에 편입할 S2_20 고정자산의 명시적 경로 집합; downstream-of-parent 자산 제외 | +| per-run outer receipt | `O/control/s2_20_outer_execution_receipt.json` | AgentBackend가 host CAS·actual Agent/code/binding·MCP outer result·workspace isolation을 기록; inline writer 아님 | + +위 경로는 이후 asset inventory와 구현 단계의 목표다. 이 SOW를 작성했다는 사실만으로 파일이 생성·검증·배포되었다고 간주하지 않는다. + +### 2.2 direct MCP Code Executor contract + +AgentBackend-level 실행은 정확히 다음 1개 task다. + +```yaml +tasks: + - task_name: Task_S2_20_deterministic_relief_plan + mcp: code-executor + tool_name: run_code + parameters: + language: python + requirements: "httpx==0.28.1" + network: "agent-network" + timeout: 300 + code: |- + # authoring deliverable에는 생략 없는 전체 static Python을 기입한다. +``` + +위 snippet은 SOW의 shape 설명이다. 실제 `Stage_2_S2_20.yml`에서 placeholder·생략 주석·외부 code import는 허용하지 않는다. 300초는 release-bound 초기값이며 representative live benchmark를 통과하기 전에는 완료 보장이 아니다. Python runtime image digest를 backend가 pin하지 못하면 live admission blocker다. + +`test_code_executor.ipynb`가 실증한 MCP 기본 순서인 JSON-RPC `initialize` → response `mcp-session-id` 보존 → `notifications/initialized` → `tools/call`을 따른다. production inline code는 notebook 예시보다 엄격하게 matching response ID, JSON-RPC `error`, MCP `isError`, content cardinality, SSE/JSON terminal response와 timeout을 검증한다. notebook의 bearer 값은 복사하지 않는다. + +### 2.3 localdocs·Weaviate network boundary + +- localdocs endpoint는 `http://mcp-localdocs:8012/mcp` 하나다. +- localdocs initialize `clientInfo`에는 `user_id={{__user_hash__}}`, `workspace_id={{__workspace_hash__}}`를 반드시 넣는다. +- localdocs runtime tool allowlist는 `read_binary_doc`, `write_binary_file`다. raw hash source로 `read_docs`를 쓰지 않는다. +- Weaviate transport는 MCP-over-HTTP JSON-RPC로 고정한다. endpoint는 `https://weaviate.eroomai.com/mcp`, initialize `protocolVersion`은 `2025-03-26`, 허용 tool은 read-only `search_hybrid` 하나다. collection·tenant·filter·`credential_ref`는 `corpus_release.json`, `requirement_fact_scope.yml`, executor binding이 exact equality로 결속한다. 이 값의 명시는 live endpoint/tool 검증 완료를 뜻하지 않는다. +- Weaviate operation은 read-only filtered hybrid search만 허용한다. schema update, object write/delete, broad collection scan은 금지한다. +- credential은 AgentBackend secret injection 또는 사전등록 MCP config로만 제공한다. plaintext secret가 YAML/code/receipt에 있으면 release failure다. +- backend-enforced egress profile은 localdocs와 exact Weaviate host/port/operation 외 outbound를 차단해야 한다. code 내부 allowlist만으로 egress 보안을 증명하지 않는다. + +### 2.4 single-flight와 single writer + +S2_20의 `single writer`는 JSON lock 파일만으로 증명하지 않는다. outer orchestrator 또는 AgentBackend는 동일 `run_binding_digest + stage_id=S2_20`에 대해 host atomic CAS/mutex를 획득한 뒤 Agent를 호출해야 한다. capability가 실증되지 않으면 offline fixture 실행은 가능하지만 live admission은 `PENDING_HOST_CAS_BINDING`이다. + +S2_20 task는 `plan/*`의 유일 writer다. 같은 binding의 기존 path가 byte-identical하면 idempotent success, 다르면 `IMMUTABLE_OUTPUT_CONFLICT`로 중단한다. 다른 binding은 다른 `O` root를 쓴다. 공용 JSONL append, last-write-wins overwrite와 timestamp 기반 ID는 금지한다. + +--- + +## 3. runtime input allowlist와 preflight + +### 3.1 outer request + +`Q`는 다음 field를 **정확히 모두 required**로 가지며 optional/additional field는 0개다. + +```yaml +schema_version: stage2_s2_20_request.v1 +request_id: nonempty-string +attempt_id: nonempty-string +run_binding_digest: 64-lower-hex +user_identity_hash: 64-lower-hex +workspace_identity_hash: 64-lower-hex +stage2_run_root_ref: stage2_runs/by-binding/ +plan_output_root_ref: stage2_runs/by-binding//plan +host_cas_receipt_ref: stage2_control/host_cas//S2_20.json +host_cas_receipt_sha256: 64-lower-hex +detached_admission_receipt_ref: Default_Agent/Stage_2_Clean/manifest/stage2_deterministic_admission_receipt.json +detached_admission_receipt_sha256: 64-lower-hex-or-PENDING_SEQUENTIAL_BIND +outer_execution_receipt_target_ref: stage2_runs/by-binding//control/s2_20_outer_execution_receipt.json +expected_s2_10_publish_status_sha256: 64-lower-hex +expected_parent_stage2_release_sha256: 64-lower-hex +expected_s2_10_release_sha256: 64-lower-hex +expected_s2_20_agent_sha256: 64-lower-hex +expected_s2_20_executor_binding_sha256: 64-lower-hex +``` + +`Q`는 `schemas/deployment.schema.json#/$defs/s2_20_request`의 정확한 instance여야 한다. `user_identity_hash`와 `workspace_identity_hash`는 Agent YAML substitution이 제공한 `{{__user_hash__}}`, `{{__workspace_hash__}}`의 canonical hash와 같아야 한다. host-CAS receipt는 실행 전에 존재해야 하고, detached admission이 아직 PENDING이면 offline/canary route만 허용한다. outer execution receipt는 AgentBackend가 실행 뒤 target path에 쓰므로 inline code가 생성·서명하지 않는다. 위 목록 이외의 임의 경로·endpoint·code는 받을 수 없다. + +`stage2_run_root_ref`는 exact pattern과 digest equality를 검증한다. absolute path, `..`, symlink escape, caller-selected alternative output root, directory scan·glob은 거부한다. + +### 3.2 사건 run input + +S2_20은 `O` 아래 다음 exact family만 읽는다. + +| input | precondition | +|---|---| +| `ingress/ingress_status.json` | status-last barrier 유효, run binding 일치 | +| `context/case_context.json` | exact hash가 ingress barrier에 결속 | +| `context/evidence_inventory.json` | evidence ID/source hash 보존 | +| `context/object_registry.json` | object context/ref lineage 보존 | +| `context/party_and_title_context.json` | party/title occurrence와 unresolved identity 보존 | +| `context/slot_crosswalk.json` | Stage 1 element/defense/rebuttal/evidence slot refs | +| `context/cluster_plan.json` | executable cluster set·SCC·wave identity | +| `context/cluster_slices/.json` | directory scan 없이 barrier/plan이 열거한 exact set | +| `context/bundle_plan.json` | selected legal-context 및 S2_10 Agent/binding handoff hash | +| `review/issue_ledger.base.json` | row ID·provenance 보존; 삭제/덮어쓰기 금지 | +| `map_s2_10/s2_10_publish_status.json` | `COMPLETE/TO_S2_20`이어야 함 | +| `map_s2_10/domain_verdicts/.json` | S2_10 barrier가 열거한 canonical immutable set | +| `map_s2_10/issue_patches/.json` | exact cluster coverage와 hash | +| `map_s2_10/assumption_parts/.json` | assumption 보존 | +| `map_s2_10/item_receipts/.json` | schema/ref/ID/read-back 검증 PASS | +| `map_s2_10/wave_receipts/wave-.json` | 전 wave `WAVE_COMPLETE`; terminal failure 0 | + +usage part는 S2_20 법리·portfolio input이 아니다. 필요하면 hash/coverage 관측용으로만 읽고 plan 내용에 영향을 주지 않는다. + +정상 진입식은 다음 exact set equality로 검사한다. + +```text +S2_00 executable_cluster_ids += S2_10 valid_domain_verdict_cluster_ids += all S2_10 terminal wave receipt covered_cluster_ids + +S2_10 terminal_technical_failure_cluster_ids = ∅ +``` + +누락 verdict, 실패 wave, invalid item receipt 또는 `STOP_TECHNICAL_INCOMPLETE` route가 있으면 S2_20 정상 plan 생성을 시작하지 않는다. + +### 3.3 release-bound data input + +S2_20은 `R` 아래 manifest가 exact path/hash로 열거한 다음 logical family를 사용한다. + +- `manifest/stage2_release.json`, `manifest/module_manifest.json` +- `manifest/s2_10_release.json`, `manifest/authority_release.json`, `manifest/corpus_release.json`, `manifest/case_type_coverage.json` +- `manifest/case_type_registry.yml` +- `manifest/case_type_rule_registry.yml` +- `registry/binding/binding_signature_projection.yml` +- `registry/planning/option_disposition_policy.yml` +- `registry/party/party_set_rules.yml` +- `registry/drafting/case_type_relief_rules.yml` +- `registry/review/review_policy_registry.yml` +- `registry/authority/authority_registry.yml` +- `law_values/general_law_values.yml`, `law_values/court_fee_values.yml` +- `registry/temporal/inheritance_reserved_share.yml` +- `registry/drafting/forbidden_relief_rules.yml`, `counter_performance_rules.yml`, `procedural_declaration_rules.yml` +- `registry/corpus/requirement_fact_scope.yml` +- `routing/actio_pauliana_route_registry.yml`, `routing/actio_pauliana_mortgage_route.yml` +- `validators/actio_value_compensation_invariants.yml` +- `registry/calculations/calculation_activation_registry.yml` +- `registry/calculations/CE-01_interest_delay.yml`부터 `CE-13_court_value_cost.yml`, `CE-R1`부터 `CE-R4`까지 release-selected calculator set +- `renderers/R01_money_payment.yml`부터 `R06_constitutive_judgment_challenge.yml`까지 release-selected renderer contract + +directory 존재나 filename 유사성은 dependency 증거가 아니다. module manifest row의 path/hash/schema/version/status, authority/corpus release와 execution eligibility를 모두 확인한다. 미구현·미검수 asset은 clean plan binding에 사용하지 않고 closed issue로 보존한다. + +### 3.4 preflight 순서 + +1. `Q`의 strict JSON·run binding·path containment 검증 +2. build-time `EXPECTED_STAGE2_RELEASE_SHA256`와 실제 parent release raw bytes 비교 +3. release self-digest, module/authority/corpus/case-type coverage reference와 actual raw hash 검증 +4. deployment Agent/code/receipt/executor-binding outer evidence echo 검증 +5. S2_00 ingress barrier와 S2_10 publish barrier·child release·producer-contract digest 검증 +6. exact input allowlist를 bounded binary read로 hydrate하고 one-read bytes로 hash·UTF-8 strict parse·schema 검증 +7. immutable input snapshot digest를 만든 뒤 execution 직전 second bounded read로 TOCTOU 여부 검증 +8. resource limit, duplicate-key, non-finite number, trailing data, path escape를 검사 +9. 모두 통과한 후에만 C20/C21을 시작 + +같은 workspace의 release JSON이 자기 신뢰를 스스로 증명하는 것으로 보지 않는다. hash-admitted deployment Agent와 AgentBackend outer receipt 또는 detached signed admission이 외부 trust anchor다. + +--- + +## 4. Agent YAML과 `task_procedure` 계약 + +### 4.1 top-level shape + +S2_20 authoring YAML은 다음 의미를 가져야 한다. + +- Stage 1개: `S2_20` +- task 1개: `Task_S2_20_deterministic_relief_plan` +- LLM provider/model/reasoning/verbosity/prompt 0개 +- top-level direct Weaviate task 0개 +- `skip_confirm: true` +- `prevs: []`, `nexts: []` +- tools config는 Code Executor와 필요 transport declaration만 포함하고 plaintext credential 0 + +### 4.2 `task_procedure`의 정확한 의미 + +`task_procedure`는 AgentBackend 실행 DAG를 다음처럼 고정한다. + +```yaml +task_procedure: + IN: + nexts: + - Task_S2_20_deterministic_relief_plan + wait_until: [] + Task_S2_20_deterministic_relief_plan: + nexts: + - OUT + wait_until: + - IN + OUT: + nexts: [] + wait_until: + - Task_S2_20_deterministic_relief_plan +``` + +C20~C35를 `task_procedure`의 별도 task node로 만들지 않는다. 그렇게 하면 deterministic stage의 exactly-one `run_code` 헌법과 충돌한다. 대신 위 유일 task의 description·workflow contract·inline code 내 named function와 internal DAG table이 다음 순서를 명시한다. + +```text +(C20 || C21) → C25 → C26 → (C22 || (C27 → C28 → C29)) → C30 → C35 +``` + +향후 YAML의 `task_procedure` comment에는 이 internal DAG와 exactly-one task 이유를 적을 수 있으나, backend가 해석하지 않는 임의 mapping key를 추가하지 않는다. + +### 4.3 stdout 계약 + +inline code는 내부 stdout/stderr을 격리하고 마지막에 canonical JSON object 하나만 stdout에 쓴다. + +```yaml +schema_version: stage2_s2_20_execution_receipt.v1 +ok: boolean +workflow_id: S2_20 +request_id: string +run_binding_digest: 64-lower-hex +plan_status: PLAN_COMPLETE | PLAN_COMPLETE_WITH_REVIEW | TECHNICAL_INCOMPLETE +route: TO_S2_30 | STOP_TECHNICAL_INCOMPLETE +artifact_set_digest: 64-lower-hex | null +plan_publish_status_path: string +plan_publish_status_sha256: 64-lower-hex | null +error: object | null +``` + +Code Executor outer result와 위 inner stdout JSON을 구분하여 검증한다. outer success만으로 stage success를 선언하지 않는다. + +--- + +## 5. inline Python 공통 구현 계약 + +### 5.1 source of truth와 금지 구현 + +- `parameters.code`는 shebang을 포함한 complete static Python이다. +- `runtime/s2_20_reduce.py/.txt`는 unique-key YAML parser가 반환한 전체 code string을 추가 dedent·개행변환 없이 UTF-8 encode한 byte-identical mirror다. +- C25/C27/C28/C29/calculator pure-core 파일은 offline 시험 source일 수 있으나 build 시 full code에 완전 전개하며 runtime import하지 않는다. +- placeholder, TODO body, `pass`로 대체한 required component, remote code download, package-at-runtime discovery, shell·subprocess, `exec`·`eval`, reflection 기반 dynamic code generation은 금지한다. +- Python standard library와 release-pinned `httpx==0.28.1` 외 dependency를 추가하려면 manifest·image·license·size·canary를 다시 봉인한다. + +### 5.2 strict parsing과 canonical bytes + +1. `read_binary_doc`의 base64는 `validate=True`로 정확히 한 번 decode한다. +2. 동일 decoded bytes로 raw SHA-256, UTF-8 strict decode, duplicate-key rejecting JSON parse와 schema 검사를 수행한다. +3. `NaN`, `Infinity`, duplicate key, trailing non-whitespace data를 거부한다. +4. YAML은 offline/release asset hydration에만 허용하고 duplicate key를 거부하는 parser와 closed schema를 사용한다. +5. output JSON은 object key codepoint sort, compact separator, finite number only, UTF-8, LF 1개로 canonicalize한다. +6. decimal·rate·money는 binary float를 사용하지 않고 integer minor unit 또는 `Decimal`과 explicit rounding rule을 사용한다. +7. upstream ID의 equality를 Unicode normalize·trim·case-fold하여 합치지 않는다. 새 display text와 filename만 NFC 정책을 따른다. +8. output-affecting 숫자는 source operand와 `law_value_id` 또는 calculator formula row를 가져야 한다. + +### 5.3 deterministic concurrency + +Python 내부 병렬화는 최대 두 branch로 제한한다. + +- phase 1: C20과 C21 +- phase 2: C22와 retrieval chain `C27→C28→C29` + +thread/process completion order, network response arrival time와 iteration insertion order를 output에 기록하지 않는다. future 결과는 fixed branch ID로 수집한 뒤 canonical order로 join한다. 시간값은 ID·정렬·내용결정 입력이 아니며 operational receipt에 필요하면 backend-supplied trace로 분리한다. + +### 5.4 canonical ID + +S2_10 permanent `claim_option_id`는 변경하지 않는다. S2_20 신규 ID는 versioned algorithm ID와 전체 minting projection hash를 가진다. + +```text +atomic_claim_id = AC- + +claim_group_id = CG- +``` + +한 option에서 복수 atomic claim을 만드는 것은 raw option 안의 명시된 서로 다른 급부·목적물·상대방·route unit을 무손실 분해할 때만 허용한다. case-type multi-match나 retrieval hit 수는 atomic claim 생성 근거가 아니다. hash collision은 새 suffix를 임의 부여하지 않고 technical failure다. + +--- + +## 6. 내부 component 계약 + +### 6.1 C20 — option conservation·dependency·compatibility + +C20은 S2_10 canonical `claim_options[]` 전체를 다음 exact partition으로 보존한다. + +```text +all_option_ids += selected_option_ids + ⊎ alternative_option_ids + ⊎ excluded_option_ids + ⊎ deferred_option_ids +``` + +partition은 pairwise disjoint하고 union이 exact해야 한다. `EXCLUDED`는 S2_10이 authority·typed provenance로 확정한 법률상 배제만 보존하며, 증거부족·client no-sue·binding gap을 새 `EXCLUDED`로 바꾸지 않는다. client no-sue는 `deferred_option_ids`와 exact instruction ref로 유지한다. + +C20은 다음을 수행한다. + +- option-local relation을 permanent ID graph로 재검증 +- precondition·accessory·alternative·cumulative·incompatibility edge 정규화 +- same-underlying-recovery key와 recovery cap 축 작성 +- 일부청구·주위적/예비적·복수 책임자 option 보존 +- cycle·dangling edge·관계 충돌 issue 작성 +- preliminary atomic claim과 stable claim group 발급 +- lawful frontier의 deterministic display order 작성 + +정렬은 `limitation_urgency`, `decision`, `relation`, stable ID만 사용한다. 승소확률·기대수익률을 창작하지 않는다. 비지배 option을 자동 소거하지 않는다. + +disposition은 `registry/planning/option_disposition_policy.yml`의 exhaustive decision table만으로 결정한다. table은 S2_10 legal decision, client pursue/no-sue instruction, precondition·compatibility 상태의 precedence를 closed enum으로 정의하고 모든 조합을 exactly-one result 또는 explicit `DISPOSITION_POLICY_GAP/CONFLICT`로 보낸다. Python `if`의 숨은 default나 순회 순서로 disposition을 정하지 않는다. + +### 6.2 C21 — party·title·liability·client instruction + +C21은 S2_00의 party/title/object context와 S2_10 option을 독립적으로 join하여 **법률규칙 적용 전 사실 projection**을 만든다. + +- 원고별 right-holder/title-chain ref +- 피고별 obligor/recipient/procedural-counterparty ref +- 공동·분할·연대·보증·대위 등 관찰된 책임형태 token·source ref +- 현재 포함된 party set과 불명확한 identity/title gap +- 목적물 identity/specification completeness +- client pursue/no-sue/priority instruction과 legal decision의 분리 + +이름 유사성이나 occurrence context ID만으로 실체 동일성을 확정하지 않는다. 확인되지 않은 동일성은 `IDENTITY_UNRESOLVED`와 review issue로 보존한다. C21은 필수당사자·고유필수적 공동소송 여부나 final party caption을 확정하지 않는다. 법률상 party-set rule은 C26에서 exact `party_set_rule_id`로 결속하고 C30에서 required/present set을 비교한다. + +### 6.3 C25 — exact `case_type_id` binding + +C25의 operand는 S2_10 normalized claim signature와 C20/C21의 deterministic projection을 `registry/binding/binding_signature_projection.yml`로 변환한 target signature뿐이다. 허용 predicate operator는 `EQ`, `IN`, `ALL_OF`, `NONE_OF`, `EXISTS`다. arbitrary expression, regex on free text, embedding, fuzzy label 또는 catch-all fallback은 금지한다. + +S2_10 source signature의 정본 field는 `legal_basis_family`, `right_holder_refs`, `obligor_refs`, `performance_kind`, `object_refs`, `legal_effect`, `source_occurrence_refs`다. projection registry는 이 7개 field와 C20 remedy/dependency 및 C21 party/object projection에서 `claim_family`, `legal_effect`, `performance_kind`, `object_kind`, `party_role_pattern`, `source_obligation_kind`, `remedy_mode`, `registry_action`, `procedural_posture`, `special_statute_tags`를 도출한다. 각 target field는 `projection_rule_id`와 source ref를 가지며 미도출값은 오직 `UNKNOWN`이다. source field를 이름만 바꾸거나 free text로 보충해서는 안 된다. + +각 atomic claim은 정확히 다음 중 하나가 된다. + +- exact one match: `BOUND` +- zero match: `UNRESOLVED` + `CASE_TYPE_BINDING_ZERO_MATCH` +- multi match: `CONFLICT` + `CASE_TYPE_BINDING_MULTI_MATCH` + +`DOCTRINE_TOPIC`과 `GROUP` row는 신규 청구의 match가 될 수 없다. `GENERIC` row는 대한민국 변호사가 승인한 closed predicate와 `claim_capable_disposition=CLAIM_CAPABLE`이 모두 있을 때만 허용한다. zero/multi match가 기존 atomic claim을 삭제하거나 추가 claim을 생성하게 해서는 안 된다. + +### 6.4 C26 — exact rule branch·renderer·CE-13 binding + +C26은 C25 `BOUND` atomic claim에 대해 다음 chain을 exact one-match로 결속한다. + +```text +normalized signature +→ binding-signature projection rule ID +→ matched case-type predicate ID +→ exact case_type_id +→ matched sub-rule predicate ID +→ exact sub_rule_id +→ rule_branch_key = case_type_id::sub_rule_id +→ structured relief rule + Markdown source path/hash +→ allowed renderer + required slots +→ corpus_scope_id + element_set_id +→ CE-13 branch + review policy +→ exact party_set_rule_id +``` + +zero/multi/missing/hash mismatch는 closed issue code로 남기며 nearest rule, generic wording 또는 임의 renderer로 대체하지 않는다. 미결 binding group은 `REVIEW_DRAFT` 또는 `WORKNOTE_ONLY`로 보존할 수 있으나 clean relief permission을 주지 않는다. + +Markdown은 `RELIEF_ONLY` 설명 source이고 structured row가 executable contract다. Markdown·rule·renderer는 S2_10 legal decision을 바꾸거나 Stage 1 사실을 생성하지 못한다. + +`party_set_rule_id`는 `registry/party/party_set_rules.yml`에서 exact one-match로 선택한다. C26은 필요한 party role·liability topology만 결속하고, 실제 required/present party set 비교와 누락 issue 발급은 C30이 수행한다. zero/multi party-set rule도 clean drafting permission을 주지 않는다. + +### 6.5 C22 — deterministic calculation + +C22는 release-selected formula data와 law value만 해석한다. 최소 필수 boundary test 대상은 다음이다. + +- CE-01: obligation별 원금·이자·지연손해금 timeline +- CE-03: 소멸시효·제척기간·출소기간·urgency +- CE-08: 상속개시일별 유류분 remedy/가액/이자 temporal branch +- CE-10: 사해행위 피보전채권·공동담보·수익자/전득자 이익 3-cap과 route +- CE-13: portfolio·병합·목적물가액·부대청구별 소가·인지·송달비용 + +나머지 CE-02, 04~07, 09, 11, 12와 CE-R1~R4도 `registry/calculations/calculation_activation_registry.yml`의 closed activation predicate가 match할 때 같은 registry engine으로 실행한다. activation은 stable claim signature·case/rule branch·requested metric만 사용하고 각 CE에 대해 `ACTIVATED | NOT_APPLICABLE | ACTIVATION_GAP | ACTIVATION_CONFLICT`를 정확히 하나 기록한다. `ACTIVATED`된 CE마다 별도 `calculation_receipts/.json`을 쓰며 formula ID, operand refs, units, law-value ID/effective interval, rounding, period rows, missing operand와 result hash를 기록한다. + +전역 최고이율식, LLM 산술, authority 없는 literal 법정수치, missing operand의 0 대입은 금지한다. 계산 불능은 option 폐기 사유가 아니라 review/filing-decision restriction이다. + +### 6.6 C27 — role-aware query plan + +각 bound atomic claim에 대해 `element | defense | rebuttal | burden | relief_consistency` role별 query row를 만든다. query는 다음을 봉인한다. + +- `atomic_claim_id`, `case_type_id`, `sub_rule_id` +- `corpus_scope_id`, `element_set_id`, doctrine role +- approved-status, jurisdiction, temporal scope와 snapshot filter +- canonical query text/data projection과 query hash +- hybrid alpha/top-k/embedding/reranker/index parameter hash +- expected corpus element IDs 또는 explicit gap policy + +query text는 사건 사실을 corpus 문서에 추가하거나 corpus hit의 지시를 실행하지 않는다. 미결 C25/C26 binding에는 검색을 넓히지 않고 `QUERY_NOT_RUN_UNRESOLVED_BINDING`을 기록한다. + +### 6.7 C28 — filtered Weaviate hybrid retrieval + +C28은 C27의 exact filter 안에서만 read-only search한다. 다음을 금지한다. + +- case type 또는 sub-rule 인접 항목 fallback +- filter 완화·제거 +- collection/tenant/index 자동 탐색 +- result 수를 채우기 위한 unapproved corpus 혼합 +- 검색 문서의 embedded prompt/tool/filter instruction 실행 + +transient retry는 release policy가 허용한 동일 request bytes로 bounded 수행한다. query/filter/config를 바꾸는 retry는 새 query가 아니라 금지된 fallback이다. raw pre-rerank hit와 score, post-rerank order, dropped reason을 보존한다. + +### 6.8 C29 — pack verification·slot crosswalk + +C29는 각 selected hit의 source document ID/hash, snapshot ID/hash, section/offset, approval, authority/proposition, corpus element ID와 temporal scope를 검증한다. 이어 `requirement_fact_scope.yml`의 법률가 승인 row로 corpus element를 Stage 1 element/opposing/defense/rebuttal/evidence slot과 exact 대조한다. + +pack의 권한은 `CAUSE_AND_ELEMENT_CHECK`다. corpus hit는 다음을 할 수 없다. + +- Stage 1에 없는 사건 사실·증거 생성 +- S2_10 decision 변경 +- C25/C26 binding 변경 +- 현행 official authority 대체 +- gap을 source ref 없이 충족 처리 + +미매핑 chunk는 context-only, conflicting chunk는 양쪽 provenance와 unresolved issue를 가진다. 동일 release·동일 query의 replay 결과 순서가 deterministic tie-break 후에도 다르면 `RETRIEVAL_TECHNICAL_INCOMPLETE`다. + +### 6.9 C30 — reports + +C30은 C20/C21/C22/C29 결과와 C26 `party_set_rule_id`를 join하여 required/present party set을 비교하고 다음을 작성한다. + +- `option_conservation_report.json` +- `party_completeness_report.json` +- `calculation_report.json` +- `filing_metrics.json` +- `exhibit_register.json` +- `element_evidence_gap/.json` + +exhibit register는 상류 입력에 기존 갑호증 label이 제공되는 경우 그 label을 보존하고, label이 없는 evidence만 stable source key 순서로 신규 번호를 발급해야 한다. 현행 상류 schema에는 기존 label 필드가 없으므로 구현·fixture는 신규 evidence의 결정적 번호 부여만 검증하며, 기존 label 보존은 S20-O09가 닫히기 전까지 미실증 상태다. S2_20은 evidence content를 고치지 않는다. filing metrics는 CE-13 receipt·range·missing operand를 명시하고 계산 불능을 임의 추정하지 않는다. party completeness report는 rule이 요구하는 role set, 현재 결속된 party refs, missing/identity-conflict refs를 분리하며 C21 사실 projection을 법률상 필수당사자 판단으로 오인하지 않는다. + +### 6.10 C35 — frozen ReliefPlan·group slice + +C35는 모든 선행 결과를 canonical stable order로 reduce하여 다음을 확정한다. + +- canonical option disposition과 atomic claim rows +- stable claim groups와 inter-group dependency +- case-type/rule/renderer/calculator/corpus binding +- selected/alternative/deferred/excluded option 보존 +- group별 party/object/amount/period/counter-performance/procedural declaration slot +- group별 legal/retrieval/calculation/review issue refs +- S2_30 drafting permission: `CLEAN_DRAFT | REVIEW_DRAFT | WORKNOTE_ONLY` +- `P31_rule_and_pack`, `P32_common_authority`, frozen S30 group slice + +S2_30은 frozen plan을 변경할 수 없다. plan content 변경이 필요하면 새 attempt로 S2_20부터 다시 실행하고 기존 plan은 immutable하게 보존한다. + +--- + +## 7. output IO와 logical barrier + +### 7.1 정상 artifact tree + +```text +O/plan/ +├── canonical_relief_plan.json +├── claim_groups.json +├── case_type_bindings.json +├── issue_ledger.plan.json +├── rule_context_packs/.json +├── prompt_segments//P31_rule_and_pack.json +├── prompt_segments//P32_common_authority.json +├── query_plans/.json +├── retrieval_results/.json +├── retrieval_receipts/.json +├── requirement_fact_packs/.json +├── element_evidence_gap/.json +├── calculation_receipts/.json +├── calculation_report.json +├── filing_metrics.json +├── exhibit_register.json +├── option_conservation_report.json +├── party_completeness_report.json +├── group_slices/.json +└── plan_publish_status.json +``` + +file family의 exact members는 `plan_publish_status.artifact_rows`가 열거하며 runtime directory scan으로 발견하지 않는다. optional family가 빈 경우에도 closed empty-list representation을 사용하고 무명 파일을 추가하지 않는다. + +### 7.2 `issue_ledger.plan` 계보 + +```text +review/issue_ledger.base.json [S2_00 immutable] + → plan/issue_ledger.plan.json [S2_20 single writer] + → review/issue_ledger.final.json [S2_40 single writer] +``` + +S2_20은 base row ID를 모두 보존한다. 해결된 row도 삭제하지 않고 resolution ref·actor class·source hash·status transition을 새 plan projection에 기록한다. C20~C35 신규 issue는 deterministic ID와 component/source refs를 가진다. + +### 7.3 status-last barrier + +`plan_publish_status.json`은 최소 다음을 가진다. + +```yaml +schema_version: stage2_plan_publish_status.v1 +workflow_id: S2_20 +run_binding_digest: 64-lower-hex +input_snapshot_digest: 64-lower-hex +parent_stage2_release_sha256: 64-lower-hex +s2_10_publish_status_sha256: 64-lower-hex +s2_20_agent_sha256: 64-lower-hex +s2_20_code_sha256: 64-lower-hex +plan_status: PLAN_COMPLETE | PLAN_COMPLETE_WITH_REVIEW | TECHNICAL_INCOMPLETE +route: TO_S2_30 | STOP_TECHNICAL_INCOMPLETE +consumer_authorized: boolean +artifact_rows: [exact-path-schema-hash] +artifact_set_digest: 64-lower-hex +group_ids: [stable-sorted-string] +clean_draft_group_ids: [stable-sorted-string] +review_draft_group_ids: [stable-sorted-string] +worknote_only_group_ids: [stable-sorted-string] +issue_summary: object +barrier_sha256: 64-lower-hex +``` + +정상/법률검토 branch는 모든 non-barrier artifact를 `write_binary_file`로 기록하고 같은 tool로 read-back하여 exact bytes/hash를 재검증한 뒤 barrier를 마지막에 기록한다. `PLAN_COMPLETE_WITH_REVIEW`는 법률·binding·corpus·calculation gap을 보존한 정상 technical completion이며 `TO_S2_30`을 허용한다. + +기술 실패가 plan 작성 전에 발생하면 정확한 `O/plan/technical_diagnostic.json` 하나를 `schemas/relief_plan.schema.json#/$defs/technical_diagnostic`으로 기록한 뒤 `TECHNICAL_INCOMPLETE/STOP_TECHNICAL_INCOMPLETE/consumer_authorized=false` barrier만 status-last로 쓸 수 있다. 이를 complete plan으로 오인하지 않도록 정상 artifact rows와 schema가 분리되어야 한다. S2_40 status-only로 자동 우회한다고 추정하지 않고 operator intervention을 요구한다. + +정상과 진단 branch 모두 `artifact_rows`가 유일한 physical output manifest다. 각 row는 exact relative path, named schema `$ref`, raw SHA-256, byte size와 producer component를 가지며, 동일 path는 정확히 한 번만 나타난다. directory scan 결과나 별도 암묵 manifest를 artifact set의 정본으로 사용하지 않는다. + +barrier file도 write 후 read-back hash를 검증하되 self-hash는 `barrier_sha256` field를 제거한 canonical object hash로 계산한다. consumer는 outer receipt와 barrier raw hash를 함께 검증한다. + +--- + +## 8. 상태·failure scope·retry + +### 8.1 법률상/자료상 issue와 기술 실패 분리 + +다음은 plan을 폐기하지 않는다. + +- schema-valid `CONDITIONAL`·`UNRESOLVED` +- case-type/sub-rule zero 또는 multi match +- required party/title/object/operand 미확정 +- approved corpus scope 또는 crosswalk gap +- query no-hit +- client no-sue instruction +- 계산 range 또는 missing operand + +이들은 `PLAN_COMPLETE_WITH_REVIEW`와 group drafting permission에 반영한다. clean relief 또는 filing-decision READY는 후단에서 제한되지만 worknote/review draft는 보존한다. + +다음은 일관된 artifact set 작성이 불가능한 technical failure다. + +- release/input/barrier/hash/schema/producer-contract mismatch +- S2_10 terminal technical failure 또는 exact coverage 불일치 +- path escape, duplicate key, non-finite value, resource-limit breach +- canonical ID collision, immutable byte conflict, read-back mismatch +- unhandled internal invariant failure +- release-bound retrieval config가 아닌 endpoint/tenant/operation만 사용 가능한 상태 + +### 8.2 retry + +- localdocs transient read/write는 release-bound fixed backoff와 max attempt로 동일 request만 재시도한다. +- Weaviate retry는 exact same query/filter/config/snapshot bytes만 허용한다. +- hash/schema/authorization/path/predicate/immutable conflict는 retry하지 않는다. +- 같은 binding 재실행은 기존 barrier와 artifact set이 exact하면 idempotent return한다. +- barrier 없는 partial output은 각 path의 existing bytes를 검증하여 byte-identical file은 재사용하고 누락 file만 쓴다. conflicting bytes는 덮어쓰지 않는다. +- attempt ID와 operational latency는 artifact identity 또는 legal ordering input이 아니다. + +--- + +## 9. schema 계약 + +S2_20 구현은 최소 다음 existing/planned schema root를 닫는다. + +| schema | S2_20 mandatory `$defs` | +|---|---| +| `schemas/ingress.schema.json` | S2_00 ingress barrier, run-binding receipt와 canonical output-root binding | +| `schemas/context.schema.json` | exact upstream context/slice/bundle refs | +| `schemas/s2_10.schema.json` | publish status·item/wave receipt echo | +| `schemas/domain_verdict.schema.json` | canonical verdict·claim option·relations·typed provenance | +| `schemas/relief_plan.schema.json` | option disposition, atomic claim, claim group, frozen plan, group slice, `$defs.plan_publish_status`, `$defs.technical_diagnostic` | +| `schemas/binding_retrieval.schema.json` | `$defs.binding_signature_projection`, `$defs.option_disposition_policy`, `$defs.party_set_rule`, signature/predicate, case-type/sub-rule binding, query/result/receipt/pack/crosswalk | +| `schemas/calculation.schema.json` | `$defs.calculation_activation_registry`, activation, operand, formula, law value, period row, receipt, filing metrics | +| `schemas/review_status.schema.json` | base→plan ledger transition, issue/review tag | +| `schemas/deployment.schema.json` | `$defs.stage2_code_executor_binding_v3`, `$defs.stage_code_executor_binding`, `$defs.s2_20_request`, `$defs.s2_20_execution_receipt`, `$defs.s2_20_inline_code_receipt`, `$defs.stage2_deterministic_admission_receipt` | + +모든 runtime output은 named `$id/$defs` root와 module manifest owner를 가져야 한다. schema가 runtime에서 생성되거나 Python dataclass로 대체되지 않는다. schema validation과 semantic invariant validation을 구별한다. + +--- + +## 10. release·hash·reseal 계약 + +### 10.1 code projection + +`build_s2_20_inline_projection.py`는 다음만 수행한다. + +1. duplicate-key rejecting parser로 authoring YAML을 읽는다. +2. exactly-one S2_20 `run_code` task와 exact parameters를 검증한다. +3. parser가 반환한 `parameters.code` string을 변경 없이 UTF-8 encode한다. +4. deployment Agent를 authoring bytes에서 투영한다. +5. full mirror `.py/.txt`를 같은 code bytes로 쓴다. +6. compile/AST, 금지 import/call, endpoint/credential literal, task semantics를 검사한다. +7. authoring/deployment/code/mirror hash를 inline receipt에 기록한다. + +builder는 authoring YAML을 재작성하거나 사건 runtime에서 실행되지 않는다. + +### 10.2 비순환 봉인 순서 + +S2_20 구현은 기존 S2_00·S2_10 release chain을 다음 순서로 재봉인한다. + +1. case type/rule/calculator/corpus/schema/workflow/test 자산과 module manifest를 확정한다. +2. parent `stage2_release.json`을 새 dependency set으로 생성하고 raw hash를 확정한다. +3. singleton S2_00 executor binding/schema를 `stage2_code_executor_binding.v3`의 closed `stage_bindings[]`로 이행하고 `deployment/stage2_loader_binding.yml`의 reference-only target schema도 v3로 갱신한다. 그 parent hash를 S2_00과 S2_20 inline constant `EXPECTED_STAGE2_RELEASE_SHA256` 및 각 stage row에 결속하여 두 Agent projection·mirror·receipt를 재생성한다. 이 migration에는 S2_00 projection builder, `test_inline_code_parity`, `test_schema_hash_and_signals`, `test_cluster_bundle_compile`의 singleton 가정 갱신이 포함된다. +4. S2_10 child release와 child-dependent receipts를 새 parent raw hash로 재봉인한다. +5. S2_00→S2_10 handoff의 opaque Agent/binding hash와 S2_20 request/preflight refs를 재검증한다. +6. detached signed admission/outer receipt가 parent release, `present_deterministic_stage_ids`에 실제 열거된 모든 deterministic Agent/code receipt, S2_10 child/binding과 backend capability를 결속한다. S2_40이 미구현인 release에서는 그 receipt를 성공한 것으로 창작하지 않으며, production admission 시점에는 S2_00·S2_20·S2_40 전부가 존재해야 한다. + +parent release가 자신의 raw hash를 내장한 Agent·binding hash를 다시 raw-hash dependency로 포함하여 순환시키지 않는다. parent raw closure에는 schema·workflow·registry·test·법률/corpus manifest만 포함한다. code-executor binding과 Agent/code receipt는 parent release의 detached admission layer이며 external trust-root receipt가 exact hash를 봉인한다. raw hash 변경 시 관계된 downstream release/receipt를 모두 다시 계산하고 과거 hash를 병존시키지 않는다. 이는 v5-1의 release-bound 요구를 버리는 것이 아니라 자기참조를 제거한 규범적 구현 해석이다. + +### 10.3 release 상태 정직성 + +offline builder/test PASS는 `IMPLEMENTED_OFFLINE_VERIFIED`까지만 증명한다. 다음은 별도 live evidence다. + +- actual `code-executor.run_code` outer/inner receipt +- request-size·300초·runtime image·pinned dependency +- user/workspace isolation과 binary byte preservation +- host CAS와 single writer +- exact Weaviate egress/secret/read-only operation +- live retrieval replay·timeout·partial failure +- S2_00→S2_10→S2_20→S2_30 route integration +- authority/rule/corpus/137 coverage 대한민국 변호사 sign-off + +--- + +## 11. fixture·test 계약 + +### 11.1 최소 논리 test family + +| family | 필수 검증 | +|---|---| +| Agent/parity | YAML exactly-one `run_code`, no LLM/direct retrieval task, `task_procedure` IN→single task→OUT, authoring/deployment/code/.py/.txt parity | +| input/preflight | S2_00/S2_10 barriers, exact verdict/wave coverage, release/child/producer hash, strict parser/path/resource/TOCTOU | +| C20/C21 | option exact partition, relation graph, stable atomic/group ID, no-sue separation, party/title/liability/identity unresolved | +| C25/C26 | exact case type/sub-rule one match, zero/multi/alias/hash mutation, row-kind/generic policy, renderer/rule/CE-13 FK | +| C22 | CE-01/03/08/10/13 boundary와 activated CE 전체, Decimal/unit/rounding, missing operand, temporal law value | +| C27~C29 | exact filters, no fallback, injection isolation, source/snapshot/approval/locator, slot crosswalk, replay/tie-break/timeout | +| C30/C35 | exhibit stability, filing/party/option report, prompt segments, frozen plan/group slice, issue-ledger lineage | +| publish/release | immutable conflict, partial write, barrier-last, idempotence, single writer, output hash, builder/release/reseal/legacy dependency scan | + +각 Python test와 offline builder/runtime source는 동일 내용의 `.txt` mirror를 둔다. `runtime/authority/authority_preflight.py/.txt`도 같은 parity·fixture 검증 대상이다. fixture와 expected oracle은 별도 JSON/YAML 자산이며 workflow YAML 안에 inline blob으로 숨기지 않는다. + +### 11.2 필수 fixture family + +1. single option/single group 정상 case +2. primary·alternative·cumulative·accessory·precondition 혼합 portfolio +3. client no-sue와 legal `EXCLUDED` 분리 +4. case type/sub-rule exact, zero, multi, alias, stale hash +5. composite/generic/group/doctrine row와 atomic branch +6. party/title/liability/identity unresolved·필수당사자 누락 +7. CE-01 interest timeline과 피고별 service event +8. CE-03 limitation/제척기간 urgency +9. CE-08 유류분 신·구법 경계 +10. ACTIO 6 route·두 기간·3-cap·recipient·mortgage branch +11. CE-13 소가·인지·병합·부대청구·missing operand +12. retrieval exact match/no-hit/timeout/replay drift/injection/conflicting chunk +13. corpus approval/hash/snapshot/crosswalk mutation +14. deterministic new exhibit label; 기존 label preservation은 상류 label 계약 fixture가 생긴 뒤 별도 검증 +15. option silent-loss·duplicate recovery mutation +16. partial-write/barrier-before-artifact/immutable conflict/concurrent same binding +17. old Stage 2 v.0~v.3 path injection + +fixture의 당사자·금액·날짜는 external data와 source locator/hash를 가지며 general Python이나 registry에 사건별 값을 하드코딩하지 않는다. + +위 17개 항목은 물리 payload 파일 수와 무관한 logical family다. 각 항목은 고유 `fixture_case_id`와 최소 1개의 독립 mutation row를 가져야 하고, `tests/fixtures/s2_20/regression_manifest.json`이 `fixture_case_id → payload path + JSON pointer/selector → expected issue code/route + oracle hash`를 1:1 결속한다. 여러 family를 한 payload container에 저장할 수 있으나 누락·암묵 공유·expected code 없는 row는 허용하지 않는다. + +### 11.3 acceptance gates + +| gate | 통과조건 | +|---|---| +| G20-00 Agent shape | 1 Stage, 1 `run_code`, 0 LLM, 0 direct retrieval task, exact `task_procedure` | +| G20-01 Code parity | authoring/deployment/full mirror byte equality·compile·AST·금지 import 0 | +| G20-02 Input closure | S2_00/S2_10 exact barrier·coverage·schema/hash/release closure | +| G20-03 Conservation | option/issue/source ref silent loss 0, disposition partition exact | +| G20-04 Binding | atomic claim별 case-type/sub-rule exact predicate, zero/multi closed issue, fuzzy fallback 0 | +| G20-05 Calculation | activated CE receipt 100%, output numeral provenance 100%, missing operand fabrication 0 | +| G20-06 Retrieval | exact filter/snapshot/approval/crosswalk, injection isolation, retry/replay deterministic | +| G20-07 Freeze | stable group/plan/slice/P31/P32/report hash와 producer single writer PASS | +| G20-08 Barrier | non-barrier write/read-back 후 status-last, partial/conflict/idempotence property PASS | +| G20-09 Release | manifest/schema/Agent/code/binding/receipt/reseal closure와 v.0~v.3 dependency 0 | +| G20-10 Live admission | Code Executor, host CAS, workspace, byte I/O, Weaviate egress/secret, route live receipt PASS | + +G20-00~09 offline PASS가 G20-10 또는 법률가 semantic sign-off를 대체하지 않는다. + +--- + +## 12. 단계별 생성 작업 + +### Phase S20-0 — contract lock + +1. S2_10 canonical option/input coverage와 S2_20 request shape 고정 +2. `(C20 || C21) → C25 → C26 → (C22 || (C27 → C28 → C29)) → C30 → C35` internal DAG 고정 +3. atomic claim/group ID와 option conservation formula 고정 +4. S2_10 source signature→C25 target signature projection, case type/sub-rule/party-set predicate grammar와 issue code 고정 +5. option disposition decision table와 calculator activation·law-value·per-calculation receipt contract 고정 +6. Weaviate endpoint/filter/snapshot/crosswalk/replay/credential contract 고정 +7. artifact tree·single writer·barrier/status/route contract 고정 +8. release reseal graph와 external trust boundary 고정 + +Exit: 미확인 사항은 closed OPEN item과 owner/evidence로 남기고 임의 runtime primitive나 secret을 창작하지 않는다. + +### Phase S20-1 — schema·fixture first + +1. relief-plan/binding-retrieval/calculation schema와 shared refs 작성 +2. 정상·review·technical diagnostic output golden shape 작성 +3. C20/C21/C25/C26/C22/C27~29/C30/C35 fixture oracle 작성 +4. zero/multi/hash/path/replay/injection/partial-write mutation 작성 +5. release·barrier·outer/inner receipt schema 작성 + +Exit: runtime code 없이도 모든 fixture input/output·expected route가 기계 판독 가능하다. + +### Phase S20-2 — inline runtime 구현 + +1. strict localdocs MCP/session/binary adapter와 release preflight +2. closed parser/canonical serializer/ID mint/resource limit +3. C20/C21 parallel branch와 deterministic join +4. C25/C26 exact binding +5. C22 calculator branch +6. C27→C28→C29 retrieval branch +7. C30 reports와 C35 frozen reduce +8. write/read-back/status-last barrier·idempotence +9. stdout single canonical JSON object + +Exit: unit/property/mutation tests PASS, LLM call 0, 승인 endpoint 외 egress 0, 사건별 code mutation 0. + +### Phase S20-3 — Agent·projection·manifest 결속 + +1. `Stage_2_S2_20.yml`에 complete inline code와 exact `task_procedure` 작성 +2. deployment Agent/full mirror/pure-core oracle/builder/inline receipt 생성 +3. shared code-executor binding에 S2_20 Agent/code/release/egress/secret row 추가 +4. schemas/workflow/tests/fixture/module/case-type/authority/corpus dependency hash 결속 +5. parent release→S2_00/S2_20 code→S2_10 child 순차 reseal +6. old Stage 2 path·external `.py`·credential·fuzzy fallback scanner 실행 + +Exit: offline builder `--check`, full tests와 explicit hash-chain closure가 PASS한다. + +### Phase S20-4 — live canary + +1. actual Code Executor outer/inner receipt +2. representative request-size·300초·runtime image·dependency 검증 +3. user/workspace cross-isolation negative test +4. localdocs binary read/write byte equality +5. exact Weaviate host/tenant/search operation·secret·egress enforcement +6. same-binding CAS/idempotence와 partial-write recovery +7. S2_10 COMPLETE→S2_20 barrier→S2_30 mock/live route + +Exit: release class별 signed admission을 받는다. subset canary를 `FULL_137_PRODUCTION_READY`로 표현하지 않는다. + +--- + +## 13. Definition of Done + +### 13.1 `IMPLEMENTED_OFFLINE_VERIFIED` + +- [ ] authoring Agent에 complete static inline Python task가 정확히 1개다. +- [ ] `task_procedure`가 `IN→Task_S2_20_deterministic_relief_plan→OUT`을 exact 선언한다. +- [ ] `(C20 || C21) → C25 → C26 → (C22 || (C27 → C28 → C29)) → C30 → C35` internal DAG가 code/workflow/test에 동일하다. +- [ ] authoring/deployment/code/full `.py/.txt` mirror가 byte-identical하고 compile/AST receipt가 PASS한다. +- [ ] runtime external `.py`, LLM, shell/subprocess/dynamic code, 구 v.0~v.3 dependency가 0이다. +- [ ] S2_00/S2_10 input barrier와 exact verdict/wave coverage를 검증한다. +- [ ] option exact partition과 base→plan issue-ledger conservation이 PASS한다. +- [ ] atomic claim/group ID가 반복·순열·parallel 실행에서 동일하다. +- [ ] case type/sub-rule exact match와 zero/multi closed issue가 PASS하고 fuzzy fallback이 0이다. +- [ ] 17 calculator activation/receipt와 CE-01/03/08/10/13 boundary fixture가 PASS한다. +- [ ] exact Weaviate filter·snapshot·approval·crosswalk·replay·injection fixture가 PASS한다. +- [ ] frozen plan/group slice/P31/P32/report의 source refs·hash가 complete하다. +- [ ] 모든 non-barrier artifact write/read-back 후 plan barrier를 마지막에 쓴다. +- [ ] 동일 binding idempotence, 다른 binding isolation, conflict no-overwrite가 PASS한다. +- [ ] parent/child release와 S2_00/S2_10/S2_20 관련 receipt가 explicit hash-chain 검사에서 일치한다. +- [ ] live backend·Weaviate·legal admission 미수행 상태를 명시한다. + +### 13.2 `LIVE_CANARY_ADMITTED` + +- [ ] actual `run_code`가 outer success와 inner receipt를 모두 통과한다. +- [ ] 300초/request-size/runtime image/httpx pinning이 대표 case에서 실증된다. +- [ ] localdocs user/workspace isolation·binary byte preservation이 실증된다. +- [ ] host CAS/single writer와 partial retry가 실증된다. +- [ ] exact Weaviate egress/credential/read-only operation과 retrieval replay가 실증된다. +- [ ] S2_10→S2_20→S2_30 route가 barrier/hash를 따라 정확히 한 번 실행된다. +- [ ] canary 범위 case type/rule/corpus/calculator authority에 법률가 승인 evidence가 있다. + +### 13.3 `PRODUCTION_READY` + +- signed parent/child/detached admission trust chain +- release scope의 authority/law-value/rule/corpus/calculator semantic review +- 137종은 `FULL_137_PRODUCTION_READY`의 별도 8개 조건 전부 충족 +- representative Stage 1→S2_00→S2_10→S2_20→S2_30→S2_40 E2E +- security·cost·latency·rollback·tamper gate +- 자격 있는 대한민국 변호사의 filing-decision workflow admission + +--- + +## 14. 금지규칙 + +1. S2_20 Agent에 두 번째 Code Executor·LLM·direct MCP task를 추가하지 않는다. +2. C20~C35를 top-level task로 분리하여 5-task 헌법을 깨지 않는다. +3. 실제 authoring YAML code를 placeholder·요약 주석·외부 import로 대체하지 않는다. +4. case name, group label, embedding, fuzzy filename으로 substantive claim 또는 case type을 결정하지 않는다. +5. case type/sub-rule multi-match를 복수 청구로 자동 생성하지 않는다. +6. S2_10 decision을 C25/rule/corpus/calculator가 변경하지 않는다. +7. option, adverse fact, review issue, client no-sue를 silent drop하지 않는다. +8. rule/corpus hit를 Stage 1 사실·증거로 승격하지 않는다. +9. retrieval no-hit·binding gap·missing operand를 0 또는 확정값으로 채우지 않는다. +10. query filter를 완화하거나 인접 case type/sub-rule/corpus로 fallback하지 않는다. +11. retrieval content의 prompt/tool/filter instruction을 실행하지 않는다. +12. output-affecting 숫자를 `law_value_id`·operand·receipt 없이 생성하지 않는다. +13. binary float로 금액·이율·기간을 계산하지 않는다. +14. code 내부 credential, 과거 notebook bearer 또는 임의 external endpoint를 기록하지 않는다. +15. same-binding conflicting bytes를 overwrite하지 않는다. +16. barrier 전에 downstream consume를 허용하지 않는다. +17. S2_20이 `READY`, final status, final document, review receipt 또는 commit을 쓰지 않는다. +18. directory scan·glob·basename fallback으로 runtime dependency를 찾지 않는다. +19. old Stage 2 `v.0~v.3` YAML·prompt·loader·asset 원문을 runtime dependency로 사용하지 않는다. +20. offline fixture·static hash PASS를 live/production/법률검수 완료로 표현하지 않는다. + +--- + +## 15. 잔여 OPEN과 구현 착수조건 + +| OPEN | owner | 필요한 evidence | offline 구현 영향 | +|---|---|---|---| +| S20-O02 host CAS primitive | AgentBackend owner | actual handler/version/digest/live concurrency receipt | live blocker; offline 구현 가능 | +| S20-O03 Weaviate MCP/tool/credential live admission | corpus/infra owner | 고정 MCP endpoint·`search_hybrid`의 실제 tool schema·secret/egress receipt | 계약값은 닫힘; mock offline 가능, live blocker | +| S20-O04 case type/rule 137 release | 대한민국 변호사·release owner | registry/rule Markdown/projection/sign-off/coverage | subset fixture 가능; full-137 blocker | +| S20-O05 corpus release | corpus curator·대한민국 변호사 | snapshot/index/chunk/crosswalk/approval seal | mock offline 가능; affected production scope blocker | +| S20-O06 calculator/law-value semantic approval | 법률 reviewer·release owner | formula/authority/temporal fixture sign-off | structural offline 가능; production blocker | +| S20-O07 Code Executor live admission | AgentBackend/Code Executor owner | secret/image/timeout/request-size/outer-inner receipt | live blocker | +| S20-O08 detached release admission | release operator | non-cyclic signed envelope | production blocker | +| S20-O09 existing exhibit-label handoff | Stage 1/S2_00 schema owner | evidence별 기존 갑호증 label·source ref·충돌 규칙과 fixture | 신규 label offline 검증 가능; 기존 label 보존 E2E blocker | +| S20-O10 typed calculation operand ledger | Stage 1/S2_00 schema owner·calculation owner | operand ID·typed value·unit·effective date·source locator/hash | `MISSING_OPERAND` 보존 가능; `CALCULATED` E2E blocker | + +위 OPEN을 감추기 위해 미검증 endpoint·primitive·법률수치·generic fallback을 창작하지 않는다. + +--- + +## 16. 최종 판정 + +본 SOW는 S2_20을 **정확히 1개의 release-bound inline MCP `run_code` task**로 구현하기 위한 self-contained 작업명세다. AgentBackend의 `task_procedure`는 IN→single task→OUT만 표현하고, 법률·계산·검색의 실제 내부 순서는 inline Python의 `(C20 || C21)→C25→C26→(C22 || (C27→C28→C29))→C30→C35` DAG로 고정한다. + +`Stage_2_S2_20.yml`, runtime mirror, registry·schema·fixture·test와 non-cyclic release package는 offline 구현·검증되었다. 이 판정은 실제 Code Executor·host CAS·Weaviate egress/secret 실행, 승인된 137개 청구취지 규칙·corpus·authority, typed operand/기존 exhibit-label 상류 계약, full E2E 또는 대한민국 변호사 검수를 포함하지 않는다. 이들에는 각각 별도의 live/admission evidence가 필요하다. diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/S2_20_assets.md b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/S2_20_assets.md new file mode 100644 index 00000000..44cae9d6 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/S2_20_assets.md @@ -0,0 +1,468 @@ +# S2_20 deterministic ReliefPlan 실행 자산 inventory + +> 기준 전략: `stage_2_optimal_update_strategy_v.5-1.md` §0.5, §3, §4.6–§4.8, §7, §12–§16 +> +> 선행 구현: `S2_00_assets_v.2.md`, `S2_10_assets_v.1.md` +> +> canonical 배포 root: `Default_Agent/Stage_2_Clean/` +> +> 문서 상태: `IMPLEMENTED_OFFLINE_VERIFIED / LIVE_AND_LEGAL_ADMISSION_PENDING`. 이 inventory는 live Code Executor·Weaviate 실행, authority/corpus 승인 또는 대한민국 변호사 검수 완료 증거가 아니다. + +--- + +## 구현 결과 overlay + +유한 구현 inventory의 Agent·projection·inline mirror·schema·registry·renderer·builder·fixture·test와 release/binding/receipt 연쇄는 생성되어 offline 검증되었다. 현 snapshot의 구조적 수량은 case type registry/coverage 137행, calculation descriptor 17개, renderer 6개, S2_20 logical fixture family 17개다. 그러나 `R/rules/relief/*.md`는 **0/137**, 승인된 structured rule branch는 **0개**이며, authority/corpus/law-value admission은 `PENDING` 또는 `DEV_UNAVAILABLE`이다. + +현행 상류 산출물에는 기존 갑호증 label과 계산용 typed operand value ledger가 없다. 따라서 exhibit 자산은 신규 label의 stable assignment만 offline 검증하며, calculator 자산은 operand 누락을 `MISSING_OPERAND`로 보존한다. 기존 label 보존과 `CALCULATED` E2E는 상류 계약 보강 후 별도 검증 대상이다. + +--- + +## 0. 판독 기준과 구현 경계 + +```text +M = Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/ +R = M/Default_Agent/Stage_2_Clean/ +P = Case_02_Comparison_Research/ +O = stage2_runs/by-binding// +``` + +| disposition | 의미 | +|---|---| +| `NEW` | 현행 `R`에 없고 S2_20 구현을 위해 새로 생성 | +| `UPDATE-RESEAL` | 현행 파일을 유지하되 S2_20 field·hash·dependency를 추가하고 재봉인 | +| `REUSE-BOUND` | 현행 bytes와 의미계약을 유지하고 새 release에서 actual hash를 재검증 | +| `REUSE-AS-SEED` | 현행 자산을 구조 seed로만 사용; 법률·semantic 승인 전 production 사용 금지 | +| `EXTERNAL-PENDING` | 대한민국 변호사, authority curator 또는 Weaviate operator가 별도로 완성·승인해야 함 | +| `REFERENCE-ONLY` | build/migration/감사 source일 뿐 사건 runtime이 직접 읽지 않음 | +| `RUNTIME-ARTIFACT` | 사건별 실행 중 생성되며 고정 배포 자산 수에서 제외 | + +S2_20은 `NON-LLM-DETERMINISTIC` stage다. 실행 Agent에는 `mcp: code-executor`, `tool_name: run_code` task를 정확히 하나만 두고, LLM task·map/reduce fan-out·외부 `.py` import를 두지 않는다. authoring YAML의 `parameters.code`가 실행 source of truth이고 `runtime/*.py/.txt`는 byte-identical full mirror 또는 pure-core offline oracle이다. + +내부 DAG는 다음 하나다. + +```text +C20/C21 option·party portfolio + | + v +C25/C26 exact case_type·sub_rule·renderer binding + | + +----------------------+ + v v +C22 deterministic CE C27 -> C28 -> C29 +calculations verified requirement pack + +----------------------+ + | + v + C30 reports -> C35 freeze + | + v + plan_publish_status.json LAST barrier +``` + +`case_type_id`는 S2_10의 청구권 존부 판단키가 아니라 S2_20 후단의 drafting·retrieval·coverage selector다. 사건명 fuzzy match, nearest-name fallback, 인접 corpus fallback 및 legacy Stage 2 v.0–v.3 runtime import는 모두 금지한다. + +### 0.1 수량을 읽는 법 + +- §1은 S2_20 실행코드·schema·registry·test를 만드는 **유한 구현 단위**다. `.py/.txt`는 두 물리 파일이지만 하나의 생성·검증 단위로 취급한다. +- §2의 청구취지 규칙은 정확히 137 Markdown family이고, authority capture와 corpus raw 문서는 각각 `N_authority`, `N_source_doc`이다. 법률 내용을 자동 생성해 구현 수를 부풀리지 않는다. +- §3의 shared release 파일은 S2_20 소유물이 아니지만 S2_20을 실제 배포하려면 hash cascade상 반드시 재봉인한다. +- 동일 파일이 역할별 표에 다시 언급되어도 물리 파일을 중복 생성하지 않는다. +- 구현 orchestration의 상위 묶음은 `N_workstream=2`다: (A) schema·선언형 registry·renderer·fixture, (B) inline/pure-core·builder·test·shared-binding migration. 이는 사용자 method의 `N`을 재정의하지 않는다. 실제 `N_independent_batches`는 dependency DAG의 각 ready wave에서 서로 쓰기 경로와 선행 hash가 겹치지 않는 생성 batch를 세어 정하고, ready wave 종료 때마다 join한다. 두 workstream join 뒤 projection→manifest→release→receipt는 순차 봉인한다. + +### 0.2 specification package + +| exact path | 성격 | 실행 관계 | +|---|---|---| +| `M/S2_20_SOW.md` | S2_20 구현 전략서 | authoring·schema·test의 규범 source이나 runtime input은 아님 | +| `M/S2_20_assets.md` | 본 inventory | disposition·경로·hash cascade의 관리 문서이나 runtime input은 아님 | + +--- + +## 1. S2_20 유한 구현 inventory + +### 1.1 authoring·projection·workflow·receipt + +| exact path | 조치 | writer | consumer | 역할·검증·hash 관계 | +|---|---|---|---|---| +| `M/Stage_2_S2_20.yml` | `NEW` | S2_20 authoring owner | AgentBackend | 유일 편집 정본. exactly-one `run_code`, 완전한 inline Python, `task_procedure` DAG, build-time parent release raw SHA-256를 포함 | +| `R/agent_scripts/Stage_2_S2_20.yml` | `NEW` | projection builder | AgentBackend | authoring과 byte-identical한 version-free 배포본 | +| `R/workflows/S2_20_canonical_relief_plan_reduce.yml` | `NEW` | workflow owner | builder, validator, release | `(C20 || C21) → C25 → C26 → (C22 || (C27 → C28 → C29)) → C30 → C35`, IO·route·barrier의 선언형 계약; Python code 없음 | +| `R/runtime/s2_20_reduce.py` | `NEW` | projection builder | offline tests | authoring `parameters.code` 전체의 exact UTF-8 mirror; runtime import 금지 | +| `R/runtime/s2_20_reduce.txt` | `NEW` | projection builder | 사람 검토, parity test | 위 `.py`와 byte-identical | +| `R/offline_build/build_s2_20_inline_projection.py` | `NEW` | release build owner | offline build/CI | unique-key YAML parse, task shape·DAG·code compile/AST 검사, authoring→projection·mirror·receipt 생성과 `--check` | +| `R/offline_build/build_s2_20_inline_projection.txt` | `NEW` | release build owner | parity test | 위 `.py`와 byte-identical | +| `R/manifest/s2_20_inline_code_receipt.json` | `NEW` | projection builder | executor binding, release validator | `deployment.schema.json#/$defs/s2_20_inline_code_receipt`; authoring/projection/code/mirror path·hash, compile/AST, exactly-one-task, release constant를 기록 | +| `R/manifest/stage2_deterministic_admission_receipt.json` | `NEW-PENDING-EXTERNAL-EVIDENCE` | release operator / signer | production admission verifier | `deployment.schema.json#/$defs/stage2_deterministic_admission_receipt`; parent release와 `present_deterministic_stage_ids`의 Agent/code/outer receipt를 detached 봉인하며 live evidence 전 `PENDING` | +| `R/manifest/s2_20_parent_member_paths.json` | `NEW` | release build owner | `build_release_manifests.py` | parent raw closure에 추가할 S2_20 고정자산 115개 경로의 명시적 allowlist; Agent·inline code·receipt·shared binding·child release 등 parent hash를 소비하는 downstream 자산은 제외 | +| `R/deployment/stage2_code_executor_binding.yml` | `UPDATE-MIGRATE-RESEAL` | release operator | AgentBackend | singleton S2_00 binding을 `stage2_code_executor_binding.v3`의 closed `stage_bindings[]`로 이행하고, S2_00 row를 byte-semantics 보존한 채 S2_20 Agent·code·workflow·receipt·schema, `httpx==0.28.1`, timeout, localdocs+exact read-only Weaviate egress row를 추가 | + +`stage2_code_executor_binding.yml`에는 Weaviate plaintext secret를 쓰지 않는다. S2_20 row는 MCP-over-HTTP JSON-RPC endpoint `https://weaviate.eroomai.com/mcp`, initialize `protocolVersion=2025-03-26`, read-only tool `search_hybrid`, collection·tenant, `credential_ref`, backend-enforced `egress_profile_id`를 exact 결속한다. endpoint/tool은 **계약값일 뿐 live 검증 완료 주장이 아니며**, credential material은 AgentBackend secret store에서만 주입한다. official authority 웹사이트는 S2_20 runtime egress allowlist에 넣지 않는다. + +### 1.2 inline에 완전 전개할 pure-core oracle + +| exact path pair | 조치 | writer | runtime consumer | brief 역할 | +|---|---|---|---|---| +| `R/runtime/binding_signature_projection.py/.txt` | `NEW` | S2_20 core owner | 외부 import 0; inline 전개본 | S2_10 normalized signature와 C20/C21 projection을 C25 closed target vocabulary로 출처보존 변환; 미도출값은 `UNKNOWN` | +| `R/runtime/c25_case_type_bind.py/.txt` | `NEW` | S2_20 core owner | 외부 import 0; inline 전개본 | closed signature predicate의 exact `case_type_id` binding까지만 수행, zero/multi code | +| `R/runtime/c26_rule_branch_bind.py/.txt` | `NEW` | S2_20 core owner | 동일 | bound case type의 exact `sub_rule_id`·renderer·party-set·corpus·CE-13 FK 결속 | +| `R/runtime/c27_query_plan.py/.txt` | `NEW` | S2_20 core owner | 동일 | element·defense·rebuttal·burden·relief-consistency 역할별 deterministic query plan | +| `R/runtime/c28_weaviate_retrieve.py/.txt` | `NEW` | S2_20 core owner | 동일 | metadata-filtered hybrid retrieval, timeout, ordered hit·score·tie-break·replay receipt | +| `R/runtime/c29_pack_verify.py/.txt` | `NEW` | S2_20 core owner | 동일 | snapshot/hash/locator/approval/authority·slot crosswalk와 prompt-injection data 경계 검증 | +| `R/runtime/calculators/registry_engine.py/.txt` | `NEW` | calculation owner | 동일 | CE YAML dispatch, operand·unit·temporal law-value 검증, canonical calculation receipt | +| `R/runtime/authority/authority_preflight.py/.txt` | `NEW` | authority/release owner | 동일 | runtime web fetch 없이 sealed authority/law-value temporal dependency와 freshness 상태만 검사 | + +각 pair는 동일 source bytes로 생성한다. pure-core 파일은 독립 테스트용이며 authoring Agent가 사건 runtime에서 이 파일을 읽거나 import하지 않는다. + +### 1.3 schema bundle + +| exact path | 조치 | writer | consumer | 역할 | +|---|---|---|---|---| +| `R/schemas/relief_plan.schema.json` | `NEW` | schema owner | S2_20, S2_30, S2_40 | option disposition, atomic claim, stable group, frozen ReliefPlan, plan ledger 및 정확한 `$defs.plan_publish_status`, `$defs.technical_diagnostic` | +| `R/schemas/binding_retrieval.schema.json` | `NEW` | schema owner | C25~C29, S2_30/S2_40 | `$defs.binding_signature_projection`, `$defs.option_disposition_policy`, `$defs.party_set_rule`, signature/predicate/binding, query/result/receipt/pack, corpus scope·slot crosswalk | +| `R/schemas/calculation.schema.json` | `NEW` | schema owner | calculator, S2_20/S2_40 | `$defs.calculation_activation_registry`, CE request, operand, period, unit, law-value, range·missing operand와 receipt | +| `R/schemas/domain_verdict.schema.json` | `NEW-MINIMAL-FACADE` | schema owner | S2_20 | 현행 `s2_10.schema.json#/$defs/canonical_domain_verdict`를 단일 source로 참조하는 thin facade; verdict schema를 복제하지 않음 | +| `R/schemas/review_status.schema.json` | `UPDATE-RESEAL` | schema owner | S2_00/S2_20/S2_40 | base ledger를 보존하고 append-only `issue_ledger.plan` row·lineage·closed issue code 추가 | +| `R/schemas/deployment.schema.json` | `UPDATE-MIGRATE-RESEAL` | schema owner | builder, binding, release validator | `$defs.stage_code_executor_binding`, `$defs.stage2_code_executor_binding_v3`, `$defs.s2_20_request`, `$defs.s2_20_execution_receipt`, `$defs.s2_20_inline_code_receipt`, `$defs.stage2_deterministic_admission_receipt` 추가; 기존 singleton S2_00 instance를 closed `stage_bindings[]` 구조로 이행 | +| `R/schemas/context.schema.json` | `REUSE-BOUND` | S2_00 owner | S2_20 input validator | cluster slice, party/object/slot, bundle refs/hash의 현행 정본 | +| `R/schemas/ingress.schema.json` | `REUSE-BOUND` | S2_00 owner | S2_20 preflight | S2_00 ingress barrier와 canonical run/output-root binding 검증 | +| `R/schemas/s2_10.schema.json` | `REUSE-BOUND` | S2_10 owner | S2_20 input validator | publish status와 per-cluster canonical DomainVerdict의 현행 정본; bytes 변경 없이 facade가 참조 | + +standalone DomainVerdict schema를 현행 S2_10 정의와 별개로 복사하면 두 정본이 생기므로 금지한다. 향후 ownership을 facade 쪽으로 역전하려면 S2_10 schema·validator·child release를 명시적으로 migration/reseal해야 한다. + +### 1.4 canonical binding·drafting·retrieval YAML + +| exact path | 조치 | writer | S2_20 사용 | +|---|---|---|---| +| `R/registry/binding/binding_signature_projection.yml` | `NEW` | schema/case-type owner + 법률 reviewer | S2_10의 7개 source field와 C20/C21·remedy context를 C25 target field로 변환하는 closed rule, source ref와 `UNKNOWN` 정책 | +| `R/registry/planning/option_disposition_policy.yml` | `NEW` | planning/legal policy owner | client instruction·S2_10 decision·dependency·compatibility를 selected/alternative/excluded/deferred로 exact partition하는 exhaustive precedence table | +| `R/registry/party/party_set_rules.yml` | `NEW` | party/legal policy owner | C21이 만든 사실 projection에 대해 C26이 결속할 필수당사자·책임형태 규칙과 C30 completeness predicate | +| `R/registry/calculations/calculation_activation_registry.yml` | `NEW` | calculation/legal owner | CE-01~13/R1~R4의 mutually auditable activation predicate·priority·zero/multi issue code·receipt requirement | +| `R/manifest/case_type_registry.yml` | `NEW` | case-type compiler + 법률 reviewer | 137 source row, closed signature predicate, row kind와 claim-capable disposition | +| `R/manifest/case_type_rule_registry.yml` | `NEW` | rule compiler + 법률 reviewer | case type→sub-rule·renderer·Markdown·corpus·CE-13·review exact FK/hash | +| `R/registry/drafting/case_type_relief_rules.yml` | `NEW` | relief-rule compiler + 법률 reviewer | 137 Markdown 내부 `sub_rule_id` branch의 executable structured projection | +| `R/registry/review/review_policy_registry.yml` | `NEW` | legal policy owner | base/tag/runtime trigger→required receipt·READY eligibility | +| `R/registry/temporal/inheritance_reserved_share.yml` | `NEW` | authority/legal owner | 상속개시일에 따른 유류분 remedy·renderer·interest branch | +| `R/registry/drafting/forbidden_relief_rules.yml` | `NEW` | legal/drafting owner | 강제 사죄·사과광고 금지와 허용 정정·반론 branch | +| `R/registry/drafting/counter_performance_rules.yml` | `NEW` | legal/drafting owner | 동시이행·상환이행·제공·수령거절과 CE-01 결속 | +| `R/registry/drafting/procedural_declaration_rules.yml` | `NEW` | legal/drafting owner | 소장 송달 의사표시의 actor·권한·도달·효과상태 | +| `R/registry/corpus/requirement_fact_scope.yml` | `NEW` | corpus curator + legal reviewer | collection/query/tie-break/crosswalk/replay named section의 유일 retrieval config | +| `R/routing/actio_pauliana_route_registry.yml` | `NEW` | ACTIO legal owner | 6 route, operand·피고·recipient·renderer 선택 | +| `R/routing/actio_pauliana_mortgage_route.yml` | `NEW` | ACTIO legal owner | 기존 담보부 양도와 사해적 담보설정·말소·배당 route 분리 | +| `R/validators/actio_value_compensation_invariants.yml` | `NEW` | ACTIO validator owner | 두 기간, 수익자/전득자, 원상회복 recipient, 피보전채권·공동담보·이익 3-cap invariant | + +Markdown과 legal corpus는 사실 source가 아니다. 위 structured row도 S2_10의 claim option 또는 Stage 1 사실·증거를 수정할 수 없다. + +### 1.5 deterministic calculation YAML 17개 + +아래 파일은 모두 `NEW`, writer는 calculation/legal owner, consumer는 S2_20 calculator와 S2_40 independent validator다. Python code가 아니라 formula·operand·unit·authority/law-value token·failure code를 가진 선언형 data다. + +| exact path | 역할 | +|---|---| +| `R/registry/calculations/CE-01_interest_delay.yml` | obligation·피고·event별 이자/지연손해금 rate timeline | +| `R/registry/calculations/CE-02_allocation_setoff_balance.yml` | 변제충당·상계·잔액 | +| `R/registry/calculations/CE-03_limitation_deadline.yml` | 소멸시효·제척기간·출소기간·urgency | +| `R/registry/calculations/CE-04_valuation.yml` | 사용이익·목적물 가치 | +| `R/registry/calculations/CE-05_personal_injury.yml` | 일실수입·치료비·과실상계 | +| `R/registry/calculations/CE-06_construction_defect.yml` | 기성·추가공사·하자보수·감액 | +| `R/registry/calculations/CE-07_lease_use_gain.yml` | 임대차 공제·사용이익 | +| `R/registry/calculations/CE-08_inheritance_reserved_share.yml` | 유류분 가액·temporal remedy branch | +| `R/registry/calculations/CE-09_wage_severance.yml` | 임금·수당·퇴직금 | +| `R/registry/calculations/CE-10_actio_insolvency_value.yml` | 피보전채권·공동담보·수익자/전득자 이익 3-cap | +| `R/registry/calculations/CE-11_distribution_share_division.yml` | 배당·지분·분할금액 | +| `R/registry/calculations/CE-12_insurance.yml` | 보험가액·중복보험·공제 | +| `R/registry/calculations/CE-13_court_value_cost.yml` | 소가·인지·송달비용과 병합·부대청구 branch | +| `R/registry/calculations/CE-R1_ip_damage.yml` | 지식재산 손해액 | +| `R/registry/calculations/CE-R2_org_liquidation.yml` | 회사·조합 청산·분배 | +| `R/registry/calculations/CE-R3_transport_maritime.yml` | 운송·해상 손해·책임제한 | +| `R/registry/calculations/CE-R4_financial_instrument.yml` | 금융상품·계좌·정산 | + +output-affecting numeral에는 exact `law_value_id`, effective interval와 authority release가 필요하다. LLM 산술과 전역 최고이율식은 금지한다. + +### 1.6 renderer descriptor 6개 + +S2_20은 renderer를 실행하지 않고 exact branch를 선택·봉인한다. 실제 closed render는 S2_40이 수행한다. + +| exact path | 조치 | S2_20 역할 | +|---|---|---| +| `R/renderers/R01_money_payment.yml` | `NEW` | 금전지급 typed slot·branch 선택 | +| `R/renderers/R02_delivery_possession.yml` | `NEW` | 인도·명도·퇴거 branch 선택 | +| `R/renderers/R03_declaration_registry.yml` | `NEW` | 이전·말소·회복·경정 등기 branch 선택 | +| `R/renderers/R04_special_nonmoney_performance.yml` | `NEW` | 특별 비금전 이행 branch 선택 | +| `R/renderers/R05_declaratory.yml` | `NEW` | 확인청구·소의 이익 branch 선택 | +| `R/renderers/R06_constitutive_judgment_challenge.yml` | `NEW` | 형성·불복·공유물분할 method branch 선택 | + +### 1.7 build·release helper + +| exact path pair | 조치 | writer/consumer | 역할 | +|---|---|---|---| +| `R/offline_build/compile_case_type_registry.py/.txt` | `NEW` | build owner / registry reviewer | `P/case_kinds.md`의 제외범주 반영 137 row→canonical ID·ordinal·label·predicate compile | +| `R/offline_build/compile_relief_rule_projection.py/.txt` | `NEW` | build owner / legal reviewer | 137 Markdown→structured sub-rule projection, semantic diff·hash receipt | +| `R/offline_build/build_authority_law_value_release.py/.txt` | `NEW` | authority/release owner | approved capture·temporal rule·law-value dependency seal | +| `R/offline_build/build_corpus_snapshot.py/.txt` | `NEW` | corpus operator | source→heading-aware chunk·crosswalk·snapshot/index build receipt | +| `R/offline_build/build_release_manifests.py/.txt` | `NEW` | release operator | module/case-type/authority/corpus/parent release의 non-cyclic 조립 | +| `R/release_ops/release_validator.py/.txt` | `NEW` | release operator | physical hash·schema·dependency DAG·semantic receipt·137 coverage 검증 | +| `R/release_ops/canary_rollback.py/.txt` | `NEW` | release operator | subset canary·rollback digest·tamper test; 사건 runtime dependency 아님 | + +모든 helper는 build/release 전용이며 S2_20 inline code가 import하지 않는다. `.py/.txt`는 byte-identical이어야 한다. + +### 1.8 최소 offline test와 fixture + +| exact path pair | 조치 | 검증축 | +|---|---|---| +| `R/tests/s2_20/test_agent_and_inline_parity.py/.txt` | `NEW` | exactly-one `run_code`, task DAG, authoring/projection/code/mirror/receipt parity | +| `R/tests/s2_20/test_option_group_and_binding.py/.txt` | `NEW` | option exact partition, stable group, C25/C26 zero/multi/hash/conflict | +| `R/tests/s2_20/test_calculation_and_reports.py/.txt` | `NEW` | CE dispatch·temporal value·missing operand·ACTIO 3-cap·party/exhibit/filing report | +| `R/tests/s2_20/test_retrieval_and_pack.py/.txt` | `NEW` | exact metadata filter, score/tie-break, replay, timeout, injection, slot crosswalk | +| `R/tests/s2_20/test_plan_publish_and_release.py/.txt` | `NEW` | single writer, read-back, partial write, barrier-last, release/hash closure | +| `R/tests/s2_20/test_inline_output_schema.py/.txt` | `NEW` | inline output JSON Schema, upstream self-hash, trust-anchor path, CAS-before-write와 partial-write manifest | +| `R/tests/s2_20/test_offline_compilers.py/.txt` | `NEW` | registry/rule/authority/corpus compiler, legal-review path family와 deterministic build contract | +| `R/tests/s2_20/test_regression_manifest_closure.py/.txt` | `NEW` | global/local fixture·test source hash와 logical family closure | + +배포 root `R/tests/fixtures/s2_20/`에는 다음 9개를 둔다. + +| exact filename | 단일 oracle | +|---|---| +| `minimal_supported_portfolio.json` | 정상 single-group C20→C35 | +| `mixed_option_dispositions.json` | selected/alternative/excluded/deferred exact partition | +| `case_type_rule_binding_matrix.json` | CLAIM·GENERIC·DOCTRINE_TOPIC/GROUP와 sub-rule exact binding | +| `case_type_zero_multi_hash_mutations.json` | zero/multi/fuzzy/hash mismatch closed code | +| `calculation_temporal_boundaries.json` | CE-01/03/08/10/13 적용시점·missing operand | +| `actio_route_cap_matrix.json` | ACTIO 6 route·피고·recipient·두 기간·3-cap | +| `retrieval_replay_injection.json` | exact filter·동점순서·timeout·embedded instruction data 처리 | +| `plan_publish_barrier_failure.json` | partial write/read-back mismatch·early barrier 거부 | +| `regression_manifest.json` | 앞 8개 payload path/hash와 아래 17개 logical `fixture_case_id`를 1:1 mapping하고, 각 case별 mutation selector·expected issue/route·oracle hash를 봉인 | + +global `R/tests/fixtures/regression_manifest.json`도 S2_20 test·fixture hash를 추가하도록 `UPDATE-RESEAL`한다. offline PASS는 live Weaviate, 법률 correctness 또는 production admission을 증명하지 않는다. + +8개 payload 파일은 container일 수 있으나 §11.2의 17개 logical fixture family를 축약하지 않는다. 각 family는 고유 `fixture_case_id`와 최소 1개의 독립 mutation row를 가지며 `regression_manifest.json`에서 정확히 한 payload selector와 expected code/route에 결속되어야 한다. + +--- + +## 2. 외부 법률·corpus 고정 자산 + +### 2.1 청구취지 규칙 137개와 compile source + +| exact path/family | 조치 | writer | consumer·경계 | +|---|---|---|---| +| `R/rules/relief/.md` | `EXTERNAL-PENDING`, 정확히 137개 | 대한민국 변호사/법률 content owner | C26와 P31 source. 문서별 `sub_rule_id`, authority/provenance, required slot, non-claim disposition을 포함 | +| `P/case_kinds.md` | `REFERENCE-ONLY` | 기존 catalog owner | compile source 단 하나; runtime 직접 read 금지 | +| `M/청구취지작성규칙문서예시/*.md` | `REFERENCE-ONLY` | 기존 자료 | 구조 seed일 뿐 137 coverage 또는 approved runtime asset 아님 | +| `R/procedural_rules/소가_산정_규칙.md` | `EXTERNAL-PENDING` | 대한민국 변호사 | CE-13 사람이 읽는 검수 source; runtime parsing 금지 | +| `R/procedural_rules/민사소송_등_인지규칙.md` | `EXTERNAL-PENDING` | 대한민국 변호사 | 인지·송달비용 검수 source; runtime parsing 금지 | + +runtime은 directory scan이나 filename match를 하지 않고 `case_type_rule_registry.yml`의 exact path/hash와 `case_type_relief_rules.yml`의 selected branch만 사용한다. + +### 2.2 authority·law-value + +| exact path/family | 조치 | writer | S2_20 사용 | +|---|---|---|---| +| `R/registry/authority/authority_registry.yml` | `REUSE-AS-SEED / EXTERNAL-PENDING` | authority curator + 대한민국 변호사 | 승인 proposition·temporal scope·official locator; profile은 authority 대체 불가 | +| `R/law_values/general_law_values.yml` | `REUSE-AS-SEED / EXTERNAL-PENDING` | authority/calculation owner | 이율·상한·기간 등의 versioned token | +| `R/law_values/court_fee_values.yml` | `REUSE-AS-SEED / EXTERNAL-PENDING` | authority/calculation owner | 소가·인지·송달비용의 시점별 token | +| `R/authority/source_captures/.json` | `EXTERNAL-PENDING`, `N_authority` | authority curator | official locator·raw hash·retrieved_at·효력/부칙/후속취급 | +| `R/manifest/authority_release.json` | `UPDATE-RESEAL` | release operator | 사용 가능한 registry/capture/law-value exact set/hash seal | + +현행 authority release가 `DEV_UNAVAILABLE`이면 구조가 존재해도 S2_20 production 실행을 승인하지 않는다. S2_20 runtime은 인터넷에서 법령·판례를 보충하지 않는다. + +### 2.3 requirement-fact corpus·Weaviate + +| exact path/family | 조치 | writer | consumer·경계 | +|---|---|---|---| +| `R/corpus/raw//.md` | `EXTERNAL-PENDING`, `N_source_doc` | corpus curator + 대한민국 변호사 | 승인 요건사실 raw canonical source; runtime은 Weaviate snapshot을 조회 | +| `R/corpus/source_manifest.json` | `NEW` | corpus build | source ID·case type/sub-rule·hash·approval·authority | +| `R/corpus/chunk_manifest.jsonl` | `NEW` | corpus build | heading-aware chunk의 section/offset·role·element·hash | +| `R/corpus/weaviate_collection.schema.json` | `NEW` | corpus/Weaviate operator | object metadata, tenant, index field와 closed filter schema | +| `R/corpus/build_receipt.json` | `NEW` | corpus/Weaviate operator | chunk·embedding/reranker/index config와 snapshot build evidence | +| `R/manifest/corpus_release.json` | `UPDATE-RESEAL` | release operator | collection/tenant/schema/snapshot/chunk/crosswalk exact set/hash seal | +| `M/요건사실론문서raw/*.md` | `REFERENCE-ONLY` | 기존 자료 | canonical corpus로 승인·이관하기 전 seed; runtime 직접 load 금지 | + +corpus coverage가 없으면 인접 사건종류나 인접 `sub_rule_id`를 자동 대체하지 않는다. legal content gap은 해당 claim/group에 `LAWYER_REVIEW_REQUIRED`를 남기고, transport·snapshot·replay 무결성 실패는 `RETRIEVAL_TECHNICAL_INCOMPLETE`로 분리한다. + +### 2.4 ACTIO migration source + +| exact path | 조치 | 역할 | +|---|---|---| +| `M/사해행위취소소송관련기존자산/`의 지정 10종 | `REFERENCE-ONLY` | ACTIO route·계산·항변 proposition의 migration source; runtime load 0 | +| `R/migration/actio_assets_receipt.json` | `NEW` | raw path/NFC basename/hash, 추출 proposition, official authority 교정/drop 이유, canonical target hash·reviewer | +| `R/migration/legacy_asset_disposition.yml` | `NEW` | legacy 16종 `MIGRATE\|REBUILD\|DROP` 비차단 audit; runtime fallback 0 | +| `R/registry/regression/finding_fixture_map.yml` | `NEW` | discrepancy/eval finding→invariant→fixture mapping; runtime 법리 source 아님 | + +지정 source 사용은 허용된 migration provenance일 뿐 구 `v.0~v.3` YAML 지시를 상속한다는 뜻이 아니다. + +--- + +## 3. 재사용 upstream·shared release 자산 + +### 3.1 S2_00·S2_10 runtime 계약 재사용 + +| exact path/family | 조치 | S2_20 역할 | +|---|---|---| +| `R/schemas/context.schema.json` 및 S2_00 context outputs | `REUSE-BOUND` | cluster slice·party/object/slot·base ledger 입력 검증 | +| `R/schemas/s2_10.schema.json` | `REUSE-BOUND` | `s2_10_publish_status`와 immutable DomainVerdict/receipt 검증 | +| `R/agent_scripts/Stage_2_S2_10.yml`, `R/deployment/stage2_s2_10_llm_binding.yml` | `REUSE-BOUND` | 법률판단 producer identity 확인; S2_20이 LLM을 재호출하지 않음 | +| `R/profiles/overlays/ACTIO-*.yml` 5개 | `REUSE-BOUND-UPSTREAM` | S2_10 output/release에 봉인된 activation ID·hash만 확인; legal content 직접 reroute 금지 | +| `R/registry/substantive/*.yml`, `R/profiles/crosscut/*.yml`, `R/profiles/special_law/*.yml` | `REUSE-BOUND-UPSTREAM` | S2_10 producer closure; S2_20 direct context load 0 | + +S2_20은 Stage 1 raw 파일을 다시 읽지 않는다. S2_00의 immutable context와 S2_10 canonical parts만 소비한다. + +### 3.2 shared manifest·coverage update + +| exact path | 조치 | writer | 변경 내용 | +|---|---|---|---| +| `R/manifest/module_manifest.json` | `UPDATE-RESEAL` | release build owner | S2_20 workflow/schema/registry/calculator/renderer/builder/test의 path·hash·IO·authority·dependency·status 추가 | +| `R/manifest/case_type_coverage.json` | `UPDATE-RESEAL` | legal/release owner | 137 row별 rule·renderer·corpus·CE/SL·review·sign-off와 subset/full scope | +| `R/manifest/authority_release.json` | `UPDATE-RESEAL` | authority/release owner | approved temporal authority와 law-value closure | +| `R/manifest/corpus_release.json` | `UPDATE-RESEAL` | corpus/release owner | approved snapshot·crosswalk·index closure | +| `R/manifest/stage2_release.json` | `UPDATE-RESEAL` | release operator | 위 manifest와 S2_20 workflow/schema/registry/test closure를 묶는 parent oracle | +| `R/tests/fixtures/regression_manifest.json` | `UPDATE-RESEAL` | test owner | S2_20 test/fixture exact path/hash/expected code 추가 | + +directory 존재나 정적 file count는 release 증거가 아니다. `case_type_coverage.json`이 subset이면 canary는 가능하지만 `FULL_137_PRODUCTION_READY`로 표시하지 않는다. + +### 3.3 parent hash 변경에 따른 cross-stage 재봉인 + +S2_20 module·authority·corpus가 parent `stage2_release.json`을 바꾸므로 아래 파일은 알고리즘 변경 없이 hash 의존값을 재생성한다. + +| exact path | 조치 | 이유 | +|---|---|---| +| `M/Stage_2_S2_00_v.2.yml` | `UPDATE-RESEAL` | inline `expected_stage2_release_sha256` 교체 | +| `R/agent_scripts/Stage_2_S2_00.yml` | `UPDATE-RESEAL` | 변경 authoring 재투영 | +| `R/runtime/s2_00_ingress.py/.txt` | `UPDATE-RESEAL` | 새 inline code exact mirror | +| `R/manifest/s2_00_inline_code_receipt.json` | `UPDATE-RESEAL` | 새 Agent/code/release hash 기록 | +| `R/schemas/deployment.schema.json` | `UPDATE-MIGRATE-RESEAL` | singleton instance를 `stage2_code_executor_binding.v3`/`stage_bindings[]` 계약으로 이행 | +| `R/deployment/stage2_code_executor_binding.yml` | `UPDATE-MIGRATE-RESEAL` | S2_00 row 보존과 S2_20 row 추가를 한 binding에서 결속 | +| `R/deployment/stage2_loader_binding.yml` | `UPDATE-RESEAL` | reference-only superseded pointer의 target schema를 v3로 바꾸되 legacy loader runtime 권한은 계속 false | +| `R/offline_build/build_s2_00_inline_projection.py/.txt` | `UPDATE-RESEAL` | v3 binding의 S2_00 row 검사와 새 parent hash를 반영하되 S2_00 ingress 알고리즘은 변경하지 않음 | +| `R/tests/s2_00/test_inline_code_parity.py/.txt` | `UPDATE-RESEAL` | singleton top-level 가정을 stage별 S2_00 row 선택으로 이행 | +| `R/tests/s2_00/test_schema_hash_and_signals.py/.txt` | `UPDATE-RESEAL` | v3 binding schema·S2_00 row·parent hash 검증 추가 | +| `R/tests/s2_00/test_cluster_bundle_compile.py/.txt` | `UPDATE-RESEAL` | singleton top-level binding read를 `stage_bindings[stage_id=S2_00]` exact-one 선택으로 이행 | +| `R/manifest/s2_10_release.json` | `UPDATE-RESEAL` | 새 parent raw hash를 child release에 결속 | +| `R/manifest/s2_10_agent_receipt.json` | `UPDATE-RESEAL` | 새 child/parent chain 기록 | +| `R/manifest/s2_10_platform_adapter_receipt.json` | `UPDATE-RESEAL-PENDING` | 새 release/binding hash, 상태는 external evidence 전 `PENDING` | +| `R/manifest/s2_10_model_benchmark_receipt.json` | `UPDATE-RESEAL-PENDING` | 새 child hash, 실측 전 `PENDING` 유지 | +| `R/manifest/s2_10_legal_review_receipt.json` | `UPDATE-RESEAL-PENDING` | 새 child hash, 변호사 검수 전 `PENDING` 유지 | +| `R/manifest/stage2_deterministic_admission_receipt.json` | `NEW-RESEAL-PENDING` | parent와 실제 present deterministic stage의 Agent/code/outer receipt hash를 detached 결속; signer/live evidence 전 PENDING | + +S2_10 Agent·prompt·LLM binding bytes는 S2_20 때문에 바꾸지 않는다. `domain_verdict.schema.json` facade가 현행 S2_10 definition을 참조하므로 S2_10 output schema의 이중 정본이나 불필요한 prompt reseal을 만들지 않는다. + +### 3.4 non-cyclic hash order + +```text +legal/corpus source + S2_20 schema/registry/workflow/test + | + v + module + coverage + authority + corpus manifests + | + v + parent stage2_release raw SHA-256 + | + +--> S2_00 authoring constant -> projection/code/receipt + | + +--> S2_10 child release + dependent pending receipts + | + +--> S2_20 authoring constant -> projection/code/receipt + | + v + shared stage2_code_executor_binding +``` + +parent release는 S2_20 Agent/code/receipt/executor-binding hash를 자기 내부 closure에 역봉인하지 않는다. 대신 외부 executor binding이 parent raw hash와 이 실행 bytes를 결속한다. 이 순서를 깨고 서로의 최종 hash를 양방향으로 넣지 않는다. + +이는 `stage_2_optimal_update_strategy_v.5-1.md`의 release-bound 원칙을 구현하는 **비순환 계층화 규칙**이다. parent raw closure에는 schema·workflow·registry·test·법률/corpus manifest만 포함한다. parent hash를 내장하는 S2_00/S2_20 Agent·inline receipt·executor binding은 detached admission layer가 결속한다. production scope에서 요구되는 deterministic stage ID는 고정 문자열 3개를 미리 성공으로 선언하지 않고 `present_deterministic_stage_ids`로 열거하며, 해당 release에 실제 구현·검증된 stage receipt를 전부 요구한다. + +--- + +## 4. runtime IO — 고정 자산 수 제외 + +### 4.1 입력 + +| runtime path | writer | S2_20 소비 조건 | +|---|---|---| +| `stage2_control/s2_20_request.json` | outer orchestrator/AgentBackend | `deployment.schema.json#/$defs/s2_20_request`, exact `run_binding_digest`·request/output root·outer evidence echo; 허용된 유일 outer request `Q` | +| `O/control/s2_20_outer_execution_receipt.json` | AgentBackend | `deployment.schema.json#/$defs/s2_20_execution_receipt`; host CAS, actual Agent/code/binding hash, MCP outer result, user/workspace isolation evidence를 결속하며 inline code는 쓰지 않음 | +| `O/map_s2_10/s2_10_publish_status.json` | native S2_10 adapter | `COMPLETE/TO_S2_20`, exact parent/child/run hash | +| `O/map_s2_10/domain_verdicts/.json` | native S2_10 adapter | canonical immutable DomainVerdict exact coverage/hash | +| `O/map_s2_10/issue_patches/.json` | native S2_10 adapter | option별 issue patch exact coverage/hash | +| `O/map_s2_10/assumption_parts/.json` | native S2_10 adapter | assumption lineage 보존 | +| `O/map_s2_10/item_receipts/.json` | native S2_10 adapter | schema/ref/ID/read-back PASS | +| `O/map_s2_10/wave_receipts/wave-.json` | native S2_10 adapter | 모든 terminal wave의 `WAVE_COMPLETE`, covered cluster exact set | +| `O/context/case_context.json` | S2_00 | barrier가 결속한 exact path/schema/hash만 사용 | +| `O/context/evidence_inventory.json` | S2_00 | evidence ID/source hash 보존 | +| `O/context/object_registry.json` | S2_00 | object lineage 보존 | +| `O/context/party_and_title_context.json` | S2_00 | party/title/identity state 보존 | +| `O/context/slot_crosswalk.json` | S2_00 | Stage 1 slot ref 보존 | +| `O/context/cluster_plan.json` | S2_00 | executable cluster/SCC/wave identity | +| `O/context/cluster_slices/.json` | S2_00 | plan이 열거한 exact set; scan 금지 | +| `O/context/bundle_plan.json` | S2_00 | legal-context와 S2_10 producer binding refs/hash | +| `O/review/issue_ledger.base.json` | S2_00 | 모든 row 보존; S2_20은 append-only plan lineage만 작성 | + +### 4.2 출력과 single writer + +다음은 모두 S2_20만 쓰고 S2_30·S2_40이 소비한다. + +```text +O/plan/canonical_relief_plan.json +O/plan/claim_groups.json +O/plan/case_type_bindings.json +O/plan/issue_ledger.plan.json +O/plan/rule_context_packs/.json +O/plan/prompt_segments//P31_rule_and_pack.json +O/plan/prompt_segments//P32_common_authority.json +O/plan/query_plans/.json +O/plan/retrieval_results/.json +O/plan/retrieval_receipts/.json +O/plan/requirement_fact_packs/.json +O/plan/element_evidence_gap/.json +O/plan/calculation_receipts/.json +O/plan/calculation_report.json +O/plan/filing_metrics.json +O/plan/exhibit_register.json +O/plan/option_conservation_report.json +O/plan/party_completeness_report.json +O/plan/group_slices/.json +O/plan/plan_publish_status.json +``` + +모든 artifact를 canonical UTF-8 bytes로 기록하고 read-back hash를 확인한 뒤 `plan_publish_status.json`을 마지막에 쓴다. barrier 이전에는 S2_30 소비가 0이다. retrieval/rule/corpus가 Stage 1 사실 또는 frozen option을 변경해서는 안 된다. + +일관된 정상 artifact set을 만들 수 없는 기술 실패는 정상 tree와 분리된 정확한 `O/plan/technical_diagnostic.json`을 `relief_plan.schema.json#/$defs/technical_diagnostic`로 쓰고, 마지막에 `consumer_authorized=false`인 status barrier만 쓴다. 정상/진단 어느 경우든 `plan_publish_status.json`의 `artifact_rows[]`가 각 physical output의 유일 manifest이며 path·schema ref·raw hash·size를 정확히 한 번 열거한다. + +--- + +## 5. 생성·검증·봉인 순서 + +1. S2_20 SOW와 본 inventory에서 IO·writer·error/status vocabulary를 고정한다. +2. schema bundle과 workflow를 먼저 작성하고 request·execution receipt·barrier·diagnostic `$defs`, 현행 context/S2_10 schema refs를 exact hash로 결속한다. +3. signature projection·option disposition·party-set·calculation activation과 case type·rule·calculation·renderer·ACTIO·review·retrieval declarative YAML을 작성한다. +4. 법률가 승인 137 rules, authority/law values와 corpus snapshot을 별도 workstream에서 확보한다. 미확보 상태를 synthetic PASS로 채우지 않는다. +5. pure-core oracle과 test/fixture를 작성한 뒤 full inline code로 완전 전개한다. +6. authoring `Stage_2_S2_20.yml`을 작성하고 builder로 projection·mirror·inline receipt를 생성한다. +7. module/coverage/authority/corpus manifest를 검증하고 parent release raw hash를 먼저 확정한다. +8. parent hash를 S2_00·S2_10 child chain과 S2_20 authoring에 순서대로 반영하고 projection·receipt를 재생성한다. +9. shared Code Executor binding이 S2_00/S2_20 exact bytes와 stage별 egress를 결속하도록 재봉인한다. +10. unique-key YAML/JSON, JSON Schema, Python compile/AST, `.py/.txt`, authoring↔projection, option conservation, C25, CE, retrieval replay, barrier, manifest/hash closure를 offline 검증한다. +11. 별도로 live `run_code`, workspace isolation, binary preservation, exact Weaviate egress/credential, timeout/size, same-binding idempotence와 route integration을 실증한다. +12. authority·137 rule·corpus semantic review와 대표사건 대한민국 변호사 검수를 마친 뒤에만 production admission을 판단한다. + +--- + +## 6. 완료 상태와 금지된 과대주장 + +| 상태 | 의미 | +|---|---| +| `IMPLEMENTED_OFFLINE_VERIFIED` | Agent/schema/registry/core/test와 non-cyclic release hash가 offline PASS | +| `LIVE_CODE_EXECUTOR_AND_WEAVIATE_PENDING` | live MCP·workspace·egress·credential·timeout/replay 미실증 | +| `PENDING_LEGAL_CORPUS_ADMISSION` | authority, 137 rule, corpus 및 fixture의 법률가 승인 미완료 | +| `PRODUCTION_ADMITTED` | signed release, live evidence와 대한민국 변호사 approval까지 별도 충족 | + +다음은 완료 증거가 아니다. + +- S2_20 YAML·Python 파일이 존재한다는 사실 +- 137 case type 이름만 registry에 들어간 사실 +- `case_type_coverage.json`의 137행만으로 승인된 rule Markdown·branch가 있다고 보는 것(현 snapshot은 0/137, branch 0) +- 현재 5개 규칙 예시 또는 6개 raw 요건사실 문서 +- offline fixture PASS만으로 live Weaviate 재현성·법률정확성을 주장하는 것 +- `PENDING` receipt에 예상값·가짜 signature·가짜 benchmark를 미리 기입하는 것 +- 구 Stage 2 v.0–v.3 YAML이나 legacy module을 fallback으로 읽는 것 + +S2_20 구현의 최소 성공은 option silent loss 0, exact case/sub-rule binding, authority-bound deterministic calculation, verified retrieval pack, stable claim groups와 barrier-last frozen ReliefPlan을 재현하는 것이다. 법률·corpus gap이 있어도 일관된 review draft를 보존하되 clean READY로 승격하지 않는다. diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Stage_2_S2_00_v.2.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Stage_2_S2_00_v.2.yml index 1b6cc077..819cc013 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Stage_2_S2_00_v.2.yml +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Stage_2_S2_00_v.2.yml @@ -192,7 +192,7 @@ Agent: # literal placeholders in the offline parity mirror and its unit tests. INLINE_USER_HASH = "{{__user_hash__}}" INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}" - EXPECTED_STAGE2_RELEASE_SHA256 = "0f21af3ddca538d9b6a5853dac23222c0b632d0e573de371a8de2709e691699d" + EXPECTED_STAGE2_RELEASE_SHA256 = "70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92" INLINE_REQUEST_PATH = "stage2_control/s2_00_request.json" INLINE_STAGE2_ASSET_ROOT = "Default_Agent/Stage_2_Clean" INLINE_STAGE2_RELEASE_PATH = ( diff --git a/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Stage_2_S2_20.yml b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Stage_2_S2_20.yml new file mode 100644 index 00000000..a4d3b5c0 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/2. Stage_2/Stage_2_S2_20.yml @@ -0,0 +1,3975 @@ +Agent: + name: Stage_2_S2_20 + version: "1.0.0" + description: >- + S2_00 context와 S2_10 canonical DomainVerdict를 보존하면서 option portfolio, + exact case/rule binding, deterministic calculation, requirement-fact pack과 + frozen ReliefPlan을 생성하는 S2_20 NON-LLM-DETERMINISTIC Agent. + metadata: + workflow_id: S2_20 + execution_class: NON-LLM-DETERMINISTIC + execution_authority: MCP_CODE_EXECUTOR_INLINE + task_constitution: S2_20_EXACTLY_ONE_INLINE_RUN_CODE_V1 + implementation_status: IMPLEMENTED_OFFLINE_VERIFIED_LIVE_AND_LEGAL_ADMISSION_PENDING + legacy_stage2_v0_v3_runtime_dependency_count: 0 + Stages: + - name: S2_20 + description: >- + 단일 Code Executor 호출 내부에서 + (C20 || C21) -> C25 -> C26 -> (C22 || (C27 -> C28 -> C29)) + -> C30 -> C35를 실행하고 plan_publish_status를 마지막에 발행한다. + prevs: [] + nexts: [] + skip_confirm: true + tools: + mcpServers: + localdocs: + type: streamable-http + url: http://mcp-localdocs:8012/mcp + code-executor: + type: streamable-http + url: https://code-executor.mcp.eroomai.com/mcp + tasks: + - task_name: Task_S2_20_deterministic_relief_plan + description: >- + release-bound request와 immutable S2_00/S2_10 입력을 두 번 읽어 + 검증하고, 고정 registry만 사용하여 frozen plan을 status-last로 발행한다. + mcp: code-executor + tool_name: run_code + parameters: + language: python + requirements: "httpx==0.28.1" + network: agent-network + timeout: 300 + code: |- + #!/usr/bin/env python3 + """Release-bound deterministic S2_20 ReliefPlan executor. + + The code is self-contained. It does not call an LLM, import workspace + Python, execute shell commands, install packages at runtime, discover + files by directory scan, or mutate S2_10 legal judgments. + """ + + from __future__ import annotations + + import base64 + import binascii + from concurrent.futures import ThreadPoolExecutor + import contextlib + from datetime import date + from decimal import Decimal, InvalidOperation, ROUND_DOWN, ROUND_HALF_UP, ROUND_UP + import hashlib + import io + import itertools + import json + import re + import sys + import unicodedata + from pathlib import PurePosixPath + from typing import Any, Iterable, Mapping, Sequence + + + WORKFLOW_ID = "S2_20" + ALGORITHM_VERSION = "s2_20_relief_plan/1.0.0" + EXPECTED_STAGE2_RELEASE_SHA256 = "70ef317cc0c557e44619b6cd6b4ad567e02e73271a36a0bbc93b748ae3f34f92" + LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp" + WEAVIATE_MCP_URL = "https://weaviate.eroomai.com/mcp" + MCP_PROTOCOL_VERSION = "2025-03-26" + INLINE_USER_HASH = "{{__user_hash__}}" + INLINE_WORKSPACE_HASH = "{{__workspace_hash__}}" + REQUEST_PATH = "stage2_control/s2_20_request.json" + ASSET_ROOT = "Default_Agent/Stage_2_Clean" + MAX_FILE_BYTES = 32 * 1024 * 1024 + MAX_TOTAL_BYTES = 256 * 1024 * 1024 + HEX64 = re.compile(r"^[a-f0-9]{64}$") + CLUSTER_ID = re.compile(r"^CL-[a-f0-9]{24}$") + IDENT = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$") + RELIEF_RULE_PATH = re.compile(r"^rules/relief/CT-[0-9]{3}\.md$") + LEGAL_REVIEW_PATH = re.compile(r"^legal_review/[A-Za-z0-9][A-Za-z0-9._-]{0,191}\.json$") + DISPOSITIONS = ("selected", "alternative", "excluded", "deferred") + TARGET_SIGNATURE_FIELDS = ( + "claim_family", "legal_effect", "performance_kind", "object_kind", + "party_role_pattern", "source_obligation_kind", "remedy_mode", + "registry_action", "procedural_posture", "special_statute_tags", + ) + + + class S220Error(Exception): + def __init__(self, code: str, message: str, *, details: Any | None = None) -> None: + super().__init__(message) + self.code = code + self.message = message + self.details = details + + def as_dict(self) -> dict[str, Any]: + row: dict[str, Any] = {"code": self.code, "message": self.message} + if self.details is not None: + row["details"] = self.details + return row + + + def nfc(value: str) -> str: + return unicodedata.normalize("NFC", value) + + + def no_duplicates(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise S220Error("DUPLICATE_JSON_KEY", f"duplicate JSON key: {key}") + result[key] = value + return result + + + def load_json(raw: bytes, *, label: str) -> Any: + if len(raw) > MAX_FILE_BYTES: + raise S220Error("SOURCE_SIZE_LIMIT", f"file exceeds limit: {label}") + try: + text = raw.decode("utf-8", errors="strict") + value = json.loads( + text, + object_pairs_hook=no_duplicates, + parse_constant=lambda token: (_ for _ in ()).throw( + S220Error("NON_FINITE_NUMBER", f"non-finite number: {token}") + ), + ) + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise S220Error("JSON_PARSE_ERROR", f"strict JSON parse failed: {label}") from exc + return value + + + def canonical_bytes(value: Any) -> bytes: + return ( + json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) + + "\n" + ).encode("utf-8") + + + def digest(value: Any) -> str: + payload = value if isinstance(value, bytes) else canonical_bytes(value) + return hashlib.sha256(payload).hexdigest() + + + def safe_path(value: Any, *, code: str = "PATH_INVALID") -> str: + if not isinstance(value, str) or not value or "\x00" in value: + raise S220Error(code, "path must be a non-empty string") + if value.startswith("/") or "\\" in value: + raise S220Error(code, "absolute or backslash path is forbidden") + normalized = PurePosixPath(value) + if any(part in {"", ".", ".."} for part in normalized.parts): + raise S220Error(code, "path traversal or ambiguous component") + return normalized.as_posix() + + + def join_path(*parts: str) -> str: + return safe_path("/".join(part.strip("/") for part in parts if part)) + + + def require_hex(value: Any, *, code: str) -> str: + if not isinstance(value, str) or HEX64.fullmatch(value) is None: + raise S220Error(code, "expected lower-case SHA-256") + return value + + + def stable_id(prefix: str, *parts: Any) -> str: + body = digest([nfc(str(part)) for part in parts])[:24] + return f"{prefix}-{body}" + + + def _parse_mcp_payload(raw: bytes, expected_id: int) -> Mapping[str, Any]: + content_type_json = raw.lstrip().startswith(b"{") + candidates: list[Any] = [] + if content_type_json: + candidates = [load_json(raw, label="mcp-response")] + else: + data_lines: list[str] = [] + events: list[bytes] = [] + for line in raw.decode("utf-8", errors="strict").splitlines(): + if not line: + if data_lines: + events.append("\n".join(data_lines).encode("utf-8")) + data_lines = [] + continue + if line.startswith((":", "event:", "id:", "retry:")): + continue + if not line.startswith("data:"): + raise S220Error("MCP_SSE_SHAPE", "unexpected SSE line") + data_lines.append(line[5:].lstrip()) + if data_lines: + events.append("\n".join(data_lines).encode("utf-8")) + candidates = [load_json(event, label="mcp-sse-event") for event in events] + matches = [row for row in candidates if isinstance(row, dict) and row.get("id") == expected_id] + if len(matches) != 1: + raise S220Error("MCP_RESPONSE_ID", "exactly one matching JSON-RPC result required") + row = matches[0] + if row.get("jsonrpc") != "2.0" or row.get("error") is not None: + raise S220Error("MCP_JSONRPC_ERROR", "MCP returned an invalid/error response") + if not isinstance(row.get("result"), dict): + raise S220Error("MCP_RESULT_SHAPE", "MCP result must be an object") + return row + + + def _tool_text(result: Mapping[str, Any], tool: str) -> str: + if result.get("isError") is True: + rendered = str(result.get("content", "")) + code = "LOCALDOCS_NOT_FOUND" if "not found" in rendered.lower() else "MCP_TOOL_ERROR" + raise S220Error(code, f"{tool} returned isError=true") + content = result.get("content") + if not isinstance(content, list) or len(content) != 1: + raise S220Error("MCP_CONTENT_CARDINALITY", "one content block required") + block = content[0] + if not isinstance(block, dict) or block.get("type") != "text" or not isinstance(block.get("text"), str): + raise S220Error("MCP_CONTENT_SHAPE", "one text block required") + return block["text"] + + + def _binary_from_text(text: str, path: str) -> bytes: + value = load_json(text.encode("utf-8"), label=path) + if isinstance(value, dict) and isinstance(value.get("results"), list): + rows = value["results"] + if len(rows) != 1 or not isinstance(rows[0], dict): + raise S220Error("LOCALDOCS_RESULT_CARDINALITY", "one binary result required") + value = rows[0].get("content", rows[0].get("text")) + if isinstance(value, str): + value = load_json(value.encode("utf-8"), label=path) + if not isinstance(value, dict) or not isinstance(value.get("content_base64"), str): + raise S220Error("LOCALDOCS_BINARY_ENVELOPE", "content_base64 is required") + try: + raw = base64.b64decode(value["content_base64"].encode("ascii"), validate=True) + except (UnicodeEncodeError, binascii.Error, ValueError) as exc: + raise S220Error("LOCALDOCS_BASE64", "strict base64 required") from exc + if value.get("byte_length", value.get("size", len(raw))) != len(raw): + raise S220Error("LOCALDOCS_LENGTH", f"byte length mismatch: {path}") + if value.get("sha256", digest(raw)) != digest(raw): + raise S220Error("LOCALDOCS_HASH", f"raw hash mismatch: {path}") + return raw + + + class McpClient: + def __init__(self, endpoint: str, name: str, *, client: Any | None = None) -> None: + try: + import httpx # type: ignore + except ImportError as exc: + raise S220Error("HTTPX_UNAVAILABLE", "httpx==0.28.1 is required") from exc + self.endpoint = endpoint + self.name = name + self.client = client or httpx.Client(timeout=60) + self.headers = {"Content-Type": "application/json", "Accept": "application/json, text/event-stream"} + self.ids = itertools.count(10) + self.initialized = False + + def close(self) -> None: + close = getattr(self.client, "close", None) + if callable(close): + close() + + def _post(self, body: Mapping[str, Any], expected_id: int | None) -> Mapping[str, Any] | None: + try: + response = self.client.post(self.endpoint, json=dict(body), headers=dict(self.headers)) + response.raise_for_status() + except Exception as exc: + raise S220Error("MCP_TRANSPORT", f"{self.name} transport failed") from exc + session = response.headers.get("mcp-session-id") + if session: + self.headers["mcp-session-id"] = session + if expected_id is None: + return None + return _parse_mcp_payload(bytes(response.content), expected_id) + + def initialize(self) -> None: + row = self._post( + { + "jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": { + "protocolVersion": MCP_PROTOCOL_VERSION, + "capabilities": {}, + "clientInfo": { + "name": "liti-s2-20-inline", "version": "1.0.0", + "user_id": INLINE_USER_HASH, "workspace_id": INLINE_WORKSPACE_HASH, + }, + }, + }, + 1, + ) + if row is None or row["result"].get("protocolVersion") != MCP_PROTOCOL_VERSION: + raise S220Error("MCP_PROTOCOL", "MCP protocol negotiation failed") + self._post({"jsonrpc": "2.0", "method": "notifications/initialized"}, None) + self.initialized = True + + def call(self, tool: str, arguments: Mapping[str, Any]) -> Mapping[str, Any]: + if not self.initialized: + raise S220Error("MCP_NOT_INITIALIZED", "initialize before tools/call") + message_id = next(self.ids) + row = self._post( + {"jsonrpc": "2.0", "id": message_id, "method": "tools/call", "params": {"name": tool, "arguments": dict(arguments)}}, + message_id, + ) + if row is None: + raise S220Error("MCP_EMPTY", f"empty response: {tool}") + return row["result"] + + def read_binary(self, path: str) -> bytes: + safe = safe_path(path) + return _binary_from_text(_tool_text(self.call("read_binary_doc", {"doc_name": safe}), "read_binary_doc"), safe) + + def read_optional(self, path: str) -> bytes | None: + try: + return self.read_binary(path) + except S220Error as exc: + if exc.code == "LOCALDOCS_NOT_FOUND": + return None + raise + + def write_immutable(self, path: str, payload: bytes) -> str: + safe = safe_path(path) + existing = self.read_optional(safe) + if existing is not None: + if existing != payload: + raise S220Error("IMMUTABLE_CONFLICT", f"existing bytes differ: {safe}") + return digest(existing) + encoded = base64.b64encode(payload).decode("ascii") + _tool_text( + self.call("write_binary_file", {"path": safe, "content_base64": encoded, "overwrite": False}), + "write_binary_file", + ) + observed = self.read_binary(safe) + if observed != payload: + raise S220Error("WRITE_READBACK_MISMATCH", f"read-back differs: {safe}") + return digest(observed) + + + REQUEST_KEYS = { + "schema_version", "request_id", "attempt_id", "run_binding_digest", + "user_identity_hash", "workspace_identity_hash", "stage2_run_root_ref", + "plan_output_root_ref", "host_cas_receipt_ref", "host_cas_receipt_sha256", + "detached_admission_receipt_ref", "detached_admission_receipt_sha256", + "outer_execution_receipt_target_ref", "expected_s2_10_publish_status_sha256", + "expected_parent_stage2_release_sha256", "expected_s2_10_release_sha256", + "expected_s2_20_agent_sha256", "expected_s2_20_executor_binding_sha256", + } + + + def validate_request(raw: bytes) -> dict[str, Any]: + value = load_json(raw, label=REQUEST_PATH) + if not isinstance(value, dict) or set(value) != REQUEST_KEYS: + keys = set(value) if isinstance(value, dict) else set() + raise S220Error("REQUEST_CLOSED_SHAPE", "request keys differ", details={"missing": sorted(REQUEST_KEYS - keys), "extra": sorted(keys - REQUEST_KEYS)}) + if value["schema_version"] != "stage2_s2_20_request.v1": + raise S220Error("REQUEST_VERSION", "unsupported request schema") + for key in ("request_id", "attempt_id"): + if not isinstance(value[key], str) or IDENT.fullmatch(value[key]) is None: + raise S220Error("REQUEST_ID_INVALID", f"invalid {key}") + for key in ( + "run_binding_digest", "user_identity_hash", "workspace_identity_hash", + "host_cas_receipt_sha256", "expected_s2_10_publish_status_sha256", + "expected_parent_stage2_release_sha256", "expected_s2_10_release_sha256", + "expected_s2_20_agent_sha256", "expected_s2_20_executor_binding_sha256", + ): + require_hex(value[key], code=f"{key.upper()}_INVALID") + require_hex(value["detached_admission_receipt_sha256"], code="ADMISSION_HASH_INVALID") + run = value["run_binding_digest"] + expected_root = f"stage2_runs/by-binding/{run}" + if value["stage2_run_root_ref"] != expected_root or value["plan_output_root_ref"] != f"{expected_root}/plan": + raise S220Error("REQUEST_ROOT_BINDING", "request/output roots must derive from run binding") + if value["user_identity_hash"] != INLINE_USER_HASH or value["workspace_identity_hash"] != INLINE_WORKSPACE_HASH: + raise S220Error("WORKSPACE_IDENTITY_MISMATCH", "request identity differs from Agent substitution") + exact_paths = { + "host_cas_receipt_ref": f"stage2_control/host_cas/{run}/S2_20.json", + "detached_admission_receipt_ref": f"{ASSET_ROOT}/manifest/stage2_deterministic_admission_receipt.json", + "outer_execution_receipt_target_ref": f"{expected_root}/control/s2_20_outer_execution_receipt.json", + } + for key, expected in exact_paths.items(): + safe_path(value[key]) + if value[key] != expected: + raise S220Error("REQUEST_TRUST_ANCHOR_PATH", f"{key} must equal its canonical run-bound path") + return value + + + def read_stable(localdocs: McpClient, paths: Iterable[str]) -> tuple[dict[str, bytes], str]: + ordered = sorted(set(safe_path(path) for path in paths)) + first: dict[str, bytes] = {} + total = 0 + for path in ordered: + raw = localdocs.read_binary(path) + total += len(raw) + if total > MAX_TOTAL_BYTES: + raise S220Error("TOTAL_READ_LIMIT", "input hydration exceeds total limit") + first[path] = raw + second = {path: localdocs.read_binary(path) for path in ordered} + if any(first[path] != second[path] for path in ordered): + raise S220Error("INPUT_TOCTOU", "input changed between bounded reads") + rows = [[path, digest(first[path]), len(first[path])] for path in ordered] + return first, digest(rows) + + + def option_disposition( + option: Mapping[str, Any], policy: Mapping[str, Any] + ) -> tuple[str, list[str]]: + decision = option.get("decision") + client = option.get("client_disposition") + relation = option.get("relation") + if policy.get("policy_status") == "APPROVED": + observed = { + "legal_decision": decision, + "client_instruction": "NO_SUE" if client == "DEFERRED_BY_CLIENT_INSTRUCTION" else "UNSPECIFIED", + "precondition_status": "UNRESOLVED" if option.get("precondition_refs") or relation == "precondition_candidate" else "NOT_APPLICABLE", + "compatibility_status": "INCOMPATIBLE" if option.get("incompatible_with") else "COMPATIBLE", + } + matches = [ + row for row in policy.get("rules", []) + if isinstance(row, dict) + and all(row.get(key) == value for key, value in observed.items()) + ] + if len(matches) != 1: + raise S220Error( + "DISPOSITION_POLICY_CONFLICT" if matches else "DISPOSITION_POLICY_GAP", + "approved option-disposition policy must match exactly one row", + ) + result = matches[0].get("result_disposition") + if result not in {"SELECTED", "ALTERNATIVE", "EXCLUDED", "DEFERRED"}: + raise S220Error("DISPOSITION_POLICY_GAP", "policy row cannot authorize a disposition") + return str(result).lower(), [str(matches[0].get("reason_code"))] + reason_codes = ["DISPOSITION_POLICY_NOT_ADMITTED"] + if decision == "EXCLUDED": + return "excluded", reason_codes + if client == "DEFERRED_BY_CLIENT_INSTRUCTION": + return "deferred", reason_codes + if relation in {"alternative", "precondition_candidate", "accessory"}: + return "alternative", reason_codes + if decision in {"SUPPORTED", "CONDITIONAL", "UNRESOLVED"}: + return "selected", reason_codes + raise S220Error("DISPOSITION_POLICY_GAP", "claim option has no closed disposition") + + + def project_signature( + source: Mapping[str, Any], + c20_projection: Mapping[str, Any], + c21_projection: Mapping[str, Any], + remedy_context: Mapping[str, Any], + projection_registry: Mapping[str, Any], + ) -> dict[str, Any]: + """Closed S2_10+C20+C21+remedy projection used by C25. + + No label, free text, filename, retrieval result, or model output may + supplement these explicit typed inputs. A missing/ambiguous value + remains UNKNOWN and therefore cannot produce an exact case binding. + """ + expected_source_fields = { + "vocabulary_version", "legal_basis_family", "right_holder_refs", + "obligor_refs", "performance_kind", "object_refs", "legal_effect", + "source_occurrence_refs", + } + if set(source) != expected_source_fields: + raise S220Error( + "BINDING_SIGNATURE_SOURCE_SHAPE", + "normalized claim signature must use the closed S2_10 vocabulary", + details={ + "missing": sorted(expected_source_fields - set(source)), + "extra": sorted(set(source) - expected_source_fields), + }, + ) + if source.get("vocabulary_version") != "stage2.claim_signature_vocab.v1": + raise S220Error("BINDING_SIGNATURE_SOURCE_VERSION", "unsupported source vocabulary") + source_values: dict[str, Any] = dict(source) + source_values.update( + { + "c20_remedy_projection": remedy_context.get("remedy_mode"), + "c20_dependency_projection": c20_projection.get("dependency_kind"), + "c21_party_projection": c21_projection.get("party_role_pattern"), + "c21_object_projection": c21_projection.get("object_kind"), + } + ) + projected: dict[str, Any] = { + key: ([] if key == "special_statute_tags" else "UNKNOWN") + for key in TARGET_SIGNATURE_FIELDS + } + rules = projection_registry.get("projection_rules", []) + rule_ids: list[str] = [] + provenance: list[dict[str, Any]] = [] + if projection_registry.get("registry_status") == "APPROVED" and isinstance(rules, list): + by_target: dict[str, Mapping[str, Any]] = {} + for rule in rules: + if not isinstance(rule, dict) or rule.get("target_field") not in TARGET_SIGNATURE_FIELDS: + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_INVALID", "closed projection row required") + target_field = str(rule["target_field"]) + if target_field in by_target: + raise S220Error("BINDING_SIGNATURE_PROJECTION_MULTI_RULE", target_field) + by_target[target_field] = rule + if set(by_target) != set(TARGET_SIGNATURE_FIELDS): + raise S220Error("BINDING_SIGNATURE_PROJECTION_COVERAGE", "all target fields require one rule") + for target_field in TARGET_SIGNATURE_FIELDS: + rule = by_target[target_field] + rule_id = str(rule.get("projection_rule_id", "")) + source_fields = rule.get("source_fields", []) + allowed = rule.get("allowed_output_values", []) + operator = rule.get("derivation_operator") + if not rule_id or not isinstance(source_fields, list) or not isinstance(allowed, list): + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_INVALID", target_field) + if rule.get("implementation_status") != "IMPLEMENTED" or rule.get("semantic_review_status") != "APPROVED": + raise S220Error("BINDING_SIGNATURE_PROJECTION_RULE_NOT_ADMITTED", target_field) + candidates: list[str] = [] + for field in source_fields: + value = source_values.get(str(field)) + values = value if isinstance(value, list) else [value] + candidates.extend(nfc(item) for item in values if isinstance(item, str) and item) + permitted = sorted({item for item in candidates if item in allowed}) + if operator in {"DIRECT_COPY", "CLOSED_LOOKUP"} and len(permitted) == 1: + projected[target_field] = permitted[0] + elif operator == "SET_UNION" and target_field == "special_statute_tags": + projected[target_field] = permitted + elif operator == "SET_INTERSECTION" and target_field == "special_statute_tags": + projected[target_field] = permitted if permitted and len(permitted) == len(set(candidates)) else [] + rule_ids.append(rule_id) + provenance.append( + { + "target_field": target_field, + "value": projected[target_field], + "source_paths": sorted(str(item) for item in source_fields), + "status": "DERIVED" if projected[target_field] not in ("UNKNOWN", []) else "UNKNOWN", + } + ) + if not rule_ids: + rule_ids = ["PENDING_BINDING_SIGNATURE_PROJECTION"] + provenance = [ + { + "target_field": target_field, + "value": projected[target_field], + "source_paths": [], + "status": "UNKNOWN", + } + for target_field in TARGET_SIGNATURE_FIELDS + ] + return { + "schema_version": "stage2.binding_target_signature.v1", + "source_vocabulary_version": source["vocabulary_version"], + "target_vocabulary_version": projection_registry.get("target_vocabulary_version", "stage2.case_type_signature_vocab.v1"), + "projection_rule_ids": sorted(rule_ids), + "source_signature_refs": sorted(str(item) for item in source.get("source_occurrence_refs", []) if isinstance(item, str)), + "target_signature": projected, + "target_signature_sha256": digest(projected), + "signature": projected, + "signature_sha256": digest(projected), + "provenance": provenance, + } + + + def eval_predicate(signature: Mapping[str, Any], predicate: Mapping[str, Any]) -> bool: + operator = predicate.get("operator") + field = predicate.get("field") + if operator not in {"EQ", "IN", "ALL_OF", "NONE_OF", "EXISTS"} or field not in TARGET_SIGNATURE_FIELDS: + raise S220Error("PREDICATE_GRAMMAR", "unsupported predicate operator/field") + actual = signature.get(str(field), "UNKNOWN") + expected = predicate.get("value") + if operator == "EXISTS": + if not isinstance(expected, bool): + raise S220Error("PREDICATE_GRAMMAR", "EXISTS requires a boolean value") + return (actual not in (None, "UNKNOWN", [])) is expected + if operator == "EQ": + return actual == expected + if operator == "IN": + return actual in (expected if isinstance(expected, list) else []) + actual_set = set(actual if isinstance(actual, list) else [actual]) + expected_set = set(expected if isinstance(expected, list) else []) + return expected_set <= actual_set if operator == "ALL_OF" else actual_set.isdisjoint(expected_set) + + + def eval_closed_predicate( + signature: Mapping[str, Any], predicate: Mapping[str, Any] + ) -> bool: + if predicate.get("legal_review_status") != "APPROVED": + return False + clauses = predicate.get("clauses") + if not isinstance(clauses, list) or not clauses or any(not isinstance(item, dict) for item in clauses): + raise S220Error("PREDICATE_GRAMMAR", "approved predicate requires non-empty clauses") + return all(eval_predicate(signature, item) for item in clauses) + + + def bind_case( + signature: Mapping[str, Any], registry: Mapping[str, Any] + ) -> tuple[str | None, str, list[str], list[str]]: + rows = registry.get("rows", registry.get("case_types", [])) + matches: list[tuple[str, str]] = [] + for row in rows if isinstance(rows, list) else []: + if not isinstance(row, dict): + continue + if row.get("row_kind") not in {"CLAIM", "GENERIC"}: + continue + if row.get("claim_capable_disposition") != "CLAIM_CAPABLE" or row.get("legal_classification_status") != "APPROVED": + continue + predicates = row.get("case_type_predicates", []) + if ( + isinstance(predicates, list) + and len(predicates) == 1 + and isinstance(predicates[0], dict) + and eval_closed_predicate(signature, predicates[0]) + ): + matches.append((str(row.get("case_type_id")), str(predicates[0].get("predicate_id")))) + matches = sorted(set(matches)) + if len(matches) == 1: + return matches[0][0], "BOUND", [], [matches[0][1]] + if not matches: + return None, "UNRESOLVED", ["CASE_TYPE_BINDING_ZERO_MATCH"], [] + return None, "CONFLICT", ["CASE_TYPE_BINDING_MULTI_MATCH"], [] + + + def bind_rule( + case_type_id: str | None, + signature: Mapping[str, Any], + rule_registry: Mapping[str, Any], + ) -> tuple[dict[str, Any] | None, list[str], list[str]]: + if case_type_id is None: + return None, ["SUB_RULE_BINDING_ZERO_MATCH"], [] + registry_rows = rule_registry.get("rows", rule_registry.get("case_type_rule_rows", rule_registry.get("rule_bindings", []))) + rows = [row for row in registry_rows if isinstance(row, dict) and row.get("case_type_id") == case_type_id] + branches: list[dict[str, Any]] = [] + for row in rows: + row_branches = row.get("rule_branches", row.get("sub_rule_bindings", [])) + for branch in row_branches if isinstance(row_branches, list) else []: + if not isinstance(branch, dict) or branch.get("legal_content_status") != "APPROVED": + continue + predicates = branch.get("sub_rule_predicates", []) + if ( + isinstance(predicates, list) + and len(predicates) == 1 + and isinstance(predicates[0], dict) + and eval_closed_predicate(signature, predicates[0]) + ): + branches.append(branch) + if len(branches) == 1: + branch = branches[0] + predicate_ids = [str(item.get("predicate_id")) for item in branch.get("sub_rule_predicates", []) if isinstance(item, dict) and item.get("predicate_id")] + return branch, [], sorted(predicate_ids or [str(branch.get("predicate_id", "UNKNOWN"))]) + if len(branches) > 1: + return None, ["SUB_RULE_BINDING_MULTI_MATCH"], [] + return None, ["RULE_BRANCH_MISSING"], [] + + + def _source_ref(path: str, sha256: str, pointer: str = "") -> dict[str, str]: + return {"path": path, "sha256": sha256, "json_pointer": pointer} + + + def validate_rule_foreign_keys( + case_type_id: str, + branch: Mapping[str, Any], + assets: Mapping[str, Any], + ) -> tuple[list[str], dict[str, list[dict[str, str]]]]: + """C26 exact FK/hash/status closure for an otherwise matched branch.""" + codes: list[str] = [] + refs: dict[str, list[dict[str, str]]] = { + "structured_relief_rule_refs": [], + "markdown_source_refs": [], + "renderer_refs": [], + "party_set_rule_refs": [], + "ce13_branch_refs": [], + "review_policy_refs": [], + } + legal_bytes = assets.get("release_selected_legal_bytes", {}) + markdown_path = branch.get("markdown_source_path") + markdown_hash = branch.get("markdown_source_sha256") + if ( + not isinstance(markdown_path, str) + or not isinstance(markdown_hash, str) + or not isinstance(legal_bytes, dict) + or markdown_path not in legal_bytes + or digest(legal_bytes[markdown_path]) != markdown_hash + ): + codes.append("RELIEF_RULE_SOURCE_HASH_MISMATCH") + else: + refs["markdown_source_refs"].append(_source_ref(markdown_path, markdown_hash)) + receipt_path = branch.get("legal_review_receipt_ref") + receipt_hash = branch.get("legal_review_receipt_sha256") + if ( + not isinstance(receipt_path, str) + or not isinstance(receipt_hash, str) + or receipt_path not in legal_bytes + or digest(legal_bytes[receipt_path]) != receipt_hash + ): + codes.append("RULE_BRANCH_LEGAL_RECEIPT_HASH_MISMATCH") + + relief_rules = assets.get("relief_rules", {}) + relief_rows = relief_rules.get("rule_branches", []) if isinstance(relief_rules, dict) else [] + relief_matches = [ + row for row in relief_rows + if isinstance(row, dict) + and row.get("case_type_id") == case_type_id + and row.get("sub_rule_id") == branch.get("sub_rule_id") + and row.get("rule_branch_key", f"{case_type_id}::{row.get('sub_rule_id')}") == branch.get("rule_branch_key") + ] + if ( + not isinstance(relief_rules, dict) + or relief_rules.get("execution_eligible") is not True + or relief_rules.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(relief_matches) != 1 + ): + codes.append("STRUCTURED_RELIEF_RULE_FK_UNRESOLVED") + else: + refs["structured_relief_rule_refs"].append( + _source_ref( + "registry/drafting/case_type_relief_rules.yml", + str(assets["asset_hashes"]["relief_rules"]), + f"/rule_branches/{relief_rows.index(relief_matches[0])}", + ) + ) + + renderer_descriptors = assets.get("renderer_descriptors", {}) + renderer_hashes = assets.get("renderer_hashes", {}) + for renderer_id in branch.get("renderer_ids", []): + descriptor = renderer_descriptors.get(renderer_id) if isinstance(renderer_descriptors, dict) else None + renderer_hash = renderer_hashes.get(renderer_id) if isinstance(renderer_hashes, dict) else None + if ( + not isinstance(descriptor, dict) + or descriptor.get("renderer_id") != renderer_id + or descriptor.get("execution_eligible") is not True + or descriptor.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or not isinstance(renderer_hash, str) + ): + codes.append("RENDERER_FK_UNRESOLVED") + else: + renderer_paths = { + "R01": "renderers/R01_money_payment.yml", + "R02": "renderers/R02_delivery_possession.yml", + "R03": "renderers/R03_declaration_registry.yml", + "R04": "renderers/R04_special_nonmoney_performance.yml", + "R05": "renderers/R05_declaratory.yml", + "R06": "renderers/R06_constitutive_judgment_challenge.yml", + } + refs["renderer_refs"].append(_source_ref(renderer_paths[renderer_id], renderer_hash)) + + party_registry = assets.get("party_rules", {}) + party_rows = party_registry.get("rules", []) if isinstance(party_registry, dict) else [] + party_matches = [ + row for row in party_rows + if isinstance(row, dict) + and row.get("party_set_rule_id") == branch.get("party_set_rule_id") + and row.get("legal_content_status") == "APPROVED" + ] + if party_registry.get("registry_status") != "APPROVED" or len(party_matches) != 1: + codes.append("PARTY_SET_RULE_FK_UNRESOLVED") + else: + refs["party_set_rule_refs"].append( + _source_ref( + "registry/party/party_set_rules.yml", + str(assets["asset_hashes"]["party_rules"]), + f"/rules/{party_rows.index(party_matches[0])}", + ) + ) + + scope_registry = assets.get("corpus_scope", {}) + scope_rows = scope_registry.get("rows", []) if isinstance(scope_registry, dict) else [] + scope_matches = [ + row for row in scope_rows + if isinstance(row, dict) + and row.get("corpus_scope_id") == branch.get("corpus_scope_id") + and row.get("case_type_id") == case_type_id + and branch.get("sub_rule_id") in row.get("sub_rule_ids", []) + and row.get("element_set_id") == branch.get("element_set_id") + and row.get("approval_status") == "APPROVED" + ] + if scope_registry.get("registry_status") != "APPROVED" or len(scope_matches) != 1: + codes.append("CORPUS_SCOPE_FK_UNRESOLVED") + + ce13 = assets.get("calculator_descriptors", {}).get("CE-13") + ce13_rows = ce13.get("formula_rows", []) if isinstance(ce13, dict) else [] + ce13_matches = [ + row for row in ce13_rows + if isinstance(row, dict) + and row.get("formula_id") == branch.get("ce13_branch_id") + ] + if ( + not isinstance(ce13, dict) + or ce13.get("execution_eligible") is not True + or ce13.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(ce13_matches) != 1 + ): + codes.append("CE13_BRANCH_FK_UNRESOLVED") + else: + refs["ce13_branch_refs"].append( + _source_ref( + "registry/calculations/CE-13_court_value_cost.yml", + str(assets["calculator_descriptor_hashes"]["CE-13"]), + f"/formula_rows/{ce13_rows.index(ce13_matches[0])}", + ) + ) + + review_registry = assets.get("review_policy", {}) + policy_rows = review_registry.get("policy_rows", []) if isinstance(review_registry, dict) else [] + policy_matches = [ + row for row in policy_rows + if isinstance(row, dict) and row.get("review_policy_id", row.get("policy_id")) == branch.get("review_policy_id") + ] + if ( + not isinstance(review_registry, dict) + or review_registry.get("execution_eligible") is not True + or review_registry.get("status") not in {"APPROVED", "LEGAL_CONTENT_ADMITTED"} + or len(policy_matches) != 1 + ): + codes.append("REVIEW_POLICY_FK_UNRESOLVED") + else: + refs["review_policy_refs"].append( + _source_ref( + "registry/review/review_policy_registry.yml", + str(assets["asset_hashes"]["review_policy"]), + f"/policy_rows/{policy_rows.index(policy_matches[0])}", + ) + ) + return sorted(set(codes)), { + key: sorted(value, key=lambda row: (row["path"], row["json_pointer"])) + for key, value in refs.items() + } + + + def build_portfolio( + verdicts: Sequence[Mapping[str, Any]], + verdict_sources: Mapping[str, Mapping[str, str]], + disposition_policy: Mapping[str, Any], + ) -> tuple[list[dict[str, Any]], dict[str, list[str]], list[dict[str, Any]], list[dict[str, Any]]]: + atomic: list[dict[str, Any]] = [] + partitions = {name: [] for name in DISPOSITIONS} + dispositions: list[dict[str, Any]] = [] + option_to_group: dict[str, str] = {} + relation_rows: list[tuple[str, str, str]] = [] + for verdict in verdicts: + cluster_id = str(verdict.get("cluster_id", "UNKNOWN")) + source = verdict_sources.get(cluster_id, {}) + options = verdict.get("claim_options", []) + for option_index, option in enumerate(options if isinstance(options, list) else []): + if not isinstance(option, dict): + continue + option_id = str(option.get("claim_option_id", "")) + if not option_id: + raise S220Error("CLAIM_OPTION_ID_MISSING", "canonical option ID is required") + disposition, disposition_reason_codes = option_disposition(option, disposition_policy) + partitions[disposition].append(option_id) + active = disposition in {"selected", "alternative"} + atomic_id = stable_id("AC", option_id) if active else None + group_id = stable_id("CG", option.get("same_underlying_recovery_key") or option_id) if active else None + if active and atomic_id is not None and group_id is not None: + option_to_group[option_id] = group_id + signature = option.get("normalized_claim_signature", {}) + source_path = source.get("path", f"map_s2_10/domain_verdicts/{cluster_id}.json") + source_sha = source.get("sha256", digest(verdict)) + atomic.append( + { + "atomic_claim_id": atomic_id, + "claim_option_id": option_id, + "claim_group_id": group_id, + "cluster_id": cluster_id, + "disposition": disposition.upper(), + "legal_decision": option.get("decision"), + "relation": option.get("relation"), + "client_disposition": option.get("client_disposition"), + "client_instruction_refs": sorted(str(item) for item in option.get("client_instruction_refs", []) if isinstance(item, str)), + "right_holder_refs": sorted(str(item) for item in signature.get("right_holder_refs", []) if isinstance(item, str)), + "obligor_refs": sorted(str(item) for item in signature.get("obligor_refs", []) if isinstance(item, str)), + "object_refs": sorted(str(item) for item in signature.get("object_refs", []) if isinstance(item, str)), + "same_underlying_recovery_key": option.get("same_underlying_recovery_key"), + "incompatible_with": sorted(str(item) for item in option.get("incompatible_with", []) if isinstance(item, str)), + "precondition_refs": sorted(str(item) for item in option.get("precondition_refs", []) if isinstance(item, str)), + "element_statuses": option.get("element_statuses", []), + "defense_statuses": option.get("defense_statuses", []), + "remedy_candidates": option.get("remedy_candidates", []), + "normalized_claim_signature": signature, + "c20_projection": { + "dependency_kind": option.get("relation", "UNKNOWN"), + "incompatible_option_ids": sorted(str(item) for item in option.get("incompatible_with", []) if isinstance(item, str)), + "precondition_option_ids": sorted(str(item) for item in option.get("precondition_refs", []) if isinstance(item, str)), + }, + "normalized_signature_sha256": digest(signature), + "source_refs": [{ + "source_artifact_path": source_path, + "source_artifact_sha256": source_sha, + "json_pointer": f"/claim_options/{option_index}", + }], + "typed_provenance": sorted( + [item for item in option.get("typed_provenance", []) if isinstance(item, dict)], + key=digest, + ), + "source_option_sha256": digest(option), + } + ) + relation_rows.extend((option_id, target, "INCOMPATIBLE") for target in option.get("incompatible_with", []) if isinstance(target, str)) + relation_rows.extend((option_id, target, "PRECONDITION") for target in option.get("precondition_refs", []) if isinstance(target, str)) + dispositions.append( + { + "schema_version": "stage2_option_disposition.v1", + "claim_option_id": option_id, + "source_cluster_id": cluster_id, + "source_domain_verdict_sha256": source.get("sha256", digest(verdict)), + "legal_decision": option.get("decision"), + "client_instruction": "NO_SUE" if option.get("client_disposition") == "DEFERRED_BY_CLIENT_INSTRUCTION" else "UNSPECIFIED", + "disposition": disposition.upper(), + "reason_codes": sorted(set(disposition_reason_codes + [f"S2_10_{str(option.get('decision', 'UNKNOWN'))}", f"RELATION_{str(option.get('relation', 'UNKNOWN')).upper()}"])), + "atomic_claim_ids": [atomic_id] if atomic_id is not None else [], + "claim_group_id": group_id, + "issue_refs": [], + } + ) + for values in partitions.values(): + values.sort() + seen = [item for values in partitions.values() for item in values] + if len(seen) != len(set(seen)) or set(seen) != {row["claim_option_id"] for row in dispositions}: + raise S220Error("OPTION_CONSERVATION", "disposition partition is not exact") + dependencies: list[dict[str, Any]] = [] + for source_option, target_option, relation in sorted(set(relation_rows)): + source_group = option_to_group.get(source_option) + target_group = option_to_group.get(target_option) + if source_group is None or target_group is None or source_group == target_group: + continue + dependencies.append( + { + "source_group_id": source_group, + "target_group_id": target_group, + "relation": relation, + "source_option_relation_refs": [f"{source_option}->{target_option}"], + } + ) + return ( + sorted(atomic, key=lambda row: row["atomic_claim_id"]), + partitions, + sorted(dispositions, key=lambda row: row["claim_option_id"]), + sorted(dependencies, key=digest), + ) + + + def build_party_projection( + party_context: Mapping[str, Any], object_registry: Mapping[str, Any] + ) -> dict[str, Any]: + """C21 facts-only projection; no required-party legal conclusion.""" + parties = party_context.get("parties", []) + party_refs = sorted( + str(row.get("party_identity", {}).get("party_context_id")) + for row in parties + if isinstance(row, dict) + and isinstance(row.get("party_identity"), dict) + and row["party_identity"].get("party_context_id") + ) + identity_issues = sorted( + str(row.get("party_identity", {}).get("party_context_id")) + for row in parties + if isinstance(row, dict) + and isinstance(row.get("party_identity"), dict) + and row["party_identity"].get("identity_disposition") == "IDENTITY_UNRESOLVED" + ) + objects = object_registry.get("objects", []) + object_refs = sorted( + str(row.get("object_context_id")) + for row in objects + if isinstance(row, dict) and row.get("object_context_id") + ) + object_identity_issues = sorted( + str(row.get("object_context_id")) + for row in objects + if isinstance(row, dict) + and row.get("object_context_id") + and row.get("identity_disposition") == "IDENTITY_UNRESOLVED" + ) + present_role_tokens = sorted( + { + nfc(str(row[key])) + for row in parties + if isinstance(row, dict) + for key in ("party_title", "defendant_role") + if isinstance(row.get(key), str) and row.get(key) + } + ) + return { + "schema_version": "stage2_party_projection.v1", + "party_refs": party_refs, + "object_refs": object_refs, + "title_chain_refs": [], + "identity_issue_refs": sorted(set(identity_issues + object_identity_issues)), + "present_role_tokens": present_role_tokens, + "legal_required_party_determination_made": False, + } + + + def claim_c21_projection( + claim: Mapping[str, Any], party_projection: Mapping[str, Any] + ) -> dict[str, str]: + known_parties = set(str(item) for item in party_projection.get("party_refs", [])) + known_objects = set(str(item) for item in party_projection.get("object_refs", [])) + holders = set(str(item) for item in claim.get("right_holder_refs", [])) + obligors = set(str(item) for item in claim.get("obligor_refs", [])) + objects = set(str(item) for item in claim.get("object_refs", [])) + if holders and obligors and holders | obligors <= known_parties: + party_role_pattern = f"RH_{len(holders)}_OB_{len(obligors)}" + else: + party_role_pattern = "UNKNOWN" + if not objects: + object_kind = "NO_OBJECT_REF" + elif objects <= known_objects: + object_kind = "IDENTIFIED_OBJECT_SET" + else: + object_kind = "UNKNOWN" + return { + "party_role_pattern": party_role_pattern, + "object_kind": object_kind, + } + + + def calculate_all( + atomic: Sequence[Mapping[str, Any]], + bindings: Sequence[Mapping[str, Any]], + activation_registry: Mapping[str, Any], + descriptor_hashes: Mapping[str, str], + descriptor_objects: Mapping[str, Mapping[str, Any]], + law_value_tables: Sequence[Mapping[str, Any]], + request: Mapping[str, Any], + ) -> list[dict[str, Any]]: + """C22 closed dispatcher and Decimal formula interpreter. + + The present distribution contains no admitted legal formula rows. + This implementation nevertheless closes the admitted branch: an exact + activation row and release-selected descriptor are required, every + absent operand/law value remains explicit, and no numeric fallback is + permitted. + """ + descriptors = activation_registry.get("calculator_descriptors", []) + calculator_ids = sorted( + str(row["calculator_id"]) + for row in descriptors + if isinstance(row, dict) and isinstance(row.get("calculator_id"), str) + ) + descriptor_refs = { + str(row["calculator_id"]): row + for row in descriptors + if isinstance(row, dict) and isinstance(row.get("calculator_id"), str) + } + activation_rows = activation_registry.get("activation_rows", []) + if not isinstance(activation_rows, list): + raise S220Error("CALCULATION_ACTIVATION_ROWS_INVALID", "activation_rows must be an array") + registry_ready = ( + activation_registry.get("execution_eligible") is True + and activation_registry.get("status") == "LEGAL_CONTENT_ADMITTED" + ) + binding_by_atomic = { + str(row["atomic_claim_id"]): row + for row in bindings + if isinstance(row, dict) and isinstance(row.get("atomic_claim_id"), str) + } + law_values: dict[str, Mapping[str, Any]] = {} + for table in law_value_tables: + if not isinstance(table, Mapping) or table.get("release_eligible") is not True: + continue + for token in table.get("tokens", []): + if not isinstance(token, Mapping) or token.get("review_status") != "VERIFIED": + continue + law_value_id = token.get("law_value_id") + if isinstance(law_value_id, str) and law_value_id: + if law_value_id in law_values: + raise S220Error("LAW_VALUE_DUPLICATE", f"duplicate law value: {law_value_id}") + law_values[law_value_id] = token + + rounding_modes = { + "ROUND_HALF_UP": ROUND_HALF_UP, + "ROUND_DOWN": ROUND_DOWN, + "ROUND_UP": ROUND_UP, + "TRUNCATE": ROUND_DOWN, + } + + def decimal_value(value: Any) -> Decimal: + try: + return Decimal(str(value)) + except (InvalidOperation, ValueError) as exc: + raise S220Error("INVALID_DECIMAL_OPERAND", "operand is not an exact decimal") from exc + + def claim_metrics(claim: Mapping[str, Any]) -> list[str]: + values: set[str] = set() + for remedy in claim.get("remedy_candidates", []): + if not isinstance(remedy, Mapping): + continue + if isinstance(remedy.get("remedy_kind"), str): + values.add(str(remedy["remedy_kind"])) + values.update( + str(item) + for item in remedy.get("condition_codes", []) + if isinstance(item, str) and item + ) + return sorted(values) + + def row_matches( + activation_row: Mapping[str, Any], + claim: Mapping[str, Any], + binding: Mapping[str, Any], + requested_metrics: Sequence[str], + ) -> bool: + row_metrics = activation_row.get("requested_metrics", []) + equals = activation_row.get("equals", {}) + if not isinstance(row_metrics, list) or not isinstance(equals, Mapping): + raise S220Error("INVALID_ACTIVATION_ROW", "activation row shape is invalid") + if not set(str(item) for item in row_metrics) & set(requested_metrics): + return False + actual = { + "normalized_claim_signature": str(claim.get("normalized_signature_sha256", "")), + "case_type_id": str(binding.get("case_type_id", "")), + "rule_branch_key": str(binding.get("rule_branch_key", "")), + } + for key, expected in equals.items(): + if key == "requested_metric": + if str(expected) not in requested_metrics: + return False + elif key not in actual or actual[key] != str(expected): + return False + return True + + def empty_receipt( + claim: Mapping[str, Any], calculator_id: str, status: str, issues: Sequence[str] + ) -> dict[str, Any]: + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": stable_id("CR", claim["atomic_claim_id"], calculator_id), + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": claim["atomic_claim_id"], + "claim_group_id": claim["claim_group_id"], + "calculator_id": calculator_id, + "activation_status": status, + "calculation_status": status, + "activation_row_id": None, + "descriptor_sha256": descriptor_hashes.get(calculator_id), + "formula_id": None, + "formula_sha256": None, + "operand_rows": [], + "operand_refs": [], + "units": [], + "law_value_uses": [], + "effective_interval": None, + "rounding_rule": None, + "period_rows": [], + "missing_operands": [], + "result": None, + "result_sha256": None, + "issue_codes": sorted(set(str(item) for item in issues)), + } + return {**core, "receipt_sha256": digest(core)} + + def operand_row(operand_id: str, unit_id: str) -> dict[str, Any]: + return { + "operand_id": operand_id, + "source_ref": f"UNRESOLVED_STAGE1_CALCULATION_REQUEST:{operand_id}", + "status": "MISSING", + "value_type": "DECIMAL", + "value": None, + "unit_id": unit_id, + "currency": "KRW" if unit_id == "KRW" else None, + "value_sha256": None, + } + + receipts: list[dict[str, Any]] = [] + for row in sorted(atomic, key=lambda item: str(item["atomic_claim_id"])): + binding = binding_by_atomic.get(str(row["atomic_claim_id"]), {}) + requested_metrics = claim_metrics(row) + for calculator_id in calculator_ids: + if not registry_ready: + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_ACTIVATION_REGISTRY_NOT_ADMITTED"])) + continue + candidate_rows = [ + item for item in activation_rows + if isinstance(item, Mapping) and item.get("calculator_id") == calculator_id + ] + matches = [ + item for item in candidate_rows + if row_matches(item, row, binding, requested_metrics) + ] + if len(matches) > 1: + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_CONFLICT", ["CALCULATION_ACTIVATION_CONFLICT"])) + continue + if not matches: + relevant_metrics = { + str(metric) + for item in candidate_rows + for metric in item.get("requested_metrics", []) + if isinstance(metric, str) + } + if relevant_metrics & set(requested_metrics): + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_ACTIVATION_GAP"])) + else: + receipts.append(empty_receipt(row, calculator_id, "NOT_APPLICABLE", [])) + continue + activation_row = matches[0] + activation_row_id = activation_row.get("activation_row_id") + descriptor_ref = descriptor_refs.get(calculator_id, {}) + descriptor = descriptor_objects.get(calculator_id, {}) + if ( + not isinstance(activation_row_id, str) + or descriptor_ref.get("execution_eligible") is not True + or descriptor_ref.get("status") != "LEGAL_CONTENT_ADMITTED" + or descriptor.get("execution_eligible") is not True + or descriptor.get("status") != "LEGAL_CONTENT_ADMITTED" + or descriptor_ref.get("descriptor_sha256") != descriptor_hashes.get(calculator_id) + or descriptor.get("descriptor_sha256") != descriptor_hashes.get(calculator_id) + ): + receipts.append(empty_receipt(row, calculator_id, "ACTIVATION_GAP", ["CALCULATION_DESCRIPTOR_NOT_ADMITTED_OR_HASH_MISMATCH"])) + continue + formulas = [item for item in descriptor.get("formula_rows", []) if isinstance(item, Mapping)] + formula_matches = [item for item in formulas if item.get("output_metric_id") in requested_metrics] + if len(formula_matches) != 1: + status = "ACTIVATION_CONFLICT" if len(formula_matches) > 1 else "ACTIVATION_GAP" + receipts.append(empty_receipt(row, calculator_id, status, ["CALCULATION_FORMULA_SELECTION_NOT_EXACT"])) + continue + formula = formula_matches[0] + required_operands = sorted(str(item) for item in formula.get("input_operand_ids", []) if isinstance(item, str)) + output_unit = str(formula.get("output_unit_id", "")) + operand_rows = [operand_row(item, output_unit) for item in required_operands] + missing_operands = list(required_operands) + law_value_uses: list[dict[str, Any]] = [] + missing_law_values: list[str] = [] + for law_value_id in formula.get("law_value_ids", []): + source = law_values.get(str(law_value_id)) + if not isinstance(source, Mapping): + missing_law_values.append(str(law_value_id)) + continue + law_value_uses.append( + { + "law_value_id": str(law_value_id), + "authority_id": str(source["authority_id"]), + "authority_release_sha256": require_hex(source["authority_release_sha256"], code="AUTHORITY_RELEASE_SHA256_INVALID"), + "law_value_release_sha256": require_hex(source["law_value_release_sha256"], code="LAW_VALUE_RELEASE_SHA256_INVALID"), + "effective_interval": source["effective_interval"], + "value": format(decimal_value(source["value"]), "f"), + "unit_id": str(source.get("unit_id", source.get("unit"))), + "approval_status": "APPROVED", + } + ) + calculation_status = "MISSING_OPERAND" if missing_operands else ("LAW_VALUE_GAP" if missing_law_values else "CALCULATED") + result: dict[str, Any] | None = None + result_sha256: str | None = None + if calculation_status == "CALCULATED": + # The current S2_00 handoff deliberately preserves only + # calculation-request references, not numeric values. This + # branch becomes reachable only after exact PRESENT operand + # rows are supplied by a compatible handoff revision. + values = [decimal_value(item["value"]) for item in operand_rows] + operator = formula.get("operator") + if operator == "ADD": + computed = sum(values, Decimal("0")) + elif operator == "SUBTRACT": + computed = values[0] - sum(values[1:], Decimal("0")) + elif operator == "MULTIPLY": + computed = Decimal("1") + for value in values: + computed *= value + elif operator == "DIVIDE": + if len(values) != 2 or values[1] == 0: + raise S220Error("CALCULATION_INVALID_DIVISION", "division requires two operands and non-zero divisor") + computed = values[0] / values[1] + elif operator == "MIN": + computed = min(values) + elif operator == "MAX": + computed = max(values) + else: + raise S220Error("CALCULATION_OPERATOR_UNSUPPORTED", "formula operator is outside the closed set") + rounding = formula.get("rounding_rule", {}) + if not isinstance(rounding, Mapping) or rounding.get("mode") not in rounding_modes: + raise S220Error("CALCULATION_ROUNDING_INVALID", "rounding rule is invalid") + rounded = computed.quantize(decimal_value(rounding["quantum"]), rounding=rounding_modes[str(rounding["mode"])]) + result = { + "metric_id": str(formula["output_metric_id"]), + "value": format(rounded, "f"), + "unit_id": output_unit, + "currency": "KRW" if output_unit == "KRW" else None, + } + result_sha256 = digest(result) + core = { + "schema_version": "stage2.calculation_receipt.v1", + "calculation_receipt_id": stable_id("CR", row["atomic_claim_id"], calculator_id), + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": row["atomic_claim_id"], + "claim_group_id": row["claim_group_id"], + "calculator_id": calculator_id, + "activation_status": "ACTIVATED", + "calculation_status": calculation_status, + "activation_row_id": activation_row_id, + "descriptor_sha256": descriptor_hashes[calculator_id], + "formula_id": str(formula["formula_id"]), + "formula_sha256": require_hex(formula["formula_sha256"], code="FORMULA_SHA256_INVALID"), + "operand_rows": operand_rows, + "operand_refs": required_operands, + "units": sorted({item["unit_id"] for item in operand_rows if item["unit_id"]}), + "law_value_uses": sorted(law_value_uses, key=lambda item: item["law_value_id"]), + "effective_interval": formula["effective_interval"], + "rounding_rule": formula["rounding_rule"], + "period_rows": [], + "missing_operands": missing_operands, + "result": result, + "result_sha256": result_sha256, + "issue_codes": sorted( + (["CALCULATION_OPERAND_MISSING"] if missing_operands else []) + + [f"CALCULATION_LAW_VALUE_UNRESOLVED:{item}" for item in missing_law_values] + ), + } + receipts.append({**core, "receipt_sha256": digest(core)}) + return sorted(receipts, key=lambda row: row["calculation_receipt_id"]) + + + def call_with_transient_retry( + client: McpClient, + tool: str, + arguments: Mapping[str, Any], + *, + max_attempts: int = 3, + ) -> tuple[Mapping[str, Any], int]: + if not isinstance(max_attempts, int) or isinstance(max_attempts, bool) or not 1 <= max_attempts <= 3: + raise S220Error("RETRIEVAL_RETRY_POLICY_INVALID", "max_attempts must be an integer in 1..3") + for attempt_count in range(1, max_attempts + 1): + try: + return client.call(tool, arguments), attempt_count + except S220Error as exc: + if exc.code != "MCP_TRANSPORT" or attempt_count == max_attempts: + raise + raise S220Error("RETRIEVAL_RETRY_EXHAUSTED", "unreachable retry state") + + + def canonicalize_retrieval_hits( + candidates: Sequence[Mapping[str, Any]], + ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + ordered = sorted( + (dict(row) for row in candidates), + key=lambda row: ( + int(row.get("post_rerank_rank", 2**31 - 1)), + -float(row.get("reranker_score") or row.get("hybrid_score") or 0), + str(row.get("chunk_id", "")), + ), + ) + unique: dict[str, dict[str, Any]] = {} + dropped: list[dict[str, Any]] = [] + for candidate in ordered: + chunk_id = str(candidate.get("chunk_id", "")) + chunk_sha256 = str(candidate.get("chunk_sha256", "")) + if not chunk_id or HEX64.fullmatch(chunk_sha256) is None: + raise S220Error("RETRIEVAL_HIT_IDENTITY_INVALID", "canonical hit requires chunk ID and hash") + if chunk_id in unique: + dropped.append( + { + "chunk_id": chunk_id, + "chunk_sha256": chunk_sha256, + "drop_reason_code": "DUPLICATE_CHUNK", + } + ) + else: + unique[chunk_id] = candidate + return list(unique.values()), dropped + + + def build_retrieval_branch( + atomic: Sequence[Mapping[str, Any]], + bindings: Sequence[Mapping[str, Any]], + corpus_release: Mapping[str, Any], + corpus_scope: Mapping[str, Any], + request: Mapping[str, Any], + ) -> dict[str, Any]: + """C27->C28->C29 closed branch; pending corpus is explicit, never widened.""" + by_atomic = {str(row["atomic_claim_id"]): row for row in bindings} + queries: list[dict[str, Any]] = [] + results: list[dict[str, Any]] = [] + receipts: list[dict[str, Any]] = [] + pack_items: dict[str, list[dict[str, Any]]] = {} + unmapped_by_atomic: dict[str, list[str]] = {} + conflicting_by_atomic: dict[str, list[str]] = {} + corpus_admitted = ( + corpus_release.get("status") == "ADMITTED" + and corpus_release.get("available") is True + and corpus_release.get("sealed") is True + and corpus_scope.get("registry_status") == "APPROVED" + and isinstance(corpus_scope.get("rows"), list) + and bool(corpus_scope.get("rows")) + ) + snapshot_id = str(corpus_release.get("snapshot_id") or "UNAVAILABLE") + snapshot_sha256 = str(corpus_release.get("snapshot_sha256") or ("0" * 64)) + scope_rows = [row for row in corpus_scope.get("rows", []) if isinstance(row, dict)] + ready_query_rows: list[dict[str, Any]] = [] + for claim in atomic: + atomic_id = str(claim["atomic_claim_id"]) + binding = by_atomic[atomic_id] + pack_items[atomic_id] = [] + unmapped_by_atomic[atomic_id] = [] + conflicting_by_atomic[atomic_id] = [] + binding_ready = ( + binding.get("case_type_binding_status") == "BOUND" + and binding.get("sub_rule_binding_status") == "BOUND" + ) + matched_scopes = [ + row for row in scope_rows + if row.get("corpus_scope_id") == binding.get("corpus_scope_id") + and row.get("case_type_id") == binding.get("case_type_id") + and binding.get("sub_rule_id") in row.get("sub_rule_ids", []) + and row.get("element_set_id") == binding.get("element_set_id") + and row.get("approval_status") == "APPROVED" + ] + query_rows: list[dict[str, Any]] = [] + if corpus_admitted and binding_ready and len(matched_scopes) == 1: + scope = matched_scopes[0] + crosswalk = [row for row in scope.get("slot_crosswalk_rows", []) if isinstance(row, dict)] + for doctrine_role in sorted({str(row.get("doctrine_role")) for row in crosswalk if row.get("doctrine_role")}): + expected_ids = sorted( + str(row["corpus_element_id"]) + for row in crosswalk + if row.get("doctrine_role") == doctrine_role and row.get("corpus_element_id") + ) + search_filter = { + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "corpus_scope_id": binding["corpus_scope_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "jurisdiction": "KR", + "snapshot_id": snapshot_id, + "approval_status": "APPROVED", + "effective_on": str(corpus_release.get("effective_on") or corpus_release.get("as_of") or ""), + } + if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", search_filter["effective_on"]): + raise S220Error("CORPUS_EFFECTIVE_DATE_UNBOUND", "admitted corpus requires effective_on") + query_projection = { + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "corpus_element_ids": expected_ids, + } + query_text = json.dumps(query_projection, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + row = { + "query_id": stable_id("Q", atomic_id, doctrine_role), + "atomic_claim_id": atomic_id, + "case_type_id": binding["case_type_id"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "corpus_scope_id": binding["corpus_scope_id"], + "element_set_id": binding["element_set_id"], + "doctrine_role": doctrine_role, + "query_status": "READY", + "query_text": query_text, + "query_sha256": digest(query_text.encode("utf-8")), + "filter": search_filter, + "alpha": corpus_release.get("alpha"), + "top_k": corpus_release.get("top_k"), + "embedding_configuration_digest": corpus_release.get("embedding_configuration_digest"), + "reranker_configuration_digest": corpus_release.get("reranker_configuration_digest"), + "index_configuration_digest": corpus_release.get("index_configuration_digest"), + "expected_corpus_element_ids": expected_ids, + "issue_codes": [], + } + for key in ("embedding_configuration_digest", "reranker_configuration_digest", "index_configuration_digest"): + require_hex(row[key], code=f"CORPUS_{key.upper()}_UNBOUND") + if not isinstance(row["alpha"], (int, float)) or isinstance(row["alpha"], bool) or not 0 <= row["alpha"] <= 1: + raise S220Error("CORPUS_ALPHA_UNBOUND", "admitted corpus requires alpha in [0,1]") + if not isinstance(row["top_k"], int) or isinstance(row["top_k"], bool) or not 1 <= row["top_k"] <= 100: + raise S220Error("CORPUS_TOP_K_UNBOUND", "admitted corpus requires top_k 1..100") + query_rows.append(row) + ready_query_rows.append(row) + query_base = { + "schema_version": "stage2_requirement_fact_query_plan.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "rows": sorted(query_rows, key=lambda row: row["query_id"]), + } + query = {**query_base, "query_plan_sha256": digest(query_base)} + queries.append(query) + if not query_rows: + issue_codes = ["QUERY_NOT_RUN_UNRESOLVED_BINDING"] + if binding_ready and not corpus_admitted: + issue_codes.append("CORPUS_NOT_ADMITTED") + elif binding_ready and len(matched_scopes) != 1: + issue_codes.append("CORPUS_SCOPE_FK_UNRESOLVED") + result_base = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": query["query_plan_sha256"], + "snapshot_id": snapshot_id, + "snapshot_sha256": snapshot_sha256, + "result_status": "NOT_RUN_UNRESOLVED_BINDING", + "selected_hits": [], + "dropped_hits": [], + "issue_codes": sorted(set(issue_codes)), + } + results.append({**result_base, "result_sha256": digest(result_base)}) + + if ready_query_rows: + collection = corpus_release.get("collection") + tenant = corpus_release.get("tenant") + credential_ref = corpus_release.get("credential_ref") + if not all(isinstance(item, str) and item for item in (collection, tenant, credential_ref)): + raise S220Error("CORPUS_TRANSPORT_BINDING_UNBOUND", "collection, tenant and credential_ref are required") + weaviate = McpClient(WEAVIATE_MCP_URL, "weaviate") + try: + weaviate.initialize() + by_plan = {row["atomic_claim_id"]: row for row in queries} + selected_by_atomic: dict[str, list[dict[str, Any]]] = {} + dropped_by_atomic: dict[str, list[dict[str, Any]]] = {} + requests_by_atomic: dict[str, list[dict[str, Any]]] = {} + responses_by_atomic: dict[str, list[Any]] = {} + filters_by_atomic: dict[str, list[dict[str, Any]]] = {} + attempts_by_atomic: dict[str, list[int]] = {} + for query_row in sorted(ready_query_rows, key=lambda row: row["query_id"]): + atomic_id = str(query_row["atomic_claim_id"]) + arguments = { + "collection_name": collection, + "tenant": tenant, + "query": query_row["query_text"], + "filters": query_row["filter"], + "alpha": query_row["alpha"], + "limit": query_row["top_k"], + } + tool_result, attempt_count = call_with_transient_retry( + weaviate, "search_hybrid", arguments, max_attempts=3 + ) + text = _tool_text(tool_result, "search_hybrid") + response = load_json(text.encode("utf-8"), label="weaviate-search-response") + if isinstance(response, dict): + raw_hits = response.get("hits", response.get("objects", response.get("results"))) + else: + raw_hits = response + if not isinstance(raw_hits, list): + raise S220Error("WEAVIATE_RESPONSE_SHAPE", "search response must expose one hit array") + accepted: list[dict[str, Any]] = [] + dropped: list[dict[str, Any]] = [] + for rank, raw_hit in enumerate(raw_hits, 1): + if not isinstance(raw_hit, dict) or not isinstance(raw_hit.get("metadata"), dict): + raise S220Error("WEAVIATE_HIT_SHAPE", "hit and metadata objects are required") + metadata = raw_hit["metadata"] + chunk_id = str(raw_hit.get("chunk_id", metadata.get("chunk_id", ""))) + chunk_sha = str(raw_hit.get("chunk_sha256", metadata.get("chunk_sha256", ""))) + reason: str | None = None + if metadata.get("approval_status") != "APPROVED": + reason = "UNAPPROVED_CHUNK" + elif metadata.get("chunk_sha256") != chunk_sha or HEX64.fullmatch(chunk_sha) is None: + reason = "HASH_MISMATCH" + elif not ( + query_row["filter"]["case_type_id"] in metadata.get("case_type_ids", []) + and query_row["filter"]["sub_rule_id"] in metadata.get("sub_rule_ids", []) + and query_row["filter"]["rule_branch_key"] in metadata.get("rule_branch_keys", []) + and query_row["filter"]["corpus_scope_id"] in metadata.get("corpus_scope_ids", []) + and query_row["filter"]["element_set_id"] in metadata.get("element_set_ids", []) + and query_row["doctrine_role"] in metadata.get("doctrine_roles", []) + and metadata.get("jurisdiction") == "KR" + ): + reason = "FILTER_MISMATCH" + if reason: + dropped.append({"chunk_id": chunk_id or "UNBOUND", "chunk_sha256": chunk_sha if HEX64.fullmatch(chunk_sha) else "0" * 64, "drop_reason_code": reason}) + continue + accepted.append( + { + "chunk_id": chunk_id, + "chunk_sha256": chunk_sha, + "source_document_id": str(raw_hit.get("source_document_id", metadata.get("document_id"))), + "source_document_sha256": str(raw_hit.get("source_document_sha256", metadata.get("document_sha256"))), + "source_locator": str(raw_hit.get("source_locator", metadata.get("source_locator"))), + "pre_rerank_rank": int(raw_hit.get("pre_rerank_rank", rank)), + "hybrid_score": raw_hit.get("hybrid_score", raw_hit.get("score", 0)), + "post_rerank_rank": int(raw_hit.get("post_rerank_rank", rank)), + "reranker_score": raw_hit.get("reranker_score"), + "metadata": metadata, + } + ) + selected_by_atomic.setdefault(atomic_id, []).extend(accepted) + dropped_by_atomic.setdefault(atomic_id, []).extend(dropped) + requests_by_atomic.setdefault(atomic_id, []).append(arguments) + responses_by_atomic.setdefault(atomic_id, []).append(response) + filters_by_atomic.setdefault(atomic_id, []).append(query_row["filter"]) + attempts_by_atomic.setdefault(atomic_id, []).append(attempt_count) + for atomic_id in sorted(requests_by_atomic): + plan = by_plan[atomic_id] + hits, duplicate_drops = canonicalize_retrieval_hits( + selected_by_atomic.get(atomic_id, []) + ) + dropped_by_atomic.setdefault(atomic_id, []).extend(duplicate_drops) + dropped = sorted(dropped_by_atomic.get(atomic_id, []), key=lambda row: (row["chunk_id"], row["drop_reason_code"])) + result_base = { + "schema_version": "stage2_requirement_fact_retrieval_result.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": plan["query_plan_sha256"], + "snapshot_id": snapshot_id, + "snapshot_sha256": snapshot_sha256, + "result_status": "COMPLETE" if hits else "NO_HIT", + "selected_hits": hits, + "dropped_hits": dropped, + "issue_codes": [] if hits else ["RETRIEVAL_NO_HIT"], + } + results.append({**result_base, "result_sha256": digest(result_base)}) + receipt_base = { + "schema_version": "stage2_requirement_fact_retrieval_receipt.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_id": atomic_id, + "query_plan_sha256": plan["query_plan_sha256"], + "request_sha256": digest(requests_by_atomic.get(atomic_id, [])), + "response_sha256": digest(responses_by_atomic.get(atomic_id, [])), + "transport": "MCP_OVER_HTTP_JSON_RPC", + "endpoint": WEAVIATE_MCP_URL, + "tool_name": "search_hybrid", + "collection_name": collection, + "tenant": tenant, + "snapshot_sha256": snapshot_sha256, + "filter_sha256": digest(filters_by_atomic.get(atomic_id, [])), + "configuration_digest": digest( + { + "collection_name": collection, + "tenant": tenant, + "query_count": len(requests_by_atomic.get(atomic_id, [])), + } + ), + "attempt_count": max(attempts_by_atomic.get(atomic_id, [1])), + "replay_status": "NOT_REQUESTED", + "read_only_verified": True, + "credential_ref": credential_ref, + "credential_material_persisted": False, + } + receipts.append({**receipt_base, "receipt_sha256": digest(receipt_base)}) + binding = by_atomic[atomic_id] + scope = next(row for row in scope_rows if row.get("corpus_scope_id") == binding.get("corpus_scope_id")) + crosswalk_by_element = { + str(row["corpus_element_id"]): row + for row in scope.get("slot_crosswalk_rows", []) + if isinstance(row, dict) and row.get("corpus_element_id") + } + for hit in hits: + matched_element = False + for element_id in sorted(str(item) for item in hit["metadata"].get("corpus_element_ids", [])): + crosswalk = crosswalk_by_element.get(element_id) + if crosswalk is None: + unmapped_by_atomic[atomic_id].append(hit["chunk_id"]) + continue + matched_element = True + if crosswalk.get("mapping_status") == "CONFLICT": + conflicting_by_atomic[atomic_id].append(hit["chunk_id"]) + pack_items[atomic_id].append( + { + "corpus_element_id": element_id, + "doctrine_role": crosswalk["doctrine_role"], + "chunk_id": hit["chunk_id"], + "chunk_sha256": hit["chunk_sha256"], + "source_document_id": hit["source_document_id"], + "source_document_sha256": hit["source_document_sha256"], + "source_locator": hit["source_locator"], + "authority_ids": sorted(str(item) for item in hit["metadata"].get("authority_ids", [])), + "proposition_ids": sorted(str(item) for item in hit["metadata"].get("proposition_ids", [])), + "stage1_slot_refs": sorted(str(item) for item in crosswalk.get("stage1_slot_refs", [])), + "mapping_status": crosswalk.get("mapping_status", "UNMAPPED"), + "fact_creation_allowed": False, + "legal_decision_override_allowed": False, + } + ) + if not matched_element: + unmapped_by_atomic[atomic_id].append(hit["chunk_id"]) + finally: + weaviate.close() + key = lambda row: str(row["atomic_claim_id"]) + return { + "queries": sorted(queries, key=key), + "results": sorted(results, key=key), + "receipts": sorted(receipts, key=lambda row: str(row["atomic_claim_id"])), + "pack_items": { + atomic_id: sorted(rows, key=lambda row: (row["corpus_element_id"], row["chunk_id"])) + for atomic_id, rows in sorted(pack_items.items()) + }, + "unmapped_by_atomic": {key: sorted(set(value)) for key, value in sorted(unmapped_by_atomic.items())}, + "conflicting_by_atomic": {key: sorted(set(value)) for key, value in sorted(conflicting_by_atomic.items())}, + } + + + def execute_core(inputs: Mapping[str, Any], assets: Mapping[str, Any], request: Mapping[str, Any], snapshot_digest: str) -> dict[str, Any]: + verdicts = inputs["verdicts"] + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="s2-20-c20-c21") as pool: + c20_future = pool.submit( + build_portfolio, + verdicts, + inputs["verdict_sources"], + assets["disposition_policy"], + ) + c21_future = pool.submit( + build_party_projection, + inputs["party_context"], + inputs.get("object_registry", {}), + ) + atomic_work, partitions, disposition_rows, group_dependencies = c20_future.result() + party_projection = c21_future.result() + for row in atomic_work: + remedy_kinds = sorted( + { + str(item.get("remedy_kind")) + for item in row.get("remedy_candidates", []) + if isinstance(item, dict) and item.get("remedy_kind") + } + ) + row["signature_projection"] = project_signature( + row["normalized_claim_signature"], + row["c20_projection"], + claim_c21_projection(row, party_projection), + {"remedy_mode": remedy_kinds[0] if len(remedy_kinds) == 1 else "UNKNOWN"}, + assets["signature_projection_registry"], + ) + case_registry = assets["case_registry"] + rule_registry = assets["rule_registry"] + bindings: list[dict[str, Any]] = [] + rule_asset_refs: dict[str, dict[str, list[dict[str, str]]]] = {} + issues: list[dict[str, Any]] = [] + for row in atomic_work: + signature = row["signature_projection"]["target_signature"] + case_type_id, status, codes, case_predicate_ids = bind_case(signature, case_registry) + rule, rule_codes, rule_predicate_ids = bind_rule(case_type_id, signature, rule_registry) + fk_codes: list[str] = [] + refs = { + "structured_relief_rule_refs": [], "markdown_source_refs": [], + "renderer_refs": [], "party_set_rule_refs": [], + "ce13_branch_refs": [], "review_policy_refs": [], + } + if rule is not None and case_type_id is not None: + fk_codes, refs = validate_rule_foreign_keys(case_type_id, rule, assets) + all_codes = sorted(set(codes + rule_codes + fk_codes)) + sub_status = ( + "BOUND" + if rule is not None and not fk_codes + else ("CONFLICT" if any("MULTI" in code for code in rule_codes) else "UNRESOLVED") + ) + rule_asset_refs[row["atomic_claim_id"]] = refs + binding = { + "atomic_claim_id": row["atomic_claim_id"], + "target_signature": signature, + "target_signature_sha256": row["signature_projection"]["target_signature_sha256"], + "projection_rule_ids": row["signature_projection"]["projection_rule_ids"], + "case_type_binding_status": status, + "matched_case_type_predicate_ids": case_predicate_ids, + "case_type_id": case_type_id, + "sub_rule_binding_status": sub_status, + "matched_sub_rule_predicate_ids": rule_predicate_ids, + "sub_rule_id": rule.get("sub_rule_id") if rule else None, + "rule_branch_key": rule.get("rule_branch_key") if rule else None, + "renderer_ids": sorted(str(item) for item in (rule.get("renderer_ids", []) if rule else []) if isinstance(item, str)), + "corpus_scope_id": rule.get("corpus_scope_id") if rule else None, + "element_set_id": rule.get("element_set_id") if rule else None, + "party_set_rule_id": rule.get("party_set_rule_id") if rule else None, + "ce13_branch_id": rule.get("ce13_branch_id") if rule else None, + "legal_content_status": "APPROVED" if rule is not None and not fk_codes else ("PENDING_LEGAL_CLASSIFICATION" if status != "BOUND" else "PENDING_LEGAL_CONTENT"), + "binding_issue_codes": all_codes, + } + bindings.append(binding) + for code in all_codes: + issues.append({"issue_id": stable_id("ISS", row["atomic_claim_id"], code), "code": code, "scope_ref": row["atomic_claim_id"], "status": "OPEN"}) + + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="s2-20-c22-c27") as pool: + c22_future = pool.submit( + calculate_all, + atomic_work, + bindings, + assets["activation_registry"], + assets["calculator_descriptor_hashes"], + assets.get("calculator_descriptors", {}), + assets.get("law_value_tables", []), + request, + ) + c27_future = pool.submit( + build_retrieval_branch, + atomic_work, + bindings, + assets["corpus_release"], + assets["corpus_scope"], + request, + ) + calculation_receipts = c22_future.result() + retrieval = c27_future.result() + + groups: dict[str, dict[str, Any]] = {} + for row in atomic_work: + group = groups.setdefault( + row["claim_group_id"], + { + "schema_version": "stage2_claim_group.v1", + "claim_group_id": row["claim_group_id"], + "atomic_claim_ids": [], + "party_refs": [], + "object_refs": [], + "case_type_ids": [], + "rule_branch_keys": [], + "renderer_ids": [], + "party_set_rule_ids": [], + "calculation_receipt_ids": [], + "corpus_scope_ids": [], + "amount_slots": [], + "period_slots": [], + "counter_performance_refs": [], + "procedural_declaration_refs": [], + "dependencies": [], + "drafting_permission": "WORKNOTE_ONLY", + "issue_refs": [], + }, + ) + group["atomic_claim_ids"].append(row["atomic_claim_id"]) + bound_by_id = {row["atomic_claim_id"]: row for row in bindings} + calc_by_atomic: dict[str, list[dict[str, Any]]] = {} + for receipt in calculation_receipts: + calc_by_atomic.setdefault(str(receipt["atomic_claim_id"]), []).append(receipt) + retrieval_by_atomic = {row["atomic_claim_id"]: row for row in retrieval["results"]} + atom_work_by_id = {row["atomic_claim_id"]: row for row in atomic_work} + for group in groups.values(): + group["atomic_claim_ids"].sort() + group_bindings = [bound_by_id[item] for item in group["atomic_claim_ids"]] + group_calculations = [receipt for item in group["atomic_claim_ids"] for receipt in calc_by_atomic.get(item, [])] + group["party_refs"] = sorted({ref for item in group["atomic_claim_ids"] for ref in atom_work_by_id[item]["right_holder_refs"] + atom_work_by_id[item]["obligor_refs"]}) + group["object_refs"] = sorted({ref for item in group["atomic_claim_ids"] for ref in atom_work_by_id[item]["object_refs"]}) + group["case_type_ids"] = sorted({str(row["case_type_id"]) for row in group_bindings if row["case_type_id"]}) + group["rule_branch_keys"] = sorted({str(row["rule_branch_key"]) for row in group_bindings if row["rule_branch_key"]}) + group["renderer_ids"] = sorted({item for row in group_bindings for item in row["renderer_ids"]}) + group["party_set_rule_ids"] = sorted({str(row["party_set_rule_id"]) for row in group_bindings if row["party_set_rule_id"]}) + group["calculation_receipt_ids"] = sorted(str(row["calculation_receipt_id"]) for row in group_calculations) + group["corpus_scope_ids"] = sorted({str(row["corpus_scope_id"]) for row in group_bindings if row["corpus_scope_id"]}) + group["dependencies"] = [row for row in group_dependencies if row["source_group_id"] == group["claim_group_id"]] + permission_codes: list[str] = [] + if any(row["case_type_binding_status"] != "BOUND" or row["sub_rule_binding_status"] != "BOUND" for row in group_bindings): + permission_codes.append("BINDING_NOT_EXACT") + if any(row["activation_status"] in {"ACTIVATION_GAP", "ACTIVATION_CONFLICT"} for row in group_calculations): + permission_codes.append("CALCULATION_NOT_CLOSED") + if any(retrieval_by_atomic[item]["result_status"] != "COMPLETE" for item in group["atomic_claim_ids"]): + permission_codes.append("REQUIREMENT_FACT_PACK_NOT_ADMITTED") + if assets["party_rules_eligible"] is not True: + permission_codes.append("PARTY_SET_RULES_NOT_ADMITTED") + if assets["signature_projection_eligible"] is not True: + permission_codes.append("SIGNATURE_PROJECTION_NOT_ADMITTED") + if assets["disposition_policy_eligible"] is not True: + permission_codes.append("DISPOSITION_POLICY_NOT_ADMITTED") + if assets["authority_admitted"] is not True: + permission_codes.append("AUTHORITY_RELEASE_NOT_ADMITTED") + group_issue_refs: list[str] = [] + for code in sorted(set(permission_codes)): + issue_id = stable_id("ISS", group["claim_group_id"], code) + group_issue_refs.append(issue_id) + issues.append({"issue_id": issue_id, "code": code, "scope_ref": group["claim_group_id"], "status": "OPEN"}) + group["issue_refs"] = group_issue_refs + legal_decisions = {str(atom_work_by_id[item]["legal_decision"]) for item in group["atomic_claim_ids"]} + group["drafting_permission"] = ( + "CLEAN_DRAFT" + if not permission_codes + else ("WORKNOTE_ONLY" if "UNRESOLVED" in legal_decisions else "REVIEW_DRAFT") + ) + + group_rows = sorted(groups.values(), key=lambda row: row["claim_group_id"]) + permission_by_group = {row["claim_group_id"]: row["drafting_permission"] for row in group_rows} + issue_refs_by_scope: dict[str, list[str]] = {} + for issue in issues: + issue_refs_by_scope.setdefault(str(issue["scope_ref"]), []).append(str(issue["issue_id"])) + atomic_rows: list[dict[str, Any]] = [] + for row in atomic_work: + binding = bound_by_id[row["atomic_claim_id"]] + atomic_rows.append( + { + "schema_version": "stage2_atomic_claim.v1", + "atomic_claim_id": row["atomic_claim_id"], + "source_claim_option_ids": [row["claim_option_id"]], + "normalized_signature_sha256": row["normalized_signature_sha256"], + "right_holder_refs": row["right_holder_refs"], + "obligor_refs": row["obligor_refs"], + "object_refs": row["object_refs"], + "remedy_mode": str(row["signature_projection"]["target_signature"].get("remedy_mode") or "UNKNOWN"), + "case_type_binding_status": binding["case_type_binding_status"], + "case_type_id": binding["case_type_id"], + "sub_rule_binding_status": binding["sub_rule_binding_status"], + "sub_rule_id": binding["sub_rule_id"], + "rule_branch_key": binding["rule_branch_key"], + "claim_group_id": row["claim_group_id"], + "drafting_permission": permission_by_group[row["claim_group_id"]], + "source_refs": row["source_refs"], + "issue_refs": sorted(issue_refs_by_scope.get(row["atomic_claim_id"], [])), + } + ) + + issue_base = inputs["issue_ledger"] + base_rows = issue_base.get("issues", []) if isinstance(issue_base, dict) else [] + issue_plan = { + "schema_version": "stage2_issue_ledger_plan.v1", + "base_ledger_sha256": digest(inputs["issue_ledger_raw"]), + "preserved_base_issue_ids": sorted(str(row.get("issue_id")) for row in base_rows if isinstance(row, dict) and row.get("issue_id")), + "plan_issues": sorted(issues, key=lambda row: row["issue_id"]), + } + option_report = { + "schema_version": "stage2_option_conservation_report.v1", + "run_binding_digest": request["run_binding_digest"], + "all_option_ids": sorted(row["claim_option_id"] for row in disposition_rows), + "selected_option_ids": partitions["selected"], + "alternative_option_ids": partitions["alternative"], + "excluded_option_ids": partitions["excluded"], + "deferred_option_ids": partitions["deferred"], + "partition_disjoint": True, + "partition_union_exact": True, + "silent_loss_count": 0, + "duplicate_recovery_issue_refs": [], + "status": "PASS", + } + party_rule_index = { + str(rule.get("party_set_rule_id")): rule + for rule in assets.get("party_rules", {}).get("rules", []) + if isinstance(rule, dict) and rule.get("party_set_rule_id") + } + party_rows: list[dict[str, Any]] = [] + present_role_tokens = set(str(item) for item in party_projection.get("present_role_tokens", [])) + for group in group_rows: + rule_id = group["party_set_rule_ids"][0] if len(group["party_set_rule_ids"]) == 1 else "UNBOUND" + rule = party_rule_index.get(rule_id) + required_roles = sorted( + str(item) for item in (rule.get("required_role_tokens", []) if isinstance(rule, dict) else []) + ) + missing_roles = sorted(set(required_roles) - present_role_tokens) + if rule_id == "UNBOUND" or not isinstance(rule, dict): + missing_roles = sorted(set(missing_roles + ["PARTY_SET_RULE_UNBOUND"])) + identity_conflicts = party_projection["identity_issue_refs"] + party_rows.append( + { + "claim_group_id": group["claim_group_id"], + "party_set_rule_id": rule_id, + "required_role_tokens": required_roles, + "present_party_refs": group["party_refs"], + "missing_role_tokens": missing_roles, + "identity_conflict_refs": identity_conflicts, + "status": "REVIEW_REQUIRED" if missing_roles or identity_conflicts else "COMPLETE", + "issue_refs": group["issue_refs"], + } + ) + party_report = { + "schema_version": "stage2_party_completeness_report.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": party_rows, + "report_status": "REVIEW_REQUIRED" if any(row["status"] != "COMPLETE" for row in party_rows) else "COMPLETE", + } + evidence_inventory = inputs.get("evidence_inventory", {}) + evidence_items = evidence_inventory.get("items", []) if isinstance(evidence_inventory, dict) else [] + if not isinstance(evidence_items, list) or any(not isinstance(item, dict) for item in evidence_items): + raise S220Error("EVIDENCE_INVENTORY_SHAPE", "evidence inventory items must be objects") + evidence_ids = [item.get("evidence_id") for item in evidence_items] + if any(not isinstance(item, str) or not item for item in evidence_ids): + raise S220Error("EVIDENCE_ID_REQUIRED", "every evidence inventory row requires evidence_id") + if len(evidence_ids) != len(set(evidence_ids)): + raise S220Error("EVIDENCE_ID_DUPLICATE", "evidence inventory contains duplicate evidence_id") + exhibit_rows = [ + { + "evidence_ref": evidence_id, + "source_key": evidence_id, + "existing_label": None, + "assigned_label": f"갑 제{ordinal}호증", + "assignment_status": "DETERMINISTICALLY_ASSIGNED", + } + for ordinal, evidence_id in enumerate(sorted(evidence_ids), 1) + ] + exhibit_base = { + "schema_version": "stage2_exhibit_register.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": exhibit_rows, + } + exhibit_register = {**exhibit_base, "register_digest": digest(exhibit_base)} + report_documents = { + "option_conservation_report.json": (option_report, "schemas/relief_plan.schema.json#/$defs/option_conservation_report"), + "party_completeness_report.json": (party_report, "schemas/relief_plan.schema.json#/$defs/party_completeness_report"), + "exhibit_register.json": (exhibit_register, "schemas/relief_plan.schema.json#/$defs/exhibit_register"), + } + issue_raw = canonical_bytes(issue_plan) + plan_base = { + "schema_version": "stage2_canonical_relief_plan.v1", + "producer_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": snapshot_digest, + "parent_stage2_release_sha256": request["expected_parent_stage2_release_sha256"], + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "option_dispositions": disposition_rows, + "atomic_claims": atomic_rows, + "claim_groups": group_rows, + "group_dependencies": group_dependencies, + "selected_option_ids": partitions["selected"], + "alternative_option_ids": partitions["alternative"], + "excluded_option_ids": partitions["excluded"], + "deferred_option_ids": partitions["deferred"], + "report_refs": [ + {"path": path, "schema_ref": schema_ref, "sha256": digest(canonical_bytes(document))} + for path, (document, schema_ref) in sorted(report_documents.items()) + ], + "issue_ledger_ref": { + "path": "issue_ledger.plan.json", + "schema_ref": "schemas/relief_plan.schema.json#/$defs/issue_ledger_plan", + "sha256": digest(issue_raw), + }, + } + plan = {**plan_base, "plan_content_digest": digest(plan_base)} + issue_codes = [str(row["code"]) for row in issue_plan["plan_issues"]] + issue_summary = { + "total": len(issue_codes), + "legal": sum("LEGAL" in code or "AUTHORITY" in code for code in issue_codes), + "binding": sum("BINDING" in code or "RULE" in code for code in issue_codes), + "corpus": sum("CORPUS" in code or "REQUIREMENT_FACT" in code for code in issue_codes), + "calculation": sum("CALCULATION" in code or code.startswith("CE_") for code in issue_codes), + "technical": 0, + } + return { + "plan": plan, + "groups": group_rows, + "atomic": atomic_rows, + "bindings": bindings, + "issues": issue_plan, + "calculations": calculation_receipts, + "retrieval": retrieval, + "rule_asset_refs": rule_asset_refs, + "party_projection": party_projection, + "partitions": partitions, + "option_report": option_report, + "party_report": party_report, + "exhibit_register": exhibit_register, + "asset_hashes": assets["asset_hashes"], + "corpus_release": assets["corpus_release"], + "s2_20_code_sha256": assets["s2_20_code_sha256"], + "schema_documents": assets["schema_documents"], + "issue_summary": issue_summary, + } + + + def _require_mapping(value: Any, *, code: str, label: str) -> Mapping[str, Any]: + if not isinstance(value, dict): + raise S220Error(code, f"object required: {label}") + return value + + + def _verify_binding_text(raw: bytes, request: Mapping[str, Any]) -> None: + try: + text = raw.decode("utf-8", errors="strict") + except UnicodeDecodeError as exc: + raise S220Error("EXECUTOR_BINDING_UTF8", "executor binding must be UTF-8") from exc + marker = "\n - stage_id: S2_20\n" + framed = "\n" + text + if framed.count(marker) != 1: + raise S220Error("EXECUTOR_BINDING_S2_20_ROW", "exactly one S2_20 binding row is required") + section = framed.split(marker, 1)[1] + section = section.split("\n - stage_id:", 1)[0] + section = section.split("\nlegacy_fallbacks:", 1)[0] + + # Parse the closed scalar mapping by indentation instead of treating + # untrusted YAML as a bag of substrings. Sequence values are checked + # separately below; aliases, tags, flow mappings and duplicate scalar + # paths are not accepted in the live trust row. + stack: list[tuple[int, str]] = [] + scalars: dict[Any, str] = {} + for raw_line in section.splitlines(): + if not raw_line.strip(): + continue + if "\t" in raw_line or raw_line.lstrip().startswith(("#", "&", "*", "!", "{", "[")): + raise S220Error("EXECUTOR_BINDING_YAML_PROFILE", "binding uses a forbidden YAML construct") + indent = len(raw_line) - len(raw_line.lstrip(" ")) + stripped = raw_line.strip() + if stripped.startswith("- "): + continue + match = re.fullmatch(r"([A-Za-z0-9_]+):(.*)", stripped) + if match is None: + raise S220Error("EXECUTOR_BINDING_YAML_PROFILE", f"unsupported binding line: {stripped}") + while stack and stack[-1][0] >= indent: + stack.pop() + key, tail = match.group(1), match.group(2).strip() + path = tuple(item[1] for item in stack) + (key,) + if tail: + if path in scalars: + raise S220Error("EXECUTOR_BINDING_DUPLICATE_KEY", ".".join(path)) + if len(tail) >= 2 and tail[0] == tail[-1] and tail[0] in {'"', "'"}: + tail = tail[1:-1] + scalars[path] = tail + else: + stack.append((indent, key)) + + required_values = { + ("binding_id",): "S2-BINDING-S2_20-CODE-EXECUTOR-V1", + ("workflow_id",): "S2_20", + ("execution_class",): "NON-LLM-DETERMINISTIC", + ("active_runtime_authority",): "true", + ("agent_script_ref", "path"): "agent_scripts/Stage_2_S2_20.yml", + ("agent_script_ref", "sha256"): request["expected_s2_20_agent_sha256"], + ("workflow_contract_ref", "path"): "workflows/S2_20_canonical_relief_plan_reduce.yml", + ("inline_code_receipt_ref", "path"): "manifest/s2_20_inline_code_receipt.json", + ("stage2_release_ref", "path"): "manifest/stage2_release.json", + ("stage2_release_ref", "sha256"): request["expected_parent_stage2_release_sha256"], + ("expected_release_sha256",): request["expected_parent_stage2_release_sha256"], + ("agent_script_sha256",): request["expected_s2_20_agent_sha256"], + ("mcp_server_id",): "code-executor", + ("tool_name",): "run_code", + ("language",): "python", + ("network",): "agent-network", + ("timeout_seconds",): "300", + ("runtime_image_status",): "PINNED_VERIFIED", + ("dependency_lock", "requirements"): "httpx==0.28.1", + ("dependency_lock", "lock_status"): "STATIC_PIN_BOUND", + ("localdocs_contract", "fixed_request_path"): "stage2_control/s2_20_request.json", + ("external_mcp_contract", "endpoint"): WEAVIATE_MCP_URL, + ("external_mcp_contract", "operation_mode"): "READ_ONLY_EXACT_FILTER", + ("external_mcp_contract", "admission_status"): "LIVE_VERIFIED", + ("egress_profile_status",): "LIVE_VERIFIED", + ("live_admission_status",): "LIVE_CANARY_ADMITTED", + } + mismatches = sorted( + ".".join(path) + for path, expected in required_values.items() + if scalars.get(path) != expected + ) + for path in (("runtime_image_digest",), ("canonical_code_sha256",)): + if HEX64.fullmatch(scalars.get(path, "")) is None: + mismatches.append(".".join(path)) + for path in (("external_mcp_contract", "collection"), ("external_mcp_contract", "tenant")): + value = scalars.get(path, "") + if not value or value.startswith("PENDING"): + mismatches.append(".".join(path)) + if any(value.startswith("PENDING") for value in scalars.values()): + mismatches.append("pending_scalar") + + allowlist_marker = " read_path_allowlist:\n" + allowlist_tail = section.split(allowlist_marker, 1) + if len(allowlist_tail) != 2: + mismatches.append("localdocs_contract.read_path_allowlist") + allowlist_text = "" + else: + allowlist_text = allowlist_tail[1].split(" write_root_rule:", 1)[0] + required_allowlist_entries = ( + f"{ASSET_ROOT}/schemas/deployment.schema.json", + f"{ASSET_ROOT}/rules/relief/.md", + f"{ASSET_ROOT}/legal_review/.json", + ) + for entry in required_allowlist_entries: + if f" - {entry}\n" not in allowlist_text + "\n": + mismatches.append(f"allowlist:{entry}") + if mismatches: + raise S220Error( + "EXECUTOR_BINDING_NOT_ADMITTED", + "S2_20 binding is incomplete or structurally inconsistent", + details={"mismatches": sorted(set(mismatches))}, + ) + + + def _admission_signed_material(admission: Mapping[str, Any]) -> dict[str, Any]: + return { + key: value + for key, value in admission.items() + if key not in {"signature", "signature_verification_status", "signed_payload_sha256"} + } + + + def _verify_external_trust( + raw_map: Mapping[str, bytes], + fixed: Mapping[str, str], + request: Mapping[str, Any], + schema_documents: Mapping[str, Any], + ) -> tuple[Mapping[str, Any], Mapping[str, Any], Mapping[str, Any]]: + if digest(raw_map[fixed["binding"]]) != request["expected_s2_20_executor_binding_sha256"]: + raise S220Error("EXECUTOR_BINDING_HASH_MISMATCH", "executor binding bytes differ") + _verify_binding_text(raw_map[fixed["binding"]], request) + if digest(raw_map[fixed["agent"]]) != request["expected_s2_20_agent_sha256"]: + raise S220Error("S2_20_AGENT_HASH_MISMATCH", "deployment Agent bytes differ") + if digest(raw_map[fixed["s2_10_release"]]) != request["expected_s2_10_release_sha256"]: + raise S220Error("S2_10_RELEASE_HASH_MISMATCH", "S2_10 child release bytes differ") + + # Host CAS is the first semantic write-authority gate. No plan + # artifact, including a diagnostic, may be published unless this + # exact run-bound receipt has passed its release-bound schema. + host_cas = _require_mapping(load_json(raw_map[fixed["host_cas"]], label=fixed["host_cas"]), code="HOST_CAS_SHAPE", label="host CAS") + _validate_loaded_value( + host_cas, + raw_map[fixed["host_cas"]], + "schemas/deployment.schema.json#/$defs/s2_20_host_cas_receipt", + label=fixed["host_cas"], + schema_documents=schema_documents, + ) + expected_scope = f"S2_20:{request['run_binding_digest']}" + if ( + host_cas.get("run_binding_digest") != request["run_binding_digest"] + or host_cas.get("exclusivity_key") != expected_scope + ): + raise S220Error("HOST_CAS_NOT_ACQUIRED", "host CAS receipt does not prove the exact single writer") + + if digest(raw_map[fixed["admission"]]) != request["detached_admission_receipt_sha256"]: + raise S220Error("ADMISSION_RECEIPT_HASH_MISMATCH", "detached admission bytes differ") + admission = _require_mapping(load_json(raw_map[fixed["admission"]], label=fixed["admission"]), code="ADMISSION_SHAPE", label="admission") + _validate_loaded_value( + admission, + raw_map[fixed["admission"]], + "schemas/deployment.schema.json#/$defs/stage2_deterministic_admission_receipt", + label=fixed["admission"], + schema_documents=schema_documents, + ) + if admission.get("admission_status") not in {"CANARY_ADMITTED", "PRODUCTION_ADMITTED"}: + raise S220Error("ADMISSION_PENDING", "direct execution requires canary or production admission") + if admission.get("parent_release_sha256") != request["expected_parent_stage2_release_sha256"]: + raise S220Error("ADMISSION_PARENT_RELEASE", "admission parent release differs") + if admission.get("executor_binding_sha256") != request["expected_s2_20_executor_binding_sha256"]: + raise S220Error("ADMISSION_EXECUTOR_BINDING", "admission executor binding differs") + if "S2_20" not in admission.get("present_deterministic_stage_ids", []): + raise S220Error("ADMISSION_STAGE_SET", "S2_20 is absent from deterministic admission") + signature = admission.get("signature") + if ( + not isinstance(signature, str) + or not signature + or signature.startswith("PENDING") + or admission.get("signature_verification_status") != "BACKEND_VERIFIED" + ): + raise S220Error("ADMISSION_SIGNATURE", "backend-verified external admission signature is required") + signed_payload_sha256 = require_hex( + admission.get("signed_payload_sha256"), code="ADMISSION_SIGNED_PAYLOAD_HASH" + ) + signed_material = _admission_signed_material(admission) + if signed_payload_sha256 != digest(signed_material): + raise S220Error("ADMISSION_SIGNED_PAYLOAD_HASH", "admission signed payload digest differs") + capability_sha256 = require_hex( + admission.get("backend_capability_receipt_sha256"), + code="ADMISSION_BACKEND_CAPABILITY_HASH", + ) + if capability_sha256 != host_cas.get("backend_capability_receipt_sha256"): + raise S220Error( + "ADMISSION_BACKEND_CAPABILITY_MISMATCH", + "admission verification is not bound to the host CAS backend capability", + ) + matching_stage_receipts = [ + row for row in admission.get("stage_receipts", []) + if isinstance(row, dict) and row.get("asset_id") == "RECEIPT-S2_20-INLINE-CODE" + ] + if ( + len(matching_stage_receipts) != 1 + or matching_stage_receipts[0].get("path") != "manifest/s2_20_inline_code_receipt.json" + or matching_stage_receipts[0].get("sha256") != digest(raw_map[fixed["inline_receipt"]]) + ): + raise S220Error("ADMISSION_STAGE_RECEIPT", "admission does not bind the exact S2_20 inline receipt") + child = _require_mapping(load_json(raw_map[fixed["s2_10_release"]], label=fixed["s2_10_release"]), code="S2_10_RELEASE_SHAPE", label="S2_10 release") + parent_ref = child.get("parent_stage2_release_ref", {}) + if not isinstance(parent_ref, dict) or parent_ref.get("sha256") != request["expected_parent_stage2_release_sha256"]: + raise S220Error("S2_10_PARENT_RELEASE_MISMATCH", "S2_10 child release is not bound to the parent") + release = _require_mapping(load_json(raw_map[fixed["release"]], label=fixed["release"]), code="PARENT_RELEASE_SHAPE", label="parent release") + return release, child, admission + + + def _require_object_self_hash( + value: Mapping[str, Any], field: str, *, code: str, label: str + ) -> str: + declared = value.get(field) + require_hex(declared, code=code) + material = {key: item for key, item in value.items() if key != field} + if declared != digest(material): + raise S220Error(code, f"object self hash differs: {label}") + return str(declared) + + + def _validate_loaded_value( + value: Any, + raw: bytes, + schema_ref: str, + *, + label: str, + schema_documents: Mapping[str, Any], + require_canonical_bytes: bool = True, + ) -> None: + validate_artifact_payload( + canonical_bytes(value), schema_ref, label, schema_documents + ) + if require_canonical_bytes and raw != canonical_bytes(value): + raise S220Error( + "UPSTREAM_CANONICAL_BYTES_MISMATCH", + f"upstream JSON is not canonical bytes: {label}", + ) + + + def _sealed_child_asset_hash(child_release: Mapping[str, Any], path: str) -> str: + rows = child_release.get("sealed_assets", []) + matches = [ + row for row in rows + if isinstance(row, dict) and row.get("path") == path + ] if isinstance(rows, list) else [] + if len(matches) != 1: + raise S220Error( + "S2_10_CHILD_ASSET_BINDING", + f"child release must seal exactly one asset row: {path}", + ) + return require_hex(matches[0].get("sha256"), code="S2_10_CHILD_ASSET_HASH") + + + def _validate_special_legal_asset( + value: Any, + *, + schema_version: str, + identity_field: str, + identity: str, + rows_field: str, + label: str, + ) -> Mapping[str, Any]: + row = _require_mapping( + value, code="SPECIAL_LEGAL_ASSET_SHAPE", label=label + ) + if ( + row.get("schema_version") != schema_version + or row.get(identity_field) != identity + or row.get("status") not in {"PENDING_LEGAL_CONTENT", "LEGAL_CONTENT_ADMITTED"} + or not isinstance(row.get(rows_field), list) + ): + raise S220Error( + "SPECIAL_LEGAL_ASSET_CONTRACT", + f"special legal asset envelope differs: {label}", + ) + if row.get("execution_eligible") is True: + if row.get("status") != "LEGAL_CONTENT_ADMITTED" or not row.get(rows_field): + raise S220Error( + "SPECIAL_LEGAL_ASSET_NOT_ADMITTED", + f"executable special asset lacks admitted rows: {label}", + ) + elif row.get("execution_eligible") is not False: + raise S220Error( + "SPECIAL_LEGAL_ASSET_ELIGIBILITY", + f"execution_eligible must be boolean: {label}", + ) + return row + + + def validate_upstream_handoff( + all_raw: Mapping[str, bytes], + fixed: Mapping[str, str], + request: Mapping[str, Any], + root: str, + cluster_ids: Sequence[str], + wave_paths: Sequence[str], + schema_documents: Mapping[str, Any], + child_release: Mapping[str, Any], + ) -> dict[str, Any]: + """Validate and preserve the complete S2_00/S2_10 handoff consumed here.""" + + def loaded(path: str, schema_ref: str) -> Any: + value = load_json(all_raw[path], label=path) + _validate_loaded_value( + value, + all_raw[path], + schema_ref, + label=path, + schema_documents=schema_documents, + ) + return value + + ingress = _require_mapping( + loaded(fixed["ingress_status"], "schemas/ingress.schema.json#/$defs/ingress_status"), + code="INGRESS_STATUS_SHAPE", + label="ingress status", + ) + cluster_plan = _require_mapping( + loaded(fixed["cluster_plan"], "schemas/context.schema.json#/$defs/cluster_plan"), + code="CLUSTER_PLAN_SHAPE", + label="cluster plan", + ) + bundle_plan = _require_mapping( + loaded(fixed["bundle_plan"], "schemas/context.schema.json#/$defs/bundle_plan"), + code="BUNDLE_PLAN_SHAPE", + label="bundle plan", + ) + context_values: dict[str, Any] = {} + for key, definition in ( + ("case_context", "case_context"), + ("evidence_inventory", "evidence_inventory"), + ("object_registry", "object_registry"), + ("party_context", "party_and_title_context"), + ("slot_crosswalk", "slot_crosswalk"), + ): + context_values[key] = loaded( + fixed[key], f"schemas/context.schema.json#/$defs/{definition}" + ) + issue_ledger = loaded( + fixed["issue_ledger"], + "schemas/review_status.schema.json#/$defs/issue_ledger_base", + ) + + binding_receipt = _require_mapping( + ingress.get("run_binding_receipt"), + code="INGRESS_RUN_BINDING_SHAPE", + label="ingress run binding", + ) + barrier = _require_mapping( + ingress.get("output_barrier"), + code="INGRESS_BARRIER_SHAPE", + label="ingress output barrier", + ) + expected_root = f"{root}/" + if ( + binding_receipt.get("run_binding_digest") != request["run_binding_digest"] + or binding_receipt.get("canonical_output_root") != expected_root + or barrier.get("canonical_output_root") != expected_root + or barrier.get("branch") != "NORMAL" + or barrier.get("written_last") is not True + or barrier.get("non_status_artifacts_read_back_verified") is not True + or ingress.get("route") not in {"TO_S2_10", "TO_S2_10_WITH_ISSUES"} + ): + raise S220Error( + "INGRESS_BARRIER_BINDING", + "S2_00 ingress status does not prove the exact normal-route binding", + ) + if sorted(ingress.get("executable_cluster_ids", [])) != sorted(cluster_ids): + raise S220Error( + "INGRESS_CLUSTER_SET", + "ingress executable cluster set differs from cluster plan", + ) + artifact_rows = barrier.get("artifacts", []) + if not isinstance(artifact_rows, list) or barrier.get("artifact_set_digest") != digest(artifact_rows): + raise S220Error( + "INGRESS_ARTIFACT_SET_DIGEST", + "ingress output artifact set digest differs", + ) + artifact_by_path: dict[str, Mapping[str, Any]] = {} + for row in artifact_rows: + if not isinstance(row, dict) or not isinstance(row.get("path"), str): + raise S220Error("INGRESS_ARTIFACT_ROW_SHAPE", "ingress artifact row is invalid") + if row["path"] in artifact_by_path: + raise S220Error("INGRESS_ARTIFACT_PATH_DUPLICATE", f"duplicate ingress artifact: {row['path']}") + artifact_by_path[row["path"]] = row + s2_00_consumed_paths = [ + fixed[key] + for key in ( + "case_context", "evidence_inventory", "object_registry", "cluster_plan", + "party_context", "slot_crosswalk", "bundle_plan", "issue_ledger", + ) + ] + [f"{root}/context/cluster_slices/{cluster_id}.json" for cluster_id in cluster_ids] + for path in s2_00_consumed_paths: + relative = path.removeprefix(f"{root}/") + row = artifact_by_path.get(relative) + if not isinstance(row, dict) or row.get("raw_sha256") != digest(all_raw[path]): + raise S220Error( + "INGRESS_ARTIFACT_LINK_MISMATCH", + f"ingress barrier does not bind consumed artifact: {relative}", + ) + + expected_context_schema_hash = digest(all_raw[fixed["schema_context"]]) + expected_run_id = ingress.get("run_id") + expected_input_set = binding_receipt.get("input_set_digest") + expected_release_digest = binding_receipt.get("stage2_release_digest") + for label, value in { + "cluster_plan": cluster_plan, + "bundle_plan": bundle_plan, + **context_values, + }.items(): + header = value.get("artifact_header") if isinstance(value, dict) else None + if ( + not isinstance(header, dict) + or header.get("schema_sha256") != expected_context_schema_hash + or header.get("run_id") != expected_run_id + or header.get("input_set_digest") != expected_input_set + or header.get("stage2_release_digest") != expected_release_digest + ): + raise S220Error( + "CONTEXT_HEADER_BINDING", + f"context artifact header differs: {label}", + ) + + cluster_slices: dict[str, Mapping[str, Any]] = {} + slice_raw_hashes: dict[str, str] = {} + for cluster_id in sorted(cluster_ids): + path = f"{root}/context/cluster_slices/{cluster_id}.json" + slice_value = _require_mapping( + loaded(path, "schemas/context.schema.json#/$defs/cluster_slice"), + code="CLUSTER_SLICE_SHAPE", + label=path, + ) + slice_material = {key: value for key, value in slice_value.items() if key != "slice_digest"} + if ( + slice_value.get("cluster_id") != cluster_id + or slice_value.get("immutable") is not True + or slice_value.get("slice_digest") != digest(slice_material) + ): + raise S220Error( + "CLUSTER_SLICE_BINDING", + f"cluster slice identity/self hash differs: {cluster_id}", + ) + header = slice_value.get("artifact_header") + if ( + not isinstance(header, dict) + or header.get("schema_sha256") != expected_context_schema_hash + or header.get("run_id") != expected_run_id + or header.get("input_set_digest") != expected_input_set + or header.get("stage2_release_digest") != expected_release_digest + ): + raise S220Error("CLUSTER_SLICE_HEADER", f"cluster slice header differs: {cluster_id}") + cluster_slices[cluster_id] = slice_value + slice_raw_hashes[cluster_id] = digest(all_raw[path]) + + child_agent_hash = _sealed_child_asset_hash( + child_release, "agent_scripts/Stage_2_S2_10.yml" + ) + child_binding_hash = _sealed_child_asset_hash( + child_release, "deployment/stage2_s2_10_llm_binding.yml" + ) + cohorts = bundle_plan.get("cohorts", []) + if not isinstance(cohorts, list): + raise S220Error("BUNDLE_COHORT_SHAPE", "bundle cohorts must be an array") + cohort_ids: list[str] = [] + executable_ids: list[str] = [] + non_executable_ids: list[str] = [] + member_wave_by_cluster: dict[str, int] = {} + for cohort in cohorts: + if not isinstance(cohort, dict) or not isinstance(cohort.get("bundle_cohort_id"), str): + raise S220Error("BUNDLE_COHORT_SHAPE", "bundle cohort object/ID required") + cohort_id = str(cohort["bundle_cohort_id"]) + cohort_ids.append(cohort_id) + status = cohort.get("cohort_status") + if status == "EXECUTABLE": + executable_ids.append(cohort_id) + elif status == "NON_EXECUTABLE": + non_executable_ids.append(cohort_id) + member_ids = cohort.get("cohort_member_cluster_ids", []) + member_slices = cohort.get("member_slices", []) + if ( + member_ids != sorted(member_ids) + or not isinstance(member_slices, list) + or member_slices != sorted(member_slices, key=lambda row: str(row.get("cluster_id", ""))) + or member_ids != [row.get("cluster_id") for row in member_slices if isinstance(row, dict)] + ): + raise S220Error("BUNDLE_MEMBER_SET", f"bundle membership differs: {cohort_id}") + selected_refs = cohort.get("selected_context_refs", []) + if ( + not isinstance(selected_refs, list) + or [row.get("order_index") for row in selected_refs if isinstance(row, dict)] != list(range(len(selected_refs))) + or cohort.get("selected_context_ordered_refs_sha256") != digest(selected_refs) + ): + raise S220Error("BUNDLE_SELECTED_CONTEXT", f"selected context order/hash differs: {cohort_id}") + if ( + cohort.get("s2_10_agent_sha256") != child_agent_hash + or cohort.get("s2_10_llm_binding_sha256") != child_binding_hash + ): + raise S220Error("BUNDLE_S2_10_BINDING", f"bundle S2_10 binding differs: {cohort_id}") + membership_hash = digest(member_ids) + member_slice_hash = digest(member_slices) + receipt = cohort.get("context_materialization_receipt") + echoed = { + "bundle_cohort_id": cohort_id, + "s2_10_agent_sha256": child_agent_hash, + "s2_10_llm_binding_sha256": child_binding_hash, + "selected_context_ordered_refs_sha256": digest(selected_refs), + "cohort_membership_sha256": membership_hash, + "member_slice_ref_set_sha256": member_slice_hash, + } + materialization = { + "handoff_contract_version": cohort.get("handoff_contract_version"), + **echoed, + } + if ( + not isinstance(receipt, dict) + or any(receipt.get(key) != value for key, value in echoed.items()) + or receipt.get("materialization_input_sha256") != digest(materialization) + ): + raise S220Error("BUNDLE_MATERIALIZATION_RECEIPT", f"materialization receipt differs: {cohort_id}") + if status == "EXECUTABLE": + if receipt.get("verification_status") != "PASS" or cohort.get("reason_codes") != []: + raise S220Error("BUNDLE_EXECUTABLE_STATUS", f"executable cohort is not PASS: {cohort_id}") + for member in member_slices: + cluster_id = str(member["cluster_id"]) + expected_path = f"context/cluster_slices/{cluster_id}.json" + if ( + cluster_id in member_wave_by_cluster + or cluster_id not in slice_raw_hashes + or member.get("path") != expected_path + or member.get("sha256") != slice_raw_hashes[cluster_id] + or not isinstance(member.get("dependency_wave_ordinal"), int) + ): + raise S220Error("BUNDLE_SLICE_LINK", f"bundle slice link differs: {cluster_id}") + member_wave_by_cluster[cluster_id] = int(member["dependency_wave_ordinal"]) + if len(cohort_ids) != len(set(cohort_ids)): + raise S220Error("BUNDLE_COHORT_DUPLICATE", "bundle cohort IDs must be unique") + if ( + executable_ids != bundle_plan.get("executable_bundle_cohort_ids") + or non_executable_ids != bundle_plan.get("non_executable_bundle_cohort_ids") + or set(member_wave_by_cluster) != set(cluster_ids) + ): + raise S220Error("BUNDLE_EXECUTABLE_COVERAGE", "bundle executable coverage differs") + expected_wave_ordinals = list(range(len(wave_paths))) + if sorted(set(member_wave_by_cluster.values())) != expected_wave_ordinals: + raise S220Error("BUNDLE_WAVE_ORDINALS", "bundle wave ordinals are not contiguous/exact") + + status = _require_mapping( + loaded(fixed["s2_10_status"], "schemas/s2_10.schema.json#/$defs/global_publish_status"), + code="S2_10_STATUS_SHAPE", + label="S2_10 publish status", + ) + if ( + status.get("status") != "COMPLETE" + or status.get("route") != "TO_S2_20" + or status.get("run_binding_digest") != request["run_binding_digest"] + or status.get("parent_stage2_release_sha256") != request["expected_parent_stage2_release_sha256"] + or status.get("s2_10_release_sha256") != request["expected_s2_10_release_sha256"] + or status.get("status_written_last") is not True + or sorted(status.get("expected_executable_cluster_ids", [])) != sorted(cluster_ids) + or sorted(status.get("valid_domain_verdict_cluster_ids", [])) != sorted(cluster_ids) + or status.get("terminal_technical_failure_cluster_ids") != [] + ): + raise S220Error("S2_10_BARRIER", "S2_10 global barrier differs") + _require_object_self_hash( + status, "status_sha256", code="S2_10_STATUS_SELF_DIGEST", label="S2_10 publish status" + ) + producer = child_release.get("producer_contract", {}) + producer_digest = producer.get("s2_10_producer_contract_digest") if isinstance(producer, dict) else None + require_hex(producer_digest, code="S2_10_PRODUCER_CONTRACT") + + verdicts: list[Mapping[str, Any]] = [] + verdict_sources: dict[str, dict[str, str]] = {} + item_receipts: dict[str, Mapping[str, Any]] = {} + issue_parts: dict[str, Mapping[str, Any]] = {} + assumption_parts: dict[str, Mapping[str, Any]] = {} + usage_parts: dict[str, Mapping[str, Any]] = {} + request_ids: set[str] = set() + for cluster_id in sorted(cluster_ids): + slice_path = f"{root}/context/cluster_slices/{cluster_id}.json" + verdict_path = f"{root}/map_s2_10/domain_verdicts/{cluster_id}.json" + item_path = f"{root}/map_s2_10/item_receipts/{cluster_id}.json" + issue_path = f"{root}/map_s2_10/issue_patches/{cluster_id}.json" + assumption_path = f"{root}/map_s2_10/assumption_parts/{cluster_id}.json" + usage_path = f"{root}/map_s2_10/usage_parts/{cluster_id}.json" + verdict = _require_mapping( + loaded(verdict_path, "schemas/s2_10.schema.json#/$defs/canonical_domain_verdict"), + code="DOMAIN_VERDICT_SHAPE", + label=cluster_id, + ) + verdict_hash = _require_object_self_hash( + verdict, "domain_verdict_sha256", code="DOMAIN_VERDICT_SELF_DIGEST", label=cluster_id + ) + if ( + verdict.get("cluster_id") != cluster_id + or verdict.get("run_binding_digest") != request["run_binding_digest"] + or verdict.get("cluster_slice_sha256") != digest(all_raw[slice_path]) + or verdict.get("producer_contract_digest") != producer_digest + ): + raise S220Error("DOMAIN_VERDICT_LINK", f"domain verdict link differs: {cluster_id}") + item = _require_mapping( + loaded(item_path, "schemas/s2_10.schema.json#/$defs/item_receipt"), + code="ITEM_RECEIPT_SHAPE", + label=item_path, + ) + _require_object_self_hash( + item, "receipt_sha256", code="ITEM_RECEIPT_SELF_DIGEST", label=cluster_id + ) + if ( + item.get("cluster_id") != cluster_id + or item.get("run_binding_digest") != request["run_binding_digest"] + or item.get("domain_verdict_path") != f"map_s2_10/domain_verdicts/{cluster_id}.json" + or item.get("domain_verdict_sha256") != verdict_hash + or item.get("read_back_sha256") != verdict_hash + or item.get("validation_status") != "PASS" + or item.get("persistence_status") not in {"PUBLISHED", "IDEMPOTENT_BYTE_IDENTICAL"} + or item.get("request_id") != verdict.get("request_id") + or item.get("wave_ordinal") != verdict.get("wave_ordinal") + or item.get("attempt_no") != verdict.get("attempt_no") + or item.get("raw_model_output_sha256") != verdict.get("raw_model_output_sha256") + ): + raise S220Error("ITEM_RECEIPT_INVALID", f"invalid S2_10 item receipt: {cluster_id}") + issue = _require_mapping( + loaded(issue_path, "schemas/s2_10.schema.json#/$defs/issue_part"), + code="ISSUE_PART_SHAPE", + label=issue_path, + ) + assumption = _require_mapping( + loaded(assumption_path, "schemas/s2_10.schema.json#/$defs/assumption_part"), + code="ASSUMPTION_PART_SHAPE", + label=assumption_path, + ) + usage = _require_mapping( + loaded(usage_path, "schemas/s2_10.schema.json#/$defs/usage_part"), + code="USAGE_PART_SHAPE", + label=usage_path, + ) + for part, expected_hash, label in ( + (issue, item.get("issue_part_sha256"), "issue"), + (assumption, item.get("assumption_part_sha256"), "assumption"), + (usage, item.get("usage_part_sha256"), "usage"), + ): + part_hash = _require_object_self_hash( + part, "part_sha256", code=f"{label.upper()}_PART_SELF_DIGEST", label=cluster_id + ) + header = part.get("header") + if ( + part_hash != expected_hash + or not isinstance(header, dict) + or header.get("producer_contract_digest") != producer_digest + or header.get("request_id") != item.get("request_id") + or header.get("run_binding_digest") != request["run_binding_digest"] + or header.get("wave_ordinal") != item.get("wave_ordinal") + or header.get("attempt_no") != item.get("attempt_no") + or header.get("cluster_id") != cluster_id + or header.get("domain_verdict_sha256") != verdict_hash + ): + raise S220Error(f"{label.upper()}_PART_LINK", f"{label} part link differs: {cluster_id}") + if assumption.get("assumptions") != verdict.get("assumptions"): + raise S220Error("ASSUMPTION_PART_CONTENT", f"assumption part differs from verdict: {cluster_id}") + request_ids.add(str(item.get("request_id"))) + verdicts.append(verdict) + verdict_sources[cluster_id] = { + "path": verdict_path.removeprefix(f"{root}/"), + "sha256": digest(all_raw[verdict_path]), + } + item_receipts[cluster_id] = item + issue_parts[cluster_id] = issue + assumption_parts[cluster_id] = assumption + usage_parts[cluster_id] = usage + if len(request_ids) != 1: + raise S220Error("S2_10_REQUEST_ID_SET", "all S2_10 items must share one request ID") + + wave_receipts: list[Mapping[str, Any]] = [] + wave_cluster_union: set[str] = set() + for ordinal, path in enumerate(wave_paths): + wave = _require_mapping( + loaded(path, "schemas/s2_10.schema.json#/$defs/wave_receipt"), + code="WAVE_RECEIPT_SHAPE", + label=path, + ) + _require_object_self_hash( + wave, "receipt_sha256", code="WAVE_RECEIPT_SELF_DIGEST", label=path + ) + expected_clusters = sorted( + cluster_id for cluster_id, wave_ordinal in member_wave_by_cluster.items() + if wave_ordinal == ordinal + ) + if ( + wave.get("request_id") not in request_ids + or wave.get("run_binding_digest") != request["run_binding_digest"] + or wave.get("wave_ordinal") != ordinal + or wave.get("is_final_wave") != (ordinal == len(wave_paths) - 1) + or sorted(wave.get("expected_wave_cluster_ids", [])) != expected_clusters + or sorted(wave.get("valid_domain_verdict_cluster_ids", [])) != expected_clusters + or wave.get("terminal_technical_failure_cluster_ids") != [] + or wave.get("wave_status") != "WAVE_COMPLETE" + or wave.get("route") != ("TO_S2_20" if ordinal == len(wave_paths) - 1 else "NEXT_WAVE") + or wave.get("written_once") is not True + ): + raise S220Error("WAVE_RECEIPT_INVALID", f"terminal wave receipt differs: {ordinal}") + for cluster_id in expected_clusters: + if item_receipts[cluster_id].get("wave_ordinal") != ordinal: + raise S220Error("ITEM_WAVE_LINK", f"item/wave ordinal differs: {cluster_id}") + wave_cluster_union.update(expected_clusters) + wave_receipts.append(wave) + if wave_cluster_union != set(cluster_ids): + raise S220Error("WAVE_CLUSTER_COVERAGE", "terminal wave coverage differs") + if status.get("terminal_wave_receipt_sha256s") != [ + wave["receipt_sha256"] for wave in wave_receipts + ]: + raise S220Error("WAVE_RECEIPT_HASH_LINK", "global status wave hashes differ") + + return { + "ingress_status": ingress, + "cluster_plan": cluster_plan, + "bundle_plan": bundle_plan, + "cluster_slices": cluster_slices, + "verdicts": verdicts, + "verdict_sources": verdict_sources, + "item_receipts": item_receipts, + "issue_patches": issue_parts, + "assumption_parts": assumption_parts, + "usage_parts": usage_parts, + "wave_receipts": wave_receipts, + "case_context": context_values["case_context"], + "evidence_inventory": context_values["evidence_inventory"], + "object_registry": context_values["object_registry"], + "party_context": context_values["party_context"], + "slot_crosswalk": context_values["slot_crosswalk"], + "issue_ledger": issue_ledger, + } + + + def hydrate( + localdocs: McpClient, + request: Mapping[str, Any], + publication_guard: dict[str, bool], + ) -> tuple[dict[str, Any], dict[str, Any], str]: + root = request["stage2_run_root_ref"] + fixed = { + "release": f"{ASSET_ROOT}/manifest/stage2_release.json", + "module_manifest": f"{ASSET_ROOT}/manifest/module_manifest.json", + "s2_10_release": f"{ASSET_ROOT}/manifest/s2_10_release.json", + "authority_release": f"{ASSET_ROOT}/manifest/authority_release.json", + "case_coverage": f"{ASSET_ROOT}/manifest/case_type_coverage.json", + "binding": f"{ASSET_ROOT}/deployment/stage2_code_executor_binding.yml", + "agent": f"{ASSET_ROOT}/agent_scripts/Stage_2_S2_20.yml", + "inline_receipt": f"{ASSET_ROOT}/manifest/s2_20_inline_code_receipt.json", + "admission": request["detached_admission_receipt_ref"], + "host_cas": request["host_cas_receipt_ref"], + "ingress_status": f"{root}/ingress/ingress_status.json", + "case_context": f"{root}/context/case_context.json", + "evidence_inventory": f"{root}/context/evidence_inventory.json", + "object_registry": f"{root}/context/object_registry.json", + "s2_10_status": f"{root}/map_s2_10/s2_10_publish_status.json", + "cluster_plan": f"{root}/context/cluster_plan.json", + "party_context": f"{root}/context/party_and_title_context.json", + "slot_crosswalk": f"{root}/context/slot_crosswalk.json", + "bundle_plan": f"{root}/context/bundle_plan.json", + "issue_ledger": f"{root}/review/issue_ledger.base.json", + "case_registry": f"{ASSET_ROOT}/manifest/case_type_registry.yml", + "rule_registry": f"{ASSET_ROOT}/manifest/case_type_rule_registry.yml", + "signature_projection": f"{ASSET_ROOT}/registry/binding/binding_signature_projection.yml", + "disposition_policy": f"{ASSET_ROOT}/registry/planning/option_disposition_policy.yml", + "party_rules": f"{ASSET_ROOT}/registry/party/party_set_rules.yml", + "relief_rules": f"{ASSET_ROOT}/registry/drafting/case_type_relief_rules.yml", + "review_policy": f"{ASSET_ROOT}/registry/review/review_policy_registry.yml", + "authority_registry": f"{ASSET_ROOT}/registry/authority/authority_registry.yml", + "corpus_scope": f"{ASSET_ROOT}/registry/corpus/requirement_fact_scope.yml", + "activation": f"{ASSET_ROOT}/registry/calculations/calculation_activation_registry.yml", + "general_law_values": f"{ASSET_ROOT}/law_values/general_law_values.yml", + "court_fee_values": f"{ASSET_ROOT}/law_values/court_fee_values.yml", + "corpus_release": f"{ASSET_ROOT}/manifest/corpus_release.json", + "schema_relief_plan": f"{ASSET_ROOT}/schemas/relief_plan.schema.json", + "schema_binding_retrieval": f"{ASSET_ROOT}/schemas/binding_retrieval.schema.json", + "schema_calculation": f"{ASSET_ROOT}/schemas/calculation.schema.json", + "schema_corpus_collection": f"{ASSET_ROOT}/corpus/weaviate_collection.schema.json", + "schema_ingress": f"{ASSET_ROOT}/schemas/ingress.schema.json", + "schema_context": f"{ASSET_ROOT}/schemas/context.schema.json", + "schema_s2_10": f"{ASSET_ROOT}/schemas/s2_10.schema.json", + "schema_review_status": f"{ASSET_ROOT}/schemas/review_status.schema.json", + "schema_deployment": f"{ASSET_ROOT}/schemas/deployment.schema.json", + "inheritance_reserved_share": f"{ASSET_ROOT}/registry/temporal/inheritance_reserved_share.yml", + "forbidden_relief_rules": f"{ASSET_ROOT}/registry/drafting/forbidden_relief_rules.yml", + "counter_performance_rules": f"{ASSET_ROOT}/registry/drafting/counter_performance_rules.yml", + "procedural_declaration_rules": f"{ASSET_ROOT}/registry/drafting/procedural_declaration_rules.yml", + "actio_route_registry": f"{ASSET_ROOT}/routing/actio_pauliana_route_registry.yml", + "actio_mortgage_route": f"{ASSET_ROOT}/routing/actio_pauliana_mortgage_route.yml", + "actio_value_invariants": f"{ASSET_ROOT}/validators/actio_value_compensation_invariants.yml", + "renderer_R01": f"{ASSET_ROOT}/renderers/R01_money_payment.yml", + "renderer_R02": f"{ASSET_ROOT}/renderers/R02_delivery_possession.yml", + "renderer_R03": f"{ASSET_ROOT}/renderers/R03_declaration_registry.yml", + "renderer_R04": f"{ASSET_ROOT}/renderers/R04_special_nonmoney_performance.yml", + "renderer_R05": f"{ASSET_ROOT}/renderers/R05_declaratory.yml", + "renderer_R06": f"{ASSET_ROOT}/renderers/R06_constitutive_judgment_challenge.yml", + } + seed, _ = read_stable(localdocs, fixed.values()) + if digest(seed[fixed["release"]]) != EXPECTED_STAGE2_RELEASE_SHA256 or digest(seed[fixed["release"]]) != request["expected_parent_stage2_release_sha256"]: + raise S220Error("PARENT_RELEASE_HASH_MISMATCH", "parent release bytes differ") + if digest(seed[fixed["s2_10_status"]]) != request["expected_s2_10_publish_status_sha256"]: + raise S220Error("S2_10_STATUS_HASH_MISMATCH", "S2_10 publish status differs") + if digest(seed[fixed["host_cas"]]) != request["host_cas_receipt_sha256"]: + raise S220Error("HOST_CAS_HASH_MISMATCH", "host CAS receipt differs") + seed_schema_documents = build_schema_documents(seed, fixed) + validate_artifact_payload( + canonical_bytes(request), + "schemas/deployment.schema.json#/$defs/s2_20_request", + REQUEST_PATH, + seed_schema_documents, + ) + release, child_release, _ = _verify_external_trust( + seed, fixed, request, seed_schema_documents + ) + publication_guard["external_trust_verified"] = True + release_without_digest = {key: value for key, value in release.items() if key != "release_digest"} + if release.get("release_digest") != digest(release_without_digest): + raise S220Error("PARENT_RELEASE_SELF_DIGEST", "parent release self digest differs") + module_manifest = _require_mapping( + load_json(seed[fixed["module_manifest"]], label=fixed["module_manifest"]), + code="MODULE_MANIFEST_SHAPE", + label="module manifest", + ) + module_ref = release.get("module_manifest_ref", {}) + if not isinstance(module_ref, dict) or module_ref.get("sha256") != digest(seed[fixed["module_manifest"]]): + raise S220Error("MODULE_MANIFEST_RELEASE_BINDING", "parent release does not bind the module manifest") + module_rows = module_manifest.get("modules", module_manifest.get("module_rows", [])) + by_module_path = { + str(row.get("path")): row + for row in module_rows + if isinstance(row, dict) and isinstance(row.get("path"), str) + } + release_bound_keys = ( + "case_registry", "rule_registry", "signature_projection", "disposition_policy", + "party_rules", "relief_rules", "review_policy", "authority_registry", + "corpus_scope", "activation", "authority_release", "corpus_release", "case_coverage", + "general_law_values", "court_fee_values", + "schema_relief_plan", "schema_binding_retrieval", "schema_calculation", "schema_corpus_collection", + "schema_ingress", "schema_context", "schema_s2_10", "schema_review_status", "schema_deployment", + "inheritance_reserved_share", "forbidden_relief_rules", "counter_performance_rules", + "procedural_declaration_rules", "actio_route_registry", "actio_mortgage_route", + "actio_value_invariants", + "renderer_R01", "renderer_R02", "renderer_R03", "renderer_R04", "renderer_R05", "renderer_R06", + ) + for key in release_bound_keys: + relative = fixed[key].removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(seed[fixed[key]]): + raise S220Error("MODULE_MEMBER_HASH_MISMATCH", f"module manifest does not bind: {relative}") + for key in ("schema_context", "schema_s2_10", "schema_review_status"): + relative = fixed[key].removeprefix(f"{ASSET_ROOT}/") + if _sealed_child_asset_hash(child_release, relative) != digest(seed[fixed[key]]): + raise S220Error( + "S2_10_CHILD_SCHEMA_HASH_MISMATCH", + f"S2_10 child release does not bind schema: {relative}", + ) + cluster_plan = load_json(seed[fixed["cluster_plan"]], label=fixed["cluster_plan"]) + _validate_loaded_value( + cluster_plan, + seed[fixed["cluster_plan"]], + "schemas/context.schema.json#/$defs/cluster_plan", + label=fixed["cluster_plan"], + schema_documents=seed_schema_documents, + ) + ingress_status = load_json(seed[fixed["ingress_status"]], label=fixed["ingress_status"]) + _validate_loaded_value( + ingress_status, + seed[fixed["ingress_status"]], + "schemas/ingress.schema.json#/$defs/ingress_status", + label=fixed["ingress_status"], + schema_documents=seed_schema_documents, + ) + bundle_plan = load_json(seed[fixed["bundle_plan"]], label=fixed["bundle_plan"]) + _validate_loaded_value( + bundle_plan, + seed[fixed["bundle_plan"]], + "schemas/context.schema.json#/$defs/bundle_plan", + label=fixed["bundle_plan"], + schema_documents=seed_schema_documents, + ) + status = load_json(seed[fixed["s2_10_status"]], label=fixed["s2_10_status"]) + if not isinstance(status, dict) or status.get("status") != "COMPLETE" or status.get("route") != "TO_S2_20": + raise S220Error("S2_10_BARRIER", "S2_10 must be COMPLETE/TO_S2_20") + cluster_ids = cluster_plan.get("executable_cluster_ids", []) if isinstance(cluster_plan, dict) else [] + if ( + not isinstance(cluster_ids, list) + or any(not isinstance(item, str) or CLUSTER_ID.fullmatch(item) is None for item in cluster_ids) + ): + raise S220Error("CLUSTER_PLAN_SHAPE", "executable_cluster_ids must be canonical cluster IDs") + dynamic_paths: list[str] = [] + seed_activation = _require_mapping(load_json(seed[fixed["activation"]], label=fixed["activation"]), code="ACTIVATION_REGISTRY_SHAPE", label="activation registry") + descriptor_paths: dict[str, str] = {} + for descriptor in seed_activation.get("calculator_descriptors", []): + if not isinstance(descriptor, dict) or not isinstance(descriptor.get("calculator_id"), str) or not isinstance(descriptor.get("descriptor_path"), str): + raise S220Error("CALCULATOR_DESCRIPTOR_SHAPE", "calculator descriptor ID/path required") + path = join_path(ASSET_ROOT, descriptor["descriptor_path"]) + descriptor_paths[descriptor["calculator_id"]] = path + dynamic_paths.append(path) + seed_rule_registry = _require_mapping( + load_json(seed[fixed["rule_registry"]], label=fixed["rule_registry"]), + code="RULE_REGISTRY_SHAPE", + label="rule registry", + ) + release_selected_legal_paths: set[str] = set() + for registry_key, rows_key in ( + ("case_registry", "rows"), + ("party_rules", "rules"), + ("corpus_scope", "rows"), + ): + registry_value = _require_mapping( + load_json(seed[fixed[registry_key]], label=fixed[registry_key]), + code="LEGAL_REGISTRY_SHAPE", + label=registry_key, + ) + for legal_row in registry_value.get(rows_key, []): + if isinstance(legal_row, dict) and isinstance(legal_row.get("legal_review_receipt_ref"), str): + legal_review_ref = safe_path(str(legal_row["legal_review_receipt_ref"])) + if LEGAL_REVIEW_PATH.fullmatch(legal_review_ref) is None: + raise S220Error( + "LEGAL_REVIEW_PATH_FAMILY", + f"legal review receipt is outside the closed family: {legal_review_ref}", + ) + release_selected_legal_paths.add( + join_path(ASSET_ROOT, legal_review_ref) + ) + for rule_row in seed_rule_registry.get("rows", []): + if not isinstance(rule_row, dict): + continue + for branch in rule_row.get("rule_branches", []): + if not isinstance(branch, dict): + continue + for branch_path_key in ("markdown_source_path", "legal_review_receipt_ref"): + if isinstance(branch.get(branch_path_key), str): + branch_ref = safe_path(str(branch[branch_path_key])) + path_family = RELIEF_RULE_PATH if branch_path_key == "markdown_source_path" else LEGAL_REVIEW_PATH + if path_family.fullmatch(branch_ref) is None: + raise S220Error( + "LEGAL_ASSET_PATH_FAMILY", + f"{branch_path_key} is outside the closed family: {branch_ref}", + ) + release_selected_legal_paths.add( + join_path(ASSET_ROOT, branch_ref) + ) + dynamic_paths.extend(sorted(release_selected_legal_paths)) + for cluster_id in sorted(cluster_ids): + dynamic_paths.extend( + [ + f"{root}/context/cluster_slices/{cluster_id}.json", + f"{root}/map_s2_10/domain_verdicts/{cluster_id}.json", + f"{root}/map_s2_10/item_receipts/{cluster_id}.json", + f"{root}/map_s2_10/issue_patches/{cluster_id}.json", + f"{root}/map_s2_10/assumption_parts/{cluster_id}.json", + f"{root}/map_s2_10/usage_parts/{cluster_id}.json", + ] + ) + bundle_wave_ordinals = sorted({ + int(member["dependency_wave_ordinal"]) + for cohort in bundle_plan.get("cohorts", []) + if isinstance(cohort, dict) and cohort.get("cohort_status") == "EXECUTABLE" + for member in cohort.get("member_slices", []) + if isinstance(member, dict) and isinstance(member.get("dependency_wave_ordinal"), int) + }) + if ( + not bundle_wave_ordinals + or bundle_wave_ordinals[-1] >= len(cluster_ids) + or bundle_wave_ordinals != list(range(bundle_wave_ordinals[-1] + 1)) + ): + raise S220Error("BUNDLE_WAVE_ORDINALS", "executable bundle wave ordinals must be contiguous from zero") + wave_paths = [ + f"{root}/map_s2_10/wave_receipts/wave-{ordinal:04d}.json" + for ordinal in bundle_wave_ordinals + ] + dynamic_paths.extend(wave_paths) + all_raw, snapshot_digest = read_stable(localdocs, list(fixed.values()) + dynamic_paths) + schema_documents = build_schema_documents(all_raw, fixed) + upstream = validate_upstream_handoff( + all_raw, + fixed, + request, + root, + cluster_ids, + wave_paths, + schema_documents, + child_release, + ) + activation = load_json(all_raw[fixed["activation"]], label=fixed["activation"]) + case_registry = _require_mapping(load_json(all_raw[fixed["case_registry"]], label=fixed["case_registry"]), code="CASE_REGISTRY_SHAPE", label="case registry") + rule_registry = _require_mapping(load_json(all_raw[fixed["rule_registry"]], label=fixed["rule_registry"]), code="RULE_REGISTRY_SHAPE", label="rule registry") + case_rows = case_registry.get("rows", case_registry.get("case_types", [])) + rule_rows = rule_registry.get("rows", rule_registry.get("case_type_rule_rows", [])) + if not isinstance(case_rows, list) or len(case_rows) != 137 or len({row.get("case_type_id") for row in case_rows if isinstance(row, dict)}) != 137: + raise S220Error("CASE_REGISTRY_137_CLOSURE", "case registry must contain exactly 137 unique case_type_id rows") + if not isinstance(rule_rows, list) or len(rule_rows) != 137 or {row.get("case_type_id") for row in rule_rows if isinstance(row, dict)} != {row.get("case_type_id") for row in case_rows if isinstance(row, dict)}: + raise S220Error("RULE_REGISTRY_137_CLOSURE", "rule registry must cover the exact case registry set") + if not isinstance(activation, dict) or len(activation.get("calculator_descriptors", [])) != 17: + raise S220Error("CALCULATOR_17_CLOSURE", "calculation registry must enumerate CE-01..13 and CE-R1..R4") + for path in descriptor_paths.values(): + relative = path.removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(all_raw[path]): + raise S220Error("CALCULATOR_MODULE_HASH_MISMATCH", f"calculator descriptor is not release-bound: {relative}") + for path in sorted(release_selected_legal_paths): + relative = path.removeprefix(f"{ASSET_ROOT}/") + row = by_module_path.get(relative) + if not isinstance(row, dict) or row.get("sha256") != digest(all_raw[path]): + raise S220Error( + "LEGAL_ASSET_MODULE_HASH_MISMATCH", + f"selected legal asset is not release-bound: {relative}", + ) + inputs = { + **upstream, + "issue_ledger_raw": all_raw[fixed["issue_ledger"]], + } + corpus_release = _require_mapping(load_json(all_raw[fixed["corpus_release"]], label=fixed["corpus_release"]), code="CORPUS_RELEASE_SHAPE", label="corpus release") + signature_projection_registry = _require_mapping( + load_json(all_raw[fixed["signature_projection"]], label=fixed["signature_projection"]), + code="SIGNATURE_PROJECTION_REGISTRY_SHAPE", + label="signature projection registry", + ) + disposition_policy = _require_mapping( + load_json(all_raw[fixed["disposition_policy"]], label=fixed["disposition_policy"]), + code="DISPOSITION_POLICY_SHAPE", + label="option disposition policy", + ) + party_rules = _require_mapping( + load_json(all_raw[fixed["party_rules"]], label=fixed["party_rules"]), + code="PARTY_RULES_SHAPE", + label="party-set rules", + ) + corpus_scope = _require_mapping( + load_json(all_raw[fixed["corpus_scope"]], label=fixed["corpus_scope"]), + code="CORPUS_SCOPE_SHAPE", + label="requirement-fact scope registry", + ) + special_asset_contracts = { + "inheritance_reserved_share": ( + "stage2.s2_20.inheritance_reserved_share_temporal.v1", + "registry_id", "S2-20-INHERITANCE-RESERVED-SHARE-TEMPORAL", "branch_rows", + ), + "forbidden_relief_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-FORBIDDEN-RELIEF-RULES", "rule_rows", + ), + "counter_performance_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-COUNTER-PERFORMANCE-RULES", "rule_rows", + ), + "procedural_declaration_rules": ( + "stage2.s2_20.drafting_rule_registry.v1", + "registry_id", "S2-20-PROCEDURAL-DECLARATION-RULES", "rule_rows", + ), + "actio_route_registry": ( + "stage2.s2_20.actio_pauliana_route_registry.v1", + "registry_id", "S2-20-ACTIO-PAULIANA-ROUTES", "route_rows", + ), + "actio_mortgage_route": ( + "stage2.s2_20.actio_pauliana_mortgage_route.v1", + "registry_id", "S2-20-ACTIO-MORTGAGE-ROUTES", "route_rows", + ), + "actio_value_invariants": ( + "stage2.s2_20.actio_value_compensation_invariants.v1", + "validator_id", "S2-20-ACTIO-VALUE-COMPENSATION-INVARIANTS", "invariant_rows", + ), + } + special_legal_assets = { + key: _validate_special_legal_asset( + load_json(all_raw[fixed[key]], label=fixed[key]), + schema_version=contract[0], + identity_field=contract[1], + identity=contract[2], + rows_field=contract[3], + label=fixed[key], + ) + for key, contract in special_asset_contracts.items() + } + assets = { + "case_registry": case_registry, + "rule_registry": rule_registry, + "activation_registry": activation, + "corpus_release": corpus_release, + "corpus_scope": corpus_scope, + "party_rules": party_rules, + "relief_rules": load_json(all_raw[fixed["relief_rules"]], label=fixed["relief_rules"]), + "review_policy": load_json(all_raw[fixed["review_policy"]], label=fixed["review_policy"]), + "signature_projection_registry": signature_projection_registry, + "disposition_policy": disposition_policy, + "special_legal_assets": special_legal_assets, + "party_rules_eligible": party_rules.get("registry_status") == "APPROVED", + "signature_projection_eligible": signature_projection_registry.get("registry_status") == "APPROVED", + "disposition_policy_eligible": disposition_policy.get("policy_status") == "APPROVED", + "authority_admitted": load_json(all_raw[fixed["authority_release"]], label=fixed["authority_release"]).get("status") == "ADMITTED", + "asset_hashes": { + "case_registry": digest(all_raw[fixed["case_registry"]]), + "rule_registry": digest(all_raw[fixed["rule_registry"]]), + "signature_projection": digest(all_raw[fixed["signature_projection"]]), + "corpus_release": digest(all_raw[fixed["corpus_release"]]), + "relief_rules": digest(all_raw[fixed["relief_rules"]]), + "party_rules": digest(all_raw[fixed["party_rules"]]), + "review_policy": digest(all_raw[fixed["review_policy"]]), + "corpus_scope": digest(all_raw[fixed["corpus_scope"]]), + **{ + key: digest(all_raw[fixed[key]]) + for key in sorted(special_legal_assets) + }, + }, + "calculator_descriptor_hashes": { + calculator_id: digest(all_raw[path]) for calculator_id, path in sorted(descriptor_paths.items()) + }, + "calculator_descriptors": { + calculator_id: load_json(all_raw[path], label=path) + for calculator_id, path in sorted(descriptor_paths.items()) + }, + "law_value_tables": [ + load_json(all_raw[fixed["general_law_values"]], label=fixed["general_law_values"]), + load_json(all_raw[fixed["court_fee_values"]], label=fixed["court_fee_values"]), + ], + "renderer_descriptors": { + renderer_id: load_json( + all_raw[fixed[f"renderer_{renderer_id}"]], + label=fixed[f"renderer_{renderer_id}"], + ) + for renderer_id in ("R01", "R02", "R03", "R04", "R05", "R06") + }, + "renderer_hashes": { + renderer_id: digest(all_raw[fixed[f"renderer_{renderer_id}"]]) + for renderer_id in ("R01", "R02", "R03", "R04", "R05", "R06") + }, + "release_selected_legal_bytes": { + path.removeprefix(f"{ASSET_ROOT}/"): all_raw[path] + for path in sorted(release_selected_legal_paths) + }, + "schema_documents": schema_documents, + } + inline_receipt = _require_mapping(load_json(all_raw[fixed["inline_receipt"]], label=fixed["inline_receipt"]), code="INLINE_RECEIPT_SHAPE", label="inline receipt") + canonical_code = inline_receipt.get("canonical_code", {}) + code_sha256 = canonical_code.get("code_sha256") if isinstance(canonical_code, dict) else None + assets["s2_20_code_sha256"] = require_hex(code_sha256, code="INLINE_CODE_HASH_INVALID") + return inputs, assets, snapshot_digest + + + def _artifact_ref(path: str, schema_ref: str, payload: bytes) -> dict[str, str]: + return {"path": path, "schema_ref": schema_ref, "sha256": digest(payload)} + + + def artifact_payloads( + result: Mapping[str, Any], request: Mapping[str, Any] + ) -> dict[str, tuple[bytes, str, str]]: + plan = result["plan"] + groups = result["groups"] + claim_groups_base = { + "schema_version": "stage2_claim_groups.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_groups": groups, + "group_dependencies": plan["group_dependencies"], + } + claim_groups = {**claim_groups_base, "document_digest": digest(claim_groups_base)} + bindings = result["bindings"] + binding_base = { + "schema_version": "stage2_case_type_bindings.v1", + "run_binding_digest": request["run_binding_digest"], + "case_type_registry_sha256": result["asset_hashes"]["case_registry"], + "case_type_rule_registry_sha256": result["asset_hashes"]["rule_registry"], + "binding_signature_projection_sha256": result["asset_hashes"]["signature_projection"], + "bindings": bindings, + "bound_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if row["case_type_binding_status"] == "BOUND" and row["sub_rule_binding_status"] == "BOUND"), + "unresolved_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if "UNRESOLVED" in {row["case_type_binding_status"], row["sub_rule_binding_status"]}), + "conflict_atomic_claim_ids": sorted(row["atomic_claim_id"] for row in bindings if "CONFLICT" in {row["case_type_binding_status"], row["sub_rule_binding_status"]}), + } + binding_document = {**binding_base, "document_digest": digest(binding_base)} + calculation_state_counts = { + state: sum(row["activation_status"] == state for row in result["calculations"]) + for state in ("ACTIVATED", "NOT_APPLICABLE", "ACTIVATION_GAP", "ACTIVATION_CONFLICT") + } + calculation_report_base = { + "schema_version": "stage2.calculation_report.v1", + "run_binding_digest": request["run_binding_digest"], + "atomic_claim_ids": sorted({row["atomic_claim_id"] for row in result["calculations"]}), + "receipt_refs": sorted(f"calculation_receipts/{row['calculation_receipt_id']}.json" for row in result["calculations"]), + "receipt_sha256s": sorted(digest(canonical_bytes(row)) for row in result["calculations"]), + "state_counts": calculation_state_counts, + "missing_operand_ids": sorted({item for row in result["calculations"] for item in row["missing_operands"]}), + "issue_codes": sorted({item for row in result["calculations"] for item in row["issue_codes"]}), + } + calculation_report = {**calculation_report_base, "report_sha256": digest(calculation_report_base)} + filing_metrics_base = { + "schema_version": "stage2.filing_metrics.v1", + "run_binding_digest": request["run_binding_digest"], + "rows": [], + "filing_decision_status": "REVIEW_REQUIRED", + "source_ce13_receipt_ids": [], + } + filing_metrics = {**filing_metrics_base, "report_sha256": digest(filing_metrics_base)} + payloads: dict[str, tuple[bytes, str, str]] = { + "canonical_relief_plan.json": (canonical_bytes(plan), "schemas/relief_plan.schema.json#/$defs/canonical_relief_plan", "C35"), + "claim_groups.json": (canonical_bytes(claim_groups), "schemas/relief_plan.schema.json#/$defs/claim_groups_document", "C30"), + "case_type_bindings.json": (canonical_bytes(binding_document), "schemas/binding_retrieval.schema.json#/$defs/case_type_bindings_document", "C26"), + "issue_ledger.plan.json": (canonical_bytes(result["issues"]), "schemas/relief_plan.schema.json#/$defs/issue_ledger_plan", "C30"), + "calculation_report.json": (canonical_bytes(calculation_report), "schemas/calculation.schema.json#/$defs/calculation_report", "C22"), + "filing_metrics.json": (canonical_bytes(filing_metrics), "schemas/calculation.schema.json#/$defs/filing_metrics", "C22"), + "exhibit_register.json": (canonical_bytes(result["exhibit_register"]), "schemas/relief_plan.schema.json#/$defs/exhibit_register", "C30"), + "option_conservation_report.json": (canonical_bytes(result["option_report"]), "schemas/relief_plan.schema.json#/$defs/option_conservation_report", "C20"), + "party_completeness_report.json": (canonical_bytes(result["party_report"]), "schemas/relief_plan.schema.json#/$defs/party_completeness_report", "C30"), + } + for receipt in result["calculations"]: + payloads[f"calculation_receipts/{receipt['calculation_receipt_id']}.json"] = (canonical_bytes(receipt), "schemas/calculation.schema.json#/$defs/calculation_receipt", "C22") + result_by_atomic = {row["atomic_claim_id"]: row for row in result["retrieval"]["results"]} + receipt_by_atomic = {row["atomic_claim_id"]: row for row in result["retrieval"]["receipts"]} + for group in groups: + group_id = group["claim_group_id"] + group_bindings = [row for row in bindings if row["atomic_claim_id"] in group["atomic_claim_ids"]] + group_rule_refs = [ + result["rule_asset_refs"].get(atomic_id, {}) + for atomic_id in group["atomic_claim_ids"] + ] + def merged_rule_refs(key: str) -> list[dict[str, str]]: + indexed = { + (str(ref["path"]), str(ref["sha256"]), str(ref["json_pointer"])): ref + for row in group_rule_refs + for ref in row.get(key, []) + if isinstance(ref, dict) + } + return [indexed[index] for index in sorted(indexed)] + rule_base = { + "schema_version": "stage2_rule_context_pack.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "binding_rows": group_bindings, + "structured_relief_rule_refs": merged_rule_refs("structured_relief_rule_refs"), + "markdown_source_refs": merged_rule_refs("markdown_source_refs"), + "renderer_refs": merged_rule_refs("renderer_refs"), + "party_set_rule_refs": merged_rule_refs("party_set_rule_refs"), + "ce13_branch_refs": merged_rule_refs("ce13_branch_refs"), + "review_policy_refs": merged_rule_refs("review_policy_refs"), + "authority_class": "RELIEF_ONLY", + } + rule_pack = {**rule_base, "pack_sha256": digest(rule_base)} + group_receipts = [receipt_by_atomic[item] for item in group["atomic_claim_ids"] if item in receipt_by_atomic] + fact_base = { + "schema_version": "stage2_requirement_fact_pack.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "atomic_claim_ids": group["atomic_claim_ids"], + "corpus_release_sha256": result["asset_hashes"]["corpus_release"], + "snapshot_id": str(result["corpus_release"].get("snapshot_id") or "UNAVAILABLE"), + "snapshot_sha256": str(result["corpus_release"].get("snapshot_sha256") or ("0" * 64)), + "retrieval_receipt_refs": [ + {"path": f"retrieval_receipts/{row['atomic_claim_id']}.json", "sha256": digest(canonical_bytes(row)), "json_pointer": ""} + for row in group_receipts + ], + "items": sorted( + [ + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["pack_items"].get(atomic_id, []) + ], + key=lambda row: (row["corpus_element_id"], row["chunk_id"]), + ), + "unmapped_chunk_ids": sorted( + { + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["unmapped_by_atomic"].get(atomic_id, []) + } + ), + "conflicting_chunk_ids": sorted( + { + item + for atomic_id in group["atomic_claim_ids"] + for item in result["retrieval"]["conflicting_by_atomic"].get(atomic_id, []) + } + ), + "authority_class": "CAUSE_AND_ELEMENT_CHECK", + "issue_codes": sorted({code for item in group["atomic_claim_ids"] for code in result_by_atomic[item]["issue_codes"]}), + } + fact_pack = {**fact_base, "pack_sha256": digest(fact_base)} + gap_rows = [{ + "corpus_element_id": "UNRESOLVED", + "doctrine_role": "element", + "stage1_slot_refs": [], + "fact_refs": [], + "evidence_refs": [], + "coverage_status": "GAP", + "issue_refs": group["issue_refs"], + }] + gap_document = { + "schema_version": "stage2_element_evidence_gap.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "rows": gap_rows, + "gap_count": 1, + "conflict_count": 0, + } + rule_path = f"rule_context_packs/{group_id}.json" + fact_path = f"requirement_fact_packs/{group_id}.json" + gap_path = f"element_evidence_gap/{group_id}.json" + rule_raw = canonical_bytes(rule_pack) + fact_raw = canonical_bytes(fact_pack) + gap_raw = canonical_bytes(gap_document) + payloads[rule_path] = (rule_raw, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", "C26") + payloads[fact_path] = (fact_raw, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", "C29") + payloads[gap_path] = (gap_raw, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", "C30") + calculation_refs = [ + _artifact_ref( + f"calculation_receipts/{row['calculation_receipt_id']}.json", + "schemas/calculation.schema.json#/$defs/calculation_receipt", + canonical_bytes(row), + ) + for row in result["calculations"] if row["atomic_claim_id"] in group["atomic_claim_ids"] + ] + group_slice_base = { + "schema_version": "stage2_s2_20_group_slice.v1", + "run_binding_digest": request["run_binding_digest"], + "claim_group_id": group_id, + "frozen_plan_sha256": digest(canonical_bytes(plan)), + "claim_group": group, + "rule_context_pack_ref": _artifact_ref(rule_path, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", rule_raw), + "requirement_fact_pack_ref": _artifact_ref(fact_path, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", fact_raw), + "element_evidence_gap_ref": _artifact_ref(gap_path, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", gap_raw), + "calculation_receipt_refs": calculation_refs, + "issue_refs": group["issue_refs"], + "drafting_permission": group["drafting_permission"], + } + group_slice = {**group_slice_base, "group_slice_sha256": digest(group_slice_base)} + group_path = f"group_slices/{group_id}.json" + group_raw = canonical_bytes(group_slice) + payloads[group_path] = (group_raw, "schemas/relief_plan.schema.json#/$defs/group_slice", "C35") + for segment_id, authority_class, refs in ( + ("P31_rule_and_pack", "RELIEF_ONLY_AND_CAUSE_CHECK", [ + _artifact_ref(rule_path, "schemas/binding_retrieval.schema.json#/$defs/rule_context_pack", rule_raw), + _artifact_ref(fact_path, "schemas/binding_retrieval.schema.json#/$defs/requirement_fact_pack", fact_raw), + _artifact_ref(gap_path, "schemas/relief_plan.schema.json#/$defs/element_evidence_gap", gap_raw), + ]), + ("P32_common_authority", "COMMON_AUTHORITY", []), + ): + segment_base = { + "schema_version": "stage2_s2_20_prompt_segment.v1", + "segment_id": segment_id, + "claim_group_id": group_id, + "authority_class": authority_class, + "ordered_asset_refs": refs, + "source_group_slice_sha256": digest(group_raw), + } + segment = {**segment_base, "segment_payload_sha256": digest(segment_base)} + payloads[f"prompt_segments/{group_id}/{segment_id}.json"] = (canonical_bytes(segment), "schemas/relief_plan.schema.json#/$defs/prompt_segment", "C35") + for row in result["retrieval"]["queries"]: + payloads[f"query_plans/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/query_plan", "C27") + for row in result["retrieval"]["results"]: + payloads[f"retrieval_results/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/retrieval_result", "C28") + for row in result["retrieval"]["receipts"]: + payloads[f"retrieval_receipts/{row['atomic_claim_id']}.json"] = (canonical_bytes(row), "schemas/binding_retrieval.schema.json#/$defs/retrieval_receipt", "C28") + return payloads + + + def build_schema_documents( + all_raw: Mapping[str, bytes], fixed: Mapping[str, str] + ) -> dict[str, Mapping[str, Any]]: + """Load release-bound schemas and close their declared external $ref families. + + ``binding_retrieval.schema.json`` deliberately reuses the sealed + Weaviate collection schema. The inline validator has no filesystem + resolver, so this function merges the external definitions into a + private in-memory validation documents and rewrites only declared + relative references. Conflicting shared definitions fail closed. + """ + + relief = _require_mapping( + load_json(all_raw[fixed["schema_relief_plan"]], label=fixed["schema_relief_plan"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="relief plan schema", + ) + binding = _require_mapping( + load_json(all_raw[fixed["schema_binding_retrieval"]], label=fixed["schema_binding_retrieval"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="binding/retrieval schema", + ) + calculation = _require_mapping( + load_json(all_raw[fixed["schema_calculation"]], label=fixed["schema_calculation"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="calculation schema", + ) + corpus = _require_mapping( + load_json(all_raw[fixed["schema_corpus_collection"]], label=fixed["schema_corpus_collection"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="corpus collection schema", + ) + ingress = _require_mapping( + load_json(all_raw[fixed["schema_ingress"]], label=fixed["schema_ingress"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="ingress schema", + ) + context = _require_mapping( + load_json(all_raw[fixed["schema_context"]], label=fixed["schema_context"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="context schema", + ) + s2_10 = _require_mapping( + load_json(all_raw[fixed["schema_s2_10"]], label=fixed["schema_s2_10"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="S2_10 schema", + ) + review = _require_mapping( + load_json(all_raw[fixed["schema_review_status"]], label=fixed["schema_review_status"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="review/status schema", + ) + deployment = _require_mapping( + load_json(all_raw[fixed["schema_deployment"]], label=fixed["schema_deployment"]), + code="ARTIFACT_SCHEMA_SHAPE", + label="deployment schema", + ) + merged_binding = load_json(canonical_bytes(binding), label="binding schema canonical copy") + merged_defs = merged_binding.get("$defs") + corpus_defs = corpus.get("$defs") + if not isinstance(merged_defs, dict) or not isinstance(corpus_defs, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", "binding and corpus schemas require $defs") + for name, definition in corpus_defs.items(): + if name in merged_defs and merged_defs[name] != definition: + raise S220Error( + "ARTIFACT_SCHEMA_EXTERNAL_DEF_CONFLICT", + f"shared schema definition differs: {name}", + ) + merged_defs.setdefault(name, definition) + + external_prefix = "../corpus/weaviate_collection.schema.json#" + + def rewrite_external_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith(external_prefix): + value["$ref"] = "#" + ref[len(external_prefix):] + for child in value.values(): + rewrite_external_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_external_refs(child) + + rewrite_external_refs(merged_binding) + + def merge_namespaced_external_defs( + target: Mapping[str, Any], + source: Mapping[str, Any], + *, + external_prefix: str, + namespace: str, + ) -> dict[str, Any]: + merged = load_json(canonical_bytes(target), label=f"{namespace} target schema copy") + source_copy = load_json(canonical_bytes(source), label=f"{namespace} source schema copy") + merged_defs = merged.get("$defs") + source_defs = source_copy.get("$defs") + if not isinstance(merged_defs, dict) or not isinstance(source_defs, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", "merged schemas require $defs") + + def rewrite_source_local_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith("#/$defs/"): + value["$ref"] = f"#/$defs/{namespace}__{ref[len('#/$defs/'):]}" + for child in value.values(): + rewrite_source_local_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_source_local_refs(child) + + for name, definition in source_defs.items(): + copied = load_json(canonical_bytes(definition), label=f"{namespace} definition copy") + rewrite_source_local_refs(copied) + target_name = f"{namespace}__{name}" + if target_name in merged_defs and merged_defs[target_name] != copied: + raise S220Error( + "ARTIFACT_SCHEMA_EXTERNAL_DEF_CONFLICT", + f"namespaced schema definition differs: {target_name}", + ) + merged_defs[target_name] = copied + + def rewrite_target_external_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.startswith(external_prefix): + fragment = ref[len(external_prefix):] + if not fragment.startswith("#/$defs/"): + raise S220Error( + "ARTIFACT_SCHEMA_REF_UNSUPPORTED", + f"external schema reference is not a $defs pointer: {ref}", + ) + value["$ref"] = f"#/$defs/{namespace}__{fragment[len('#/$defs/'):]}" + for child in value.values(): + rewrite_target_external_refs(child) + elif isinstance(value, list): + for child in value: + rewrite_target_external_refs(child) + + rewrite_target_external_refs(merged) + return merged + + merged_context = merge_namespaced_external_defs( + context, + review, + external_prefix="review_status.schema.json", + namespace="review_status", + ) + merged_review = merge_namespaced_external_defs( + review, + relief, + external_prefix="relief_plan.schema.json", + namespace="relief_plan", + ) + return { + "schemas/relief_plan.schema.json": relief, + "schemas/binding_retrieval.schema.json": merged_binding, + "schemas/calculation.schema.json": calculation, + "schemas/ingress.schema.json": ingress, + "schemas/context.schema.json": merged_context, + "schemas/s2_10.schema.json": s2_10, + "schemas/review_status.schema.json": merged_review, + "schemas/deployment.schema.json": deployment, + } + + + def _schema_pointer(document: Any, fragment: str) -> Any: + if fragment in {"", "#"}: + return document + pointer = fragment[1:] if fragment.startswith("#") else fragment + if not pointer.startswith("/"): + raise S220Error("ARTIFACT_SCHEMA_REF_UNSUPPORTED", f"non-pointer schema fragment: {fragment}") + current = document + for raw_token in pointer[1:].split("/"): + token = raw_token.replace("~1", "/").replace("~0", "~") + try: + current = current[int(token)] if isinstance(current, list) else current[token] + except (KeyError, IndexError, TypeError, ValueError) as exc: + raise S220Error("ARTIFACT_SCHEMA_REF_UNRESOLVED", f"schema pointer is unresolved: {fragment}") from exc + return current + + + def _schema_type_matches(instance: Any, expected: str) -> bool: + if expected == "null": + return instance is None + if expected == "boolean": + return isinstance(instance, bool) + if expected == "object": + return isinstance(instance, dict) + if expected == "array": + return isinstance(instance, list) + if expected == "string": + return isinstance(instance, str) + if expected == "integer": + return isinstance(instance, int) and not isinstance(instance, bool) + if expected == "number": + return isinstance(instance, (int, float)) and not isinstance(instance, bool) + raise S220Error("ARTIFACT_SCHEMA_KEYWORD_UNSUPPORTED", f"unsupported JSON Schema type: {expected}") + + + def _schema_identity(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + + def _schema_errors( + instance: Any, + schema: Any, + root_schema: Mapping[str, Any], + instance_path: str, + stack: set[tuple[int, int]], + ) -> list[str]: + if schema is True: + return [] + if schema is False: + return [f"{instance_path}: boolean schema false"] + if not isinstance(schema, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"schema is not an object/boolean at {instance_path}") + marker = (id(schema), id(instance)) + if marker in stack: + return [] + nested_stack = set(stack) + nested_stack.add(marker) + errors: list[str] = [] + + if "$ref" in schema: + ref = schema["$ref"] + if not isinstance(ref, str) or not ref.startswith("#"): + raise S220Error("ARTIFACT_SCHEMA_REF_UNSUPPORTED", f"only local schema refs are allowed: {ref!r}") + errors.extend( + _schema_errors( + instance, + _schema_pointer(root_schema, ref), + root_schema, + instance_path, + nested_stack, + ) + ) + + for branch in schema.get("allOf", []): + errors.extend(_schema_errors(instance, branch, root_schema, instance_path, nested_stack)) + if "anyOf" in schema: + branches = [ + _schema_errors(instance, branch, root_schema, instance_path, nested_stack) + for branch in schema["anyOf"] + ] + if not any(not branch for branch in branches): + errors.append(f"{instance_path}: anyOf matched no branch") + if "oneOf" in schema: + branches = [ + _schema_errors(instance, branch, root_schema, instance_path, nested_stack) + for branch in schema["oneOf"] + ] + matched = sum(not branch for branch in branches) + if matched != 1: + errors.append(f"{instance_path}: oneOf expected one match, got {matched}") + if "not" in schema and not _schema_errors( + instance, schema["not"], root_schema, instance_path, nested_stack + ): + errors.append(f"{instance_path}: not subschema matched") + if "if" in schema: + condition_matches = not _schema_errors( + instance, schema["if"], root_schema, instance_path, nested_stack + ) + selected = "then" if condition_matches else "else" + if selected in schema: + errors.extend( + _schema_errors(instance, schema[selected], root_schema, instance_path, nested_stack) + ) + + expected_type = schema.get("type") + if expected_type is not None: + expected_types = [expected_type] if isinstance(expected_type, str) else expected_type + if not isinstance(expected_types, list) or not all(isinstance(item, str) for item in expected_types): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"invalid type declaration at {instance_path}") + if not any(_schema_type_matches(instance, item) for item in expected_types): + errors.append(f"{instance_path}: type mismatch; expected {expected_types}") + return errors + + if "const" in schema and instance != schema["const"]: + errors.append(f"{instance_path}: const mismatch") + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{instance_path}: value is outside enum") + + if isinstance(instance, str): + if "pattern" in schema: + try: + pattern_matches = re.search(str(schema["pattern"]), instance) is not None + except re.error as exc: + raise S220Error("ARTIFACT_SCHEMA_PATTERN_INVALID", f"invalid schema pattern at {instance_path}") from exc + if not pattern_matches: + errors.append(f"{instance_path}: pattern mismatch") + if len(instance) < int(schema.get("minLength", 0)): + errors.append(f"{instance_path}: shorter than minLength") + if schema.get("format") == "date": + try: + parsed_date = date.fromisoformat(instance) + except ValueError: + parsed_date = None + if parsed_date is None or parsed_date.isoformat() != instance: + errors.append(f"{instance_path}: invalid RFC 3339 full-date") + elif "format" in schema: + raise S220Error( + "ARTIFACT_SCHEMA_KEYWORD_UNSUPPORTED", + f"unsupported string format at {instance_path}: {schema['format']}", + ) + + if isinstance(instance, (int, float)) and not isinstance(instance, bool): + if "minimum" in schema and instance < schema["minimum"]: + errors.append(f"{instance_path}: below minimum") + if "maximum" in schema and instance > schema["maximum"]: + errors.append(f"{instance_path}: above maximum") + + if isinstance(instance, dict): + required = schema.get("required", []) + if not isinstance(required, list): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"required must be an array at {instance_path}") + for key in required: + if key not in instance: + errors.append(f"{instance_path}: missing required property {key!r}") + properties = schema.get("properties", {}) + if not isinstance(properties, dict): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"properties must be an object at {instance_path}") + additional = schema.get("additionalProperties", True) + for key, value in instance.items(): + child_path = f"{instance_path}.{key}" + if key in properties: + errors.extend( + _schema_errors(value, properties[key], root_schema, child_path, nested_stack) + ) + elif additional is False: + errors.append(f"{child_path}: additional property is forbidden") + elif isinstance(additional, dict): + errors.extend( + _schema_errors(value, additional, root_schema, child_path, nested_stack) + ) + if len(instance) < int(schema.get("minProperties", 0)): + errors.append(f"{instance_path}: fewer than minProperties") + + if isinstance(instance, list): + if len(instance) < int(schema.get("minItems", 0)): + errors.append(f"{instance_path}: fewer than minItems") + if "maxItems" in schema and len(instance) > int(schema["maxItems"]): + errors.append(f"{instance_path}: more than maxItems") + if schema.get("uniqueItems") is True: + identities = [_schema_identity(item) for item in instance] + if len(identities) != len(set(identities)): + errors.append(f"{instance_path}: array items are not unique") + prefix_items = schema.get("prefixItems", []) + if not isinstance(prefix_items, list): + raise S220Error("ARTIFACT_SCHEMA_SHAPE", f"prefixItems must be an array at {instance_path}") + for index, child_schema in enumerate(prefix_items[: len(instance)]): + errors.extend( + _schema_errors( + instance[index], child_schema, root_schema, f"{instance_path}[{index}]", nested_stack + ) + ) + if "items" in schema: + for index in range(len(prefix_items), len(instance)): + errors.extend( + _schema_errors( + instance[index], schema["items"], root_schema, f"{instance_path}[{index}]", nested_stack + ) + ) + if "contains" in schema: + matched = sum( + not _schema_errors(item, schema["contains"], root_schema, f"{instance_path}[{index}]", nested_stack) + for index, item in enumerate(instance) + ) + if matched < 1: + errors.append(f"{instance_path}: contains matched no item") + return errors + + + def validate_artifact_payload( + payload: bytes, + schema_ref: str, + path: str, + schema_documents: Mapping[str, Any], + ) -> None: + value = load_json(payload, label=path) + schema_path, separator, fragment = schema_ref.partition("#") + root_schema = schema_documents.get(schema_path) + if not isinstance(root_schema, dict): + raise S220Error("ARTIFACT_SCHEMA_REF_UNRESOLVED", f"release-bound schema is unavailable: {schema_path}") + schema = _schema_pointer(root_schema, f"#{fragment}" if separator else "") + errors = _schema_errors(value, schema, root_schema, "$", set()) + if errors: + raise S220Error( + "ARTIFACT_SCHEMA_VALIDATION_FAILED", + f"artifact violates release-bound schema: {path}", + details=errors[:20], + ) + + + def publish(localdocs: McpClient, request: Mapping[str, Any], result: Mapping[str, Any]) -> dict[str, Any]: + root = request["plan_output_root_ref"] + payloads = artifact_payloads(result, request) + for relative, (payload, schema_ref, _) in sorted(payloads.items()): + validate_artifact_payload(payload, schema_ref, relative, result["schema_documents"]) + rows: list[dict[str, Any]] = [] + for relative, (payload, schema_ref, producer) in sorted(payloads.items()): + try: + localdocs.write_immutable(join_path(root, relative), payload) + except Exception as exc: + raise S220Error( + "PUBLISH_PARTIAL_WRITE", + f"artifact publication stopped before barrier: {relative}", + details={ + "written_paths": [row["path"] for row in rows], + "written_artifact_rows": [dict(row) for row in rows], + "failed_path": f"plan/{relative}", + "cause": str(exc), + }, + ) from exc + rows.append({"path": f"plan/{relative}", "schema_ref": schema_ref, "raw_sha256": digest(payload), "byte_size": len(payload), "producer_component": producer}) + groups = result["groups"] + barrier_without_hash = { + "schema_version": "stage2_plan_publish_status.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": result["plan"]["input_snapshot_digest"], + "parent_stage2_release_sha256": EXPECTED_STAGE2_RELEASE_SHA256, + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "s2_20_agent_sha256": request["expected_s2_20_agent_sha256"], + "s2_20_code_sha256": result["s2_20_code_sha256"], + "plan_status": "PLAN_COMPLETE_WITH_REVIEW" if any(row["drafting_permission"] != "CLEAN_DRAFT" for row in groups) else "PLAN_COMPLETE", + "route": "TO_S2_30", + "consumer_authorized": True, + "artifact_rows": rows, + "artifact_set_digest": digest(rows), + "group_ids": [row["claim_group_id"] for row in groups], + "clean_draft_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "CLEAN_DRAFT"], + "review_draft_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "REVIEW_DRAFT"], + "worknote_only_group_ids": [row["claim_group_id"] for row in groups if row["drafting_permission"] == "WORKNOTE_ONLY"], + "issue_summary": result["issue_summary"], + } + barrier = {**barrier_without_hash, "barrier_sha256": digest(barrier_without_hash)} + barrier_raw = canonical_bytes(barrier) + try: + localdocs.write_immutable(join_path(root, "plan_publish_status.json"), barrier_raw) + except Exception as exc: + raise S220Error( + "PUBLISH_PARTIAL_WRITE", + "all artifacts were written but the final consumer barrier failed", + details={ + "written_paths": [row["path"] for row in rows], + "written_artifact_rows": [dict(row) for row in rows], + "failed_path": "plan/plan_publish_status.json", + "cause": str(exc), + }, + ) from exc + return {"barrier_sha256": digest(barrier_raw), "artifact_set_digest": barrier["artifact_set_digest"], "route": barrier["route"], "plan_status": barrier["plan_status"]} + + + def publish_diagnostic(localdocs: McpClient, request: Mapping[str, Any], error: Mapping[str, Any]) -> dict[str, Any]: + root = request["plan_output_root_ref"] + details = error.get("details") if isinstance(error.get("details"), dict) else {} + partial_rows_raw = details.get("written_artifact_rows", []) if isinstance(details, dict) else [] + partial_rows: list[dict[str, Any]] = [] + if isinstance(partial_rows_raw, list): + for row in partial_rows_raw: + if not isinstance(row, dict) or set(row) != { + "path", "schema_ref", "raw_sha256", "byte_size", "producer_component" + }: + raise S220Error("DIAGNOSTIC_PARTIAL_ROW", "partial artifact row has an open shape") + safe_path(str(row["path"])) + require_hex(row["raw_sha256"], code="DIAGNOSTIC_PARTIAL_ROW_HASH") + if not isinstance(row["byte_size"], int) or isinstance(row["byte_size"], bool) or row["byte_size"] < 1: + raise S220Error("DIAGNOSTIC_PARTIAL_ROW_SIZE", "partial artifact byte size is invalid") + partial_rows.append(dict(row)) + source_refs = sorted(str(row["path"]) for row in partial_rows) + diagnostic_base = { + "schema_version": "stage2_s2_20_technical_diagnostic.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "attempt_id": request["attempt_id"], + "failure_component": "PUBLISH" if error.get("code") == "PUBLISH_PARTIAL_WRITE" else "PREFLIGHT", + "failure_code": str(error.get("code", "INLINE_RUNTIME_ERROR")), + "failure_scope": "RUN_WIDE", + "message": str(error.get("message", "S2_20 failed")), + "input_snapshot_digest": None, + "source_refs": source_refs, + "retryable": str(error.get("code", "")).startswith(("MCP_", "LOCALDOCS_")), + "operator_action_required": True, + } + diagnostic = {**diagnostic_base, "diagnostic_sha256": digest(diagnostic_base)} + diagnostic_raw = canonical_bytes(diagnostic) + localdocs.write_immutable(join_path(root, "technical_diagnostic.json"), diagnostic_raw) + rows = partial_rows + [{ + "path": "plan/technical_diagnostic.json", + "schema_ref": "schemas/relief_plan.schema.json#/$defs/technical_diagnostic", + "raw_sha256": digest(diagnostic_raw), + "byte_size": len(diagnostic_raw), + "producer_component": "S2_20_DIAGNOSTIC", + }] + rows = sorted(rows, key=lambda row: str(row["path"])) + barrier_base = { + "schema_version": "stage2_plan_publish_status.v1", + "workflow_id": WORKFLOW_ID, + "run_binding_digest": request["run_binding_digest"], + "input_snapshot_digest": "0" * 64, + "parent_stage2_release_sha256": EXPECTED_STAGE2_RELEASE_SHA256, + "s2_10_publish_status_sha256": request["expected_s2_10_publish_status_sha256"], + "s2_20_agent_sha256": request["expected_s2_20_agent_sha256"], + "s2_20_code_sha256": digest(b"UNAVAILABLE_BEFORE_PREFLIGHT"), + "plan_status": "TECHNICAL_INCOMPLETE", + "route": "STOP_TECHNICAL_INCOMPLETE", + "consumer_authorized": False, + "artifact_rows": rows, + "artifact_set_digest": digest(rows), + "group_ids": [], + "clean_draft_group_ids": [], + "review_draft_group_ids": [], + "worknote_only_group_ids": [], + "issue_summary": {"total": 1, "legal": 0, "binding": 0, "corpus": 0, "calculation": 0, "technical": 1}, + } + barrier = {**barrier_base, "barrier_sha256": digest(barrier_base)} + barrier_raw = canonical_bytes(barrier) + localdocs.write_immutable(join_path(root, "plan_publish_status.json"), barrier_raw) + return {"barrier_sha256": digest(barrier_raw), "artifact_set_digest": digest(rows), "route": "STOP_TECHNICAL_INCOMPLETE", "plan_status": "TECHNICAL_INCOMPLETE"} + + + def run() -> int: + localdocs: McpClient | None = None + request: dict[str, Any] | None = None + publication_guard = {"external_trust_verified": False} + receipt: dict[str, Any] + exit_code = 0 + try: + with contextlib.redirect_stdout(io.StringIO()): + if HEX64.fullmatch(INLINE_USER_HASH) is None or HEX64.fullmatch(INLINE_WORKSPACE_HASH) is None: + raise S220Error("INLINE_CONTEXT_UNBOUND", "AgentBackend user/workspace substitutions are required") + localdocs = McpClient(LOCALDOCS_URL, "localdocs") + localdocs.initialize() + request = validate_request(localdocs.read_binary(REQUEST_PATH)) + inputs, assets, snapshot_digest = hydrate(localdocs, request, publication_guard) + result = execute_core(inputs, assets, request, snapshot_digest) + publication = publish(localdocs, request, result) + receipt = { + "schema_version": "stage2_s2_20_execution_receipt.v1", + "ok": True, + "workflow_id": WORKFLOW_ID, + "request_id": request["request_id"], + "run_binding_digest": request["run_binding_digest"], + "plan_status": publication["plan_status"], + "route": publication["route"], + "artifact_set_digest": publication["artifact_set_digest"], + "plan_publish_status_path": "plan/plan_publish_status.json", + "plan_publish_status_sha256": publication["barrier_sha256"], + "error": None, + } + except Exception as exc: + error = exc.as_dict() if isinstance(exc, S220Error) else {"code": "INLINE_RUNTIME_ERROR", "message": str(exc)} + diagnostic_publication: dict[str, Any] | None = None + if ( + localdocs is not None + and request is not None + and publication_guard["external_trust_verified"] + ): + try: + diagnostic_publication = publish_diagnostic(localdocs, request, error) + except Exception as diagnostic_exc: + error = {**error, "diagnostic_publish_error": str(diagnostic_exc)} + receipt = { + "schema_version": "stage2_s2_20_execution_receipt.v1", + "ok": False, + "workflow_id": WORKFLOW_ID, + "request_id": request["request_id"] if request is not None else "UNBOUND", + "run_binding_digest": request["run_binding_digest"] if request is not None else "0" * 64, + "plan_status": diagnostic_publication["plan_status"] if diagnostic_publication is not None else "TECHNICAL_INCOMPLETE", + "route": diagnostic_publication["route"] if diagnostic_publication is not None else "STOP_TECHNICAL_INCOMPLETE", + "artifact_set_digest": diagnostic_publication["artifact_set_digest"] if diagnostic_publication is not None else None, + "plan_publish_status_path": "plan/plan_publish_status.json", + "plan_publish_status_sha256": diagnostic_publication["barrier_sha256"] if diagnostic_publication is not None else None, + "error": error, + } + exit_code = 2 + finally: + if localdocs is not None: + localdocs.close() + sys.stdout.buffer.write(canonical_bytes(receipt)) + return exit_code + + + if __name__ == "__main__": + raise SystemExit(run()) + task_procedure: + IN: + nexts: + - Task_S2_20_deterministic_relief_plan + wait_until: [] + Task_S2_20_deterministic_relief_plan: + nexts: + - OUT + wait_until: + - IN + OUT: + nexts: [] + wait_until: + - Task_S2_20_deterministic_relief_plan diff --git a/Case_02_Comparison_Research/plans/s2-20-deterministic-package.md b/Case_02_Comparison_Research/plans/s2-20-deterministic-package.md new file mode 100644 index 00000000..df5e960e --- /dev/null +++ b/Case_02_Comparison_Research/plans/s2-20-deterministic-package.md @@ -0,0 +1,110 @@ +# S2_20 Deterministic ReliefPlan Package + +## Objective + +`stage_2_optimal_update_strategy_v.5-1.md`의 S2_20 계약을 단일 MCP Code Executor inline Python Agent로 구현하고, 필요한 고정 자산·검증 자산·release 결속을 함께 작성한다. + +## Deliverables + +- `YAML_Prompts/2. Stage_2/S2_20_SOW.md` +- `YAML_Prompts/2. Stage_2/S2_20_assets.md` +- `YAML_Prompts/2. Stage_2/Stage_2_S2_20.yml` +- `YAML_Prompts/2. Stage_2/Default_Agent/Stage_2_Clean/` 아래 version-free S2_20 실행·계약·schema·runtime mirror·fixture·test·release 자산 +- `YAML_Prompts/2. Stage_2/MEMORY.md` 작업 요약 + +## Scope and Non-Scope + +- 범위: S2_10 canonical immutable parts를 소비하여 option conservation, party/compatibility, exact case-type/rule binding, 계산, 제한된 Weaviate retrieval, reports, frozen ReliefPlan과 status-last plan barrier를 만드는 deterministic S2_20. +- 비범위: 신규 법률판단, fuzzy case-type fallback, 137종 법률 semantic sign-off, 실제 Weaviate/Code Executor live admission, S2_30 작성, S2_40 최종 render/commit. +- 기존 Stage 2 v.0~v.3 YAML runtime dependency는 0으로 유지한다. + +## Known Inputs + +- `stage_2_optimal_update_strategy_v.5-1.md` +- `S2_00_SOW_v.2.md`, `S2_00_assets_v.2.md`, `Stage_2_S2_00_v.2.yml` +- `S2_10_SOW_v.1.md`, `S2_10_assets_v.1.md`, `Stage_2_S2_10_v.1.yml` +- `test_code_executor.ipynb`, repository `SKILL.md`, Stage 1 `task_procedure` example +- current `Default_Agent/Stage_2_Clean/` release and manifest chain + +## Material Assumptions + +- YAML의 `parameters.code`가 runtime 정본이고 `runtime/s2_20_reduce.py` 및 `.txt`는 byte-identical offline mirror다. +- 법률가 승인·corpus 운영·live MCP binding이 닫히지 않은 자산은 명시적으로 pending/draft 상태를 유지한다. +- 미결 binding/retrieval/calculation은 option을 소거하지 않고 scoped issue와 non-READY 상태로 보존한다. + +## Questions That Could Change the Outcome + +- 없음. production 법률 승인과 live endpoint admission은 이번 작업의 완료조건이 아니라 별도 외부 gate로 분리한다. + +## Workstreams and Dependencies + +1. SOW와 asset inventory 병렬 초안. +2. 두 문서의 독립 평가 2회와 증분 개정. +3. schema/data fixture 묶음과 runtime/build/test 묶음을 병렬 생성. +4. Agent YAML과 projection/release 결속 생성. +5. 자산 검증 2회, YAML 검증 2회, 전체 offline closure 검증. + +## Source and Tool Plan + +- 로컬 canonical strategy·SOW·schema·manifest·notebook만 사용한다. +- 파일 탐색은 `rg`/`find`, 편집은 `apply_patch`, 검증은 Python unittest·YAML/JSON parser·hash/parity 검사로 수행한다. +- 외부 법률·API 사실을 새로 확정하지 않으므로 웹 검색은 수행하지 않는다. + +## Validation Plan + +- YAML unique-key parse, exactly-one `run_code`, `task_procedure` DAG closure. +- inline code compile/AST, forbidden import/call/path checks, authoring↔deployment parity, `.py/.txt` byte parity. +- option partition/disjointness, exact predicate zero/multi behavior, stable IDs/groups, status-last write, retrieval no-fallback, calculator missing-operand branches. +- manifest/release/path/hash closure와 S2_00/S2_10 회귀검사. + +## Approval Boundaries + +- 외부 시스템 호출·배포·법률 승인·production promotion은 수행하지 않는다. +- 사용자 소유의 관련 없는 dirty worktree 변경은 수정·삭제·stage하지 않는다. + +## Progress + +- [x] 요청문·AGENTS.md·MEMORY.md·v5-1 S2_20 계약·Code Executor 규칙 확인 +- [x] S2_20 SOW/assets 병렬 초안 +- [x] 문서 검증 2회 및 개정 +- [x] 고정 자산과 Agent YAML 구현 +- [x] 자산 검증·개정 2회 및 YAML 검증·개정 2회 +- [x] 전체 offline closure·재현성·비순환 hash-chain 검증 +- [x] MEMORY.md 기록 + +## Decision Log + +| Date/Stage | Decision | Basis | Consequence | +|---|---|---|---| +| 2026-08-31 / intake | S2_20은 단일 inline Python task로 고정 | v5-1 §0.5, §4.2, §16.1 및 Code Executor 계약 | 내부 C20~C35 DAG는 함수 호출 순서와 `task_procedure` 주석/계약으로 표현하고 runtime task는 늘리지 않음 | +| 2026-08-31 / trust boundary | 법률·corpus·live admission 미완료를 offline 구현과 분리 | 현 parent release와 MEMORY 상태 | offline fixture PASS를 production-ready로 표현하지 않음 | +| 2026-08-31 / asset round 1 | PENDING branch schema 통과만으로 구현완료를 선언하지 않음 | 독립 asset/schema 감사에서 approved projection·FK·calculation·retrieval 및 release helper 불일치 확인 | pure-core·inline·schema·release tools를 동일 canonical 계약으로 개정 후 Round 2 수행 | +| 2026-08-31 / YAML round 1 | trust anchor·partial-write·dynamic legal asset 검증을 inline task 내부에서 닫음 | 독립 감사의 admission/CAS 순서·legal-review path·manifest membership finding | 최종 barrier 전에 detached admission·host CAS를 검증하고 모든 부분 쓰기를 diagnostic에 보존 | +| 2026-08-31 / YAML round 2 | admission signature material에 backend capability receipt hash를 포함하고 final barrier 쓰기를 partial-write 계약으로 포섭 | 두 번째 독립 감사 residual finding 2건 | 지정된 2회 루프 종료 후 전체 regression·hash chain 재검증 | +| 2026-08-31 / closure | parent→module manifest, detached admission→parent+executor binding의 비순환 결속을 확정 | builder reproducibility와 실파일 SHA-256 semantic audit | parent는 executor binding을 비해시 external trust-root로만 지시하며 admission은 PENDING을 유지 | + +## Evidence Ledger + +| Claim/Issue | Source or Test | Status | Notes | +|---|---|---|---| +| S2_20 내부 순서 | v5-1 §7 | VERIFIED | C20/C21 → C25/C26 → C22 and C27~C29 → C30 → C35 | +| exactly-one inline Python | v5-1 §0.5, §4.2, §16.1 | VERIFIED | external runtime import 금지 | +| MCP execution shape | `test_code_executor.ipynb`, repository YAML guide | VERIFIED_STATIC | `run_code(language, code, timeout)` 및 localdocs session binding | +| current S2_20 physical assets | `Default_Agent/Stage_2_Clean/` inventory | IMPLEMENTED_OFFLINE_VERIFIED | 115-path parent allowlist, schema/registry/runtime/build/fixture/test·projection·receipt 구현 | +| pending branch output schema | `tests/s2_20/test_inline_output_schema.py` | VERIFIED_STATIC | synthetic 1 option의 34 output, 24 fixed asset, S2_10 option schema 통과; approved/live branch 증거는 아님 | +| release chain audit | builder reproducibility + read-only semantic hash audit | VERIFIED | module member actual hash, parent/module/binding/admission, S2_00/S2_20 projection·mirror, S2_10 child parent 결속과 금지 cycle 검사 PASS | +| regression suites | `tests/s2_00`, `tests/s2_10`, `tests/s2_20` | VERIFIED_OFFLINE | 70/70, 39/39, 41/41 PASS; `.py/.txt` 42/42 parity PASS | + +## Risks and Failure Modes + +- 계획 자산을 구현 완료로 오인하는 위험. +- parent/child release hash cascade 누락. +- case-type/rule/corpus 미승인 상태를 fuzzy 또는 generic fallback으로 숨기는 위험. +- Weaviate 결과를 법률판단이나 사건 사실로 승격하는 위험. +- runtime code와 `.py/.txt` mirror drift. + +## Results and Residual Uncertainty + +S2_20 SOW·asset inventory·단일 inline Code Executor Agent와 version-free 실행/계약/검증 자산을 구현했다. 최종 offline 증거는 S2_00 70/70, S2_10 39/39, S2_20 41/41, Python/TXT 42/42 parity, projection/release reproducibility·비순환 hash-chain PASS다. + +다만 현 snapshot은 구조적 137 case-type catalog만 보유하며 법률가 승인 `rules/relief/*.md` 0/137, executable branch 0, authority/corpus/law-value admission 미완료, 상류 typed calculation operand·기존 증거표목 부재 상태다. live Code Executor·Weaviate·host CAS·E2E·대한민국 변호사 검수·production admission은 수행하지 않았으므로 최종 상태는 `IMPLEMENTED_OFFLINE_VERIFIED / LIVE_AND_LEGAL_ADMISSION_PENDING`이다.