diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-00/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-00/gates.json new file mode 100644 index 00000000..2d6bb94c --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-00/gates.json @@ -0,0 +1,86 @@ +{ + "schema_version": "stage1_s2_domain_gates.v1", + "domain_id": "E-00", + "legacy_gate_source": { + "path": "S_0_GPT/legacy/part4/deterministic_gates_contract.json", + "sha256": "2b1b35bf1f28ba8f48401fffc86cebf5addad53570b9f8ab159f32c801d39752" + }, + "rules": [ + { + "gate_id": "E00-G01", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "E00_CONSERVATION_FAILURE", + "E00_MEMBERSHIP_CONFLICT_REVIEW" + ] + }, + "emitted_codes": [ + "E00_CONSERVATION_FAILURE", + "E00_MEMBERSHIP_CONFLICT_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E00-G02", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "UNROUTED_ISSUE_REVIEW", + "E00_FALSE_UNROUTED_REVIEW" + ] + }, + "emitted_codes": [ + "UNROUTED_ISSUE_REVIEW", + "E00_FALSE_UNROUTED_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": false + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E00-G03", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "E00_SILENT_FALLBACK_FORBIDDEN" + ] + }, + "emitted_codes": [ + "E00_SILENT_FALLBACK_FORBIDDEN" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + } + ], + "legacy_equivalence_projections": [], + "global_status_rule": "any review code yields READY_WITH_REVIEW; hard input or contract failure yields FAILED/BLOCKED", + "closure_status": "IMPLEMENTED_LOCAL_DEFINITION" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-01/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-01/gates.json new file mode 100644 index 00000000..866a4132 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-01/gates.json @@ -0,0 +1 @@ +{"domain_id":"E-01","gates":[{"condition":"required element/evidence slot이 누락·부분충족","emitted_review_codes":["E01_REQUIRED_ELEMENT_GAP_REVIEW"],"gate_id":"E01-G01","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-01.domain_extension.v1","E-01.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":false,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"당사자·객체·행위·효력·인과·권리귀속의 핵심 원천이 상충","emitted_review_codes":["E01_DECLARATION_IDENTITY_CONFLICT_REVIEW","E01_VALIDITY_OR_CANCELLATION_CONFLICT_REVIEW","E01_AGENCY_OR_RATIFICATION_CONFLICT_REVIEW"],"gate_id":"E01-G02","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-01.domain_extension.v1","E-01.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":false,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"법률행위·침해·유통·권리행사 기준일 또는 적용법 version이 누락·상충","emitted_review_codes":["E01_LAW_VERSION_OR_PERIOD_INPUT_REVIEW"],"gate_id":"E01-G03","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-01.domain_extension.v1","E-01.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"필요 특별법 profile·전문 leaf·민사/공법/제외 트랙 경계가 닫히지 않음","emitted_review_codes":["E01_SPECIAL_LAW_OR_TRACK_BOUNDARY_REVIEW","SPECIAL_LAW_PROFILE_REQUIRED_REVIEW","TRACK_BOUNDARY_REVIEW","PUBLIC_LAW_NEXUS_REVIEW"],"gate_id":"E01-G04","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-01.domain_extension.v1","E-01.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"계산 operand가 partial 또는 blocked인데 산식·단일값·법정수치를 확정하려 함","emitted_review_codes":["E01_CALCULATION_OPERAND_PARTIAL_REVIEW","E01_CALCULATION_OPERAND_BLOCKED_REVIEW","MISSING_REQUIRED_OPERAND","CALCULATION_PREEMPTION_BLOCKED"],"gate_id":"E01-G05","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-01.domain_extension.v1","E-01.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"meeting-only 진술을 증거로 승격하거나 최종 법률결론·청구금액·소장문구를 생성하려 함","emitted_review_codes":["E01_MEETING_ONLY_SUPPORT_REVIEW","FINAL_LEGAL_CONCLUSION_BLOCKED"],"gate_id":"E01-G06","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-01.domain_extension.v1","E-01.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}}],"global_rules":{"fact_ledger_write_continues_with_review":true,"final_legal_conclusion_forbidden":true,"meeting_only_evidence_promotion_forbidden":true,"missing_input_is_not_false":true,"review_count_may_not_decrease_without_resolution_ref":true,"runtime_case_name_lookup_forbidden":true,"statutory_numeric_constant_hardcoding_forbidden":true},"schema_version":"stage1_domain_gates.v1","status":"ACTIVE"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-02/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-02/gates.json new file mode 100644 index 00000000..f4a881a1 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-02/gates.json @@ -0,0 +1,85 @@ +{ + "schema_version": "s2_domain_gates.v1", + "domain_id": "E-02", + "rules": [ + { + "gate_id": "E02-G01", + "review_code": "E02_REQUIRED_ELEMENT_GAP_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E02_REQUIRED_ELEMENT_GAP_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E02-G02", + "review_code": "E02_AMOUNT_OR_DUE_DATE_CONFLICT", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E02_AMOUNT_OR_DUE_DATE_CONFLICT 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E02-G03", + "review_code": "E02_PAYMENT_STATUS_CONFLICT", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E02_PAYMENT_STATUS_CONFLICT 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E02-G04", + "review_code": "E02_SET_OFF_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E02_SET_OFF_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E02-G05", + "review_code": "E02_LIMITATION_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E02_LIMITATION_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E02-G06", + "review_code": "CALCULATION_PREEMPTION_BLOCKED", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 CALCULATION_PREEMPTION_BLOCKED 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": true + } + } + ], + "global_rules": { + "review_count_non_decrease": true, + "silent_drop_forbidden": true, + "unknown_source_rejected": true + }, + "legacy_gate_projection": [], + "closure_status": "CLOSED" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-03/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-03/gates.json new file mode 100644 index 00000000..31272039 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-03/gates.json @@ -0,0 +1,61 @@ +{ + "schema_version": "s2_domain_gates.v1", + "domain_id": "E-03", + "rules": [ + { + "gate_id": "E03-G01", + "review_code": "E03_PARTY_LINK_GAP_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E03_PARTY_LINK_GAP_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E03-G02", + "review_code": "E03_SUCCESSION_SCOPE_CONFLICT", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E03_SUCCESSION_SCOPE_CONFLICT 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E03-G03", + "review_code": "E03_NOTICE_EFFECT_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E03_NOTICE_EFFECT_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E03-G04", + "review_code": "E03_MULTIPARTY_ALLOCATION_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E03_MULTIPARTY_ALLOCATION_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + } + ], + "global_rules": { + "review_count_non_decrease": true, + "silent_drop_forbidden": true, + "unknown_source_rejected": true + }, + "legacy_gate_projection": [], + "closure_status": "CLOSED" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-04/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-04/gates.json new file mode 100644 index 00000000..cdfc9bb9 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-04/gates.json @@ -0,0 +1,106 @@ +{ + "schema_version": "stage1_domain_gates.v1", + "domain_id": "E-04", + "status": "ACTIVE", + "legacy_contract_refs": [ + "S_0_GPT/legacy/part4/deterministic_gates_contract.json", + "S_0_GPT/legacy/part4/structure_projection_contract.json" + ], + "gates": [ + { + "gate_id": "E04-G01", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-04.domain_extension.v2", + "E-04.effect_projection.v1" + ], + "condition": "필수 이익·손실·대응관계·법률상 원인 슬롯 중 하나라도 누락", + "emitted_review_codes": [ + "E04_REQUIRED_ELEMENT_GAP_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E04-G02", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-04.domain_extension.v2", + "E-04.effect_projection.v1" + ], + "condition": "기간·면적·임료 또는 가치기준 후보가 상충", + "emitted_review_codes": [ + "E04_USE_VALUE_CONFLICT" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E04-G03", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-04.domain_extension.v2", + "E-04.effect_projection.v1" + ], + "condition": "법률상 원인·공제·상계·불법원인급여 후보가 존재하나 확정할 수 없음", + "emitted_review_codes": [ + "E04_CAUSE_OR_GAIN_CONFLICT", + "E04_DEFENSE_OR_OFFSET_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E04-G04", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-04.domain_extension.v2", + "E-04.effect_projection.v1" + ], + "condition": "Stage 1 사실 후보를 반환의무·액수의 최종 결론으로 변환하려 함", + "emitted_review_codes": [ + "FINAL_LEGAL_CONCLUSION_BLOCKED" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + } + ], + "global_rules": { + "review_count_may_not_decrease_without_resolution_ref": true, + "missing_input_is_not_false": true, + "final_legal_conclusion_forbidden": true, + "fact_ledger_write_continues_with_review": true + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-05/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-05/gates.json new file mode 100644 index 00000000..18fc4c38 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-05/gates.json @@ -0,0 +1,163 @@ +{ + "schema_version": "stage1_domain_gates.v1", + "domain_id": "E-05", + "status": "ACTIVE", + "gates": [ + { + "gate_id": "E05-G01", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-05.domain_extension.v1", + "E-05.effect_projection.v1", + "stage1_domain_signals.v2" + ], + "condition": "행위·귀책·침해이익·손해·인과관계의 required slot 중 하나 이상이 증거로 채워지지 않음", + "emitted_review_codes": [ + "E05_REQUIRED_ELEMENT_GAP_REVIEW", + "E05_DAMAGE_SOURCE_GAP_REVIEW", + "E05_CAUSATION_SOURCE_GAP_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E05-G02", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-05.domain_extension.v1", + "E-05.effect_projection.v1", + "stage1_domain_signals.v2" + ], + "condition": "행위자·귀책·손해기전·인과관계 또는 손해범위에 상충 원천이 존재함", + "emitted_review_codes": [ + "E05_CONDUCT_OR_FAULT_CONFLICT_REVIEW", + "E05_CAUSATION_OR_DAMAGE_CONFLICT_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E05-G03", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-05.domain_extension.v1", + "E-05.effect_projection.v1", + "stage1_domain_signals.v2" + ], + "condition": "침해·손해발생·인지·권리행사 기준일이 누락되거나 상충함", + "emitted_review_codes": [ + "E05_LIMITATION_INPUT_GAP_REVIEW", + "E05_LIMITATION_DATE_CONFLICT_REVIEW", + "E05_LIMITATION_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E05-G04", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-05.domain_extension.v1", + "E-05.effect_projection.v1", + "stage1_domain_signals.v2" + ], + "condition": "전문사건 또는 공법·트랙 경계 단서가 있으나 필요한 profile·전문 leaf가 부착되지 않음", + "emitted_review_codes": [ + "SPECIAL_LAW_PROFILE_REQUIRED_REVIEW", + "PUBLIC_LAW_NEXUS_REVIEW", + "TRACK_BOUNDARY_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E05-G05", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-05.domain_extension.v1", + "E-05.effect_projection.v1", + "stage1_domain_signals.v2" + ], + "condition": "CE-01·CE-03·CE-05 operand가 partial 또는 blocked인데 산식·단일값을 만들려 함", + "emitted_review_codes": [ + "E05_CALCULATION_OPERAND_PARTIAL_REVIEW", + "E05_CALCULATION_OPERAND_BLOCKED_REVIEW", + "CALCULATION_PREEMPTION_BLOCKED" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E05-G06", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-05.domain_extension.v1", + "E-05.effect_projection.v1", + "stage1_domain_signals.v2" + ], + "condition": "meeting-only 진술을 증거로 승격하거나 책임·손해·시효의 최종 결론을 출력하려 함", + "emitted_review_codes": [ + "E05_MEETING_ONLY_SUPPORT_REVIEW", + "FINAL_LEGAL_CONCLUSION_BLOCKED" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + } + ], + "global_rules": { + "review_count_may_not_decrease_without_resolution_ref": true, + "missing_input_is_not_false": true, + "final_legal_conclusion_forbidden": true, + "meeting_only_evidence_promotion_forbidden": true, + "fact_ledger_write_continues_with_review": true, + "statutory_numeric_constant_hardcoding_forbidden": true + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-06/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-06/gates.json new file mode 100644 index 00000000..4103a69e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-06/gates.json @@ -0,0 +1 @@ +{"domain_id":"E-06","gates":[{"condition":"required element/evidence slot이 누락·부분충족","emitted_review_codes":["E06_REQUIRED_ELEMENT_GAP_REVIEW"],"gate_id":"E06-G01","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-06.domain_extension.v1","E-06.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":false,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"당사자·객체·행위·효력·인과·권리귀속의 핵심 원천이 상충","emitted_review_codes":["E06_PROFESSIONAL_STANDARD_OR_BREACH_CONFLICT_REVIEW","E06_CAUSATION_OR_DAMAGE_CONFLICT_REVIEW","E06_DISCLOSURE_OR_PRODUCT_DEFECT_CONFLICT_REVIEW"],"gate_id":"E06-G02","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-06.domain_extension.v1","E-06.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":false,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"법률행위·침해·유통·권리행사 기준일 또는 적용법 version이 누락·상충","emitted_review_codes":["E06_LAW_VERSION_OR_PERIOD_INPUT_REVIEW"],"gate_id":"E06-G03","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-06.domain_extension.v1","E-06.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"필요 특별법 profile·전문 leaf·민사/공법/제외 트랙 경계가 닫히지 않음","emitted_review_codes":["E06_SPECIAL_LAW_OR_TRACK_BOUNDARY_REVIEW","SPECIAL_LAW_PROFILE_REQUIRED_REVIEW","TRACK_BOUNDARY_REVIEW","PUBLIC_LAW_NEXUS_REVIEW"],"gate_id":"E06-G04","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-06.domain_extension.v1","E-06.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"계산 operand가 partial 또는 blocked인데 산식·단일값·법정수치를 확정하려 함","emitted_review_codes":["E06_CALCULATION_OPERAND_PARTIAL_REVIEW","E06_CALCULATION_OPERAND_BLOCKED_REVIEW","MISSING_REQUIRED_OPERAND","CALCULATION_PREEMPTION_BLOCKED"],"gate_id":"E06-G05","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-06.domain_extension.v1","E-06.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"meeting-only 진술을 증거로 승격하거나 최종 법률결론·청구금액·소장문구를 생성하려 함","emitted_review_codes":["E06_MEETING_ONLY_SUPPORT_REVIEW","FINAL_LEGAL_CONCLUSION_BLOCKED"],"gate_id":"E06-G06","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-06.domain_extension.v1","E-06.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}}],"global_rules":{"fact_ledger_write_continues_with_review":true,"final_legal_conclusion_forbidden":true,"meeting_only_evidence_promotion_forbidden":true,"missing_input_is_not_false":true,"review_count_may_not_decrease_without_resolution_ref":true,"runtime_case_name_lookup_forbidden":true,"statutory_numeric_constant_hardcoding_forbidden":true},"schema_version":"stage1_domain_gates.v1","status":"ACTIVE"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-07/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-07/gates.json new file mode 100644 index 00000000..c13c7a9a --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-07/gates.json @@ -0,0 +1,146 @@ +{ + "schema_version": "s3_domain_gates.v1", + "domain_id": "E-07", + "rules": [ + { + "gate_id": "E07-G01", + "review_code": "E07_REQUIRED_ELEMENT_GAP_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_REQUIRED_ELEMENT_GAP_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G02", + "review_code": "E07_SCOPE_AMOUNT_CONFLICT", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_SCOPE_AMOUNT_CONFLICT 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G03", + "review_code": "E07_PROGRESS_COMPLETION_CONFLICT", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_PROGRESS_COMPLETION_CONFLICT 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G04", + "review_code": "E07_DEFECT_CAUSATION_COST_CONFLICT", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_DEFECT_CAUSATION_COST_CONFLICT 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G05", + "review_code": "E07_ADDITIONAL_WORK_AUTHORIZATION_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_ADDITIONAL_WORK_AUTHORIZATION_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G06", + "review_code": "E07_BOUNDARY_OVERLAP_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_BOUNDARY_OVERLAP_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G07", + "review_code": "E07_MEETING_ONLY_MONITOR_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_MEETING_ONLY_MONITOR_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G08", + "review_code": "E07_SPECIAL_LAW_PROFILE_REQUIRED_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_SPECIAL_LAW_PROFILE_REQUIRED_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G09", + "review_code": "E07_CALCULATION_PARTIAL_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_CALCULATION_PARTIAL_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G10", + "review_code": "E07_SOURCE_MEMBERSHIP_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E07_SOURCE_MEMBERSHIP_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E07-G11", + "review_code": "CALCULATION_PREEMPTION_BLOCKED", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 CALCULATION_PREEMPTION_BLOCKED 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "BLOCKED_PENDING_OPERANDS", + "blocks_final_drafting": true + } + } + ], + "global_rules": { + "review_count_non_decrease": true, + "silent_drop_forbidden": true, + "unknown_source_rejected": true, + "meeting_only_cannot_promote_to_active": true, + "calculation_formula_hardcoding_forbidden": true + }, + "closure_status": "CLOSED" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-08/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-08/gates.json new file mode 100644 index 00000000..c2a80567 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-08/gates.json @@ -0,0 +1,146 @@ +{ + "schema_version": "s3_domain_gates.v1", + "domain_id": "E-08", + "rules": [ + { + "gate_id": "E08-G01", + "review_code": "E08_REQUIRED_ELEMENT_GAP_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_REQUIRED_ELEMENT_GAP_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G02", + "review_code": "E08_TERM_RENEWAL_CONFLICT", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_TERM_RENEWAL_CONFLICT 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G03", + "review_code": "E08_DEPOSIT_DEDUCTION_CONFLICT", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_DEPOSIT_DEDUCTION_CONFLICT 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G04", + "review_code": "E08_POSSESSION_DELIVERY_CONFLICT", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_POSSESSION_DELIVERY_CONFLICT 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G05", + "review_code": "E08_CONCURRENT_PERFORMANCE_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_CONCURRENT_PERFORMANCE_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G06", + "review_code": "E08_SPECIAL_LAW_PROFILE_REQUIRED_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_SPECIAL_LAW_PROFILE_REQUIRED_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G07", + "review_code": "E08_BOUNDARY_OVERLAP_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_BOUNDARY_OVERLAP_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G08", + "review_code": "E08_MEETING_ONLY_MONITOR_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_MEETING_ONLY_MONITOR_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G09", + "review_code": "E08_CALCULATION_PARTIAL_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_CALCULATION_PARTIAL_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G10", + "review_code": "E08_SOURCE_MEMBERSHIP_REVIEW", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 E08_SOURCE_MEMBERSHIP_REVIEW 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E08-G11", + "review_code": "CALCULATION_PREEMPTION_BLOCKED", + "evaluation": "deterministic", + "condition": "증거슬롯·source membership·routing 또는 계산 operand가 CALCULATION_PREEMPTION_BLOCKED 조건을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "BLOCKED_PENDING_OPERANDS", + "blocks_final_drafting": true + } + } + ], + "global_rules": { + "review_count_non_decrease": true, + "silent_drop_forbidden": true, + "unknown_source_rejected": true, + "meeting_only_cannot_promote_to_active": true, + "calculation_formula_hardcoding_forbidden": true + }, + "closure_status": "CLOSED" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-09/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-09/gates.json new file mode 100644 index 00000000..c7fbd6ca --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-09/gates.json @@ -0,0 +1,61 @@ +{ + "schema_version": "s2_domain_gates.v1", + "domain_id": "E-09", + "rules": [ + { + "gate_id": "E09-G01", + "review_code": "E09_CAUSE_REGISTRY_MISMATCH_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E09_CAUSE_REGISTRY_MISMATCH_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E09-G02", + "review_code": "E09_REGISTRY_SEQUENCE_CONFLICT", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E09_REGISTRY_SEQUENCE_CONFLICT 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E09-G03", + "review_code": "E09_THIRD_PARTY_CONSENT_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E09_THIRD_PARTY_CONSENT_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E09-G04", + "review_code": "E09_FRACTION_UNRESOLVED_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E09_FRACTION_UNRESOLVED_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + } + ], + "global_rules": { + "review_count_non_decrease": true, + "silent_drop_forbidden": true, + "unknown_source_rejected": true + }, + "legacy_gate_projection": [], + "closure_status": "CLOSED" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-10/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-10/gates.json new file mode 100644 index 00000000..99e83e17 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-10/gates.json @@ -0,0 +1,88 @@ +{ + "schema_version": "s2_domain_gates.v1", + "domain_id": "E-10", + "rules": [ + { + "gate_id": "E10-G01", + "review_code": "E10_SECURED_BALANCE_CONFLICT", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E10_SECURED_BALANCE_CONFLICT 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E10-G02", + "review_code": "E10_REGISTRY_SEQUENCE_CONFLICT", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E10_REGISTRY_SEQUENCE_CONFLICT 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E10-G03", + "review_code": "E10_AUCTION_DISTRIBUTION_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E10_AUCTION_DISTRIBUTION_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E10-G04", + "review_code": "E10_LIEN_CAUSE_POSSESSION_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 E10_LIEN_CAUSE_POSSESSION_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + }, + { + "gate_id": "E10-G05", + "review_code": "POSSESSION_CAUSE_CONFLICT", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 POSSESSION_CAUSE_CONFLICT 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": true + } + }, + { + "gate_id": "E10-G06", + "review_code": "LIEN_ASSERTED_AFTER_EXTINCTION_NOTICE_REVIEW", + "evaluation": "deterministic", + "condition": "관련 필수 요소·증거 상태가 LIEN_ASSERTED_AFTER_EXTINCTION_NOTICE_REVIEW 규칙을 충족", + "source_membership_required": true, + "on_true": { + "preserve_seed": true, + "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": false + } + } + ], + "global_rules": { + "review_count_non_decrease": true, + "silent_drop_forbidden": true, + "unknown_source_rejected": true + }, + "legacy_gate_projection": [ + "LEGACY-GATE-01", + "LEGACY-GATE-03" + ], + "closure_status": "CLOSED" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-11/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-11/gates.json new file mode 100644 index 00000000..8c07e922 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-11/gates.json @@ -0,0 +1,107 @@ +{ + "schema_version": "stage1_domain_gates.v1", + "domain_id": "E-11", + "status": "ACTIVE", + "legacy_contract_refs": [ + "S_0_GPT/legacy/part4/deterministic_gates_contract.json", + "S_0_GPT/legacy/part4/structure_projection_contract.json" + ], + "gates": [ + { + "gate_id": "E11-G01", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-11.domain_extension.v2", + "E-11.effect_projection.v1" + ], + "condition": "동일 asset_ref와 중첩 기간에 양립 불가능한 점유원인 쌍이 존재", + "emitted_review_codes": [ + "POSSESSION_CAUSE_CONFLICT", + "LEGAL_CAUSE_STATE_UNKNOWN_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E11-G02", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-11.domain_extension.v2", + "E-11.effect_projection.v1" + ], + "condition": "인도·철거·방해제거 대상의 표시·범위·수량이 서로 충돌", + "emitted_review_codes": [ + "E11_OBJECT_OR_SCOPE_CONFLICT", + "E11_ASSET_BINDING_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E11-G03", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-11.domain_extension.v2", + "E-11.effect_projection.v1" + ], + "condition": "권리관계 또는 현재 점유 상태의 필수 슬롯이 누락", + "emitted_review_codes": [ + "E11_REQUIRED_ELEMENT_GAP_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E11-G04", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-11.domain_extension.v2", + "E-11.effect_projection.v1" + ], + "condition": "점유 권원 또는 반환·방해배제 의무를 Stage 1에서 확정하려 함", + "emitted_review_codes": [ + "FINAL_LEGAL_CONCLUSION_BLOCKED" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + } + ], + "global_rules": { + "review_count_may_not_decrease_without_resolution_ref": true, + "missing_input_is_not_false": true, + "final_legal_conclusion_forbidden": true, + "fact_ledger_write_continues_with_review": true + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-12/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-12/gates.json new file mode 100644 index 00000000..7eeaec63 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-12/gates.json @@ -0,0 +1 @@ +{"domain_id":"E-12","gates":[{"condition":"required element/evidence slot이 누락·부분충족","emitted_review_codes":["E12_REQUIRED_ELEMENT_GAP_REVIEW"],"gate_id":"E12-G01","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-12.domain_extension.v1","E-12.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":false,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"당사자·객체·행위·효력·인과·권리귀속의 핵심 원천이 상충","emitted_review_codes":["E12_TITLE_SHARE_OR_OBJECT_CONFLICT_REVIEW","E12_GOVERNANCE_OR_RESOLUTION_CONFLICT_REVIEW","E12_BOUNDARY_OR_PARTITION_CONFLICT_REVIEW"],"gate_id":"E12-G02","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-12.domain_extension.v1","E-12.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":false,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"법률행위·침해·유통·권리행사 기준일 또는 적용법 version이 누락·상충","emitted_review_codes":["E12_LAW_VERSION_OR_PERIOD_INPUT_REVIEW"],"gate_id":"E12-G03","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-12.domain_extension.v1","E-12.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"필요 특별법 profile·전문 leaf·민사/공법/제외 트랙 경계가 닫히지 않음","emitted_review_codes":["E12_SPECIAL_LAW_OR_TRACK_BOUNDARY_REVIEW","SPECIAL_LAW_PROFILE_REQUIRED_REVIEW","TRACK_BOUNDARY_REVIEW","PUBLIC_LAW_NEXUS_REVIEW"],"gate_id":"E12-G04","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-12.domain_extension.v1","E-12.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"계산 operand가 partial 또는 blocked인데 산식·단일값·법정수치를 확정하려 함","emitted_review_codes":["E12_CALCULATION_OPERAND_PARTIAL_REVIEW","E12_CALCULATION_OPERAND_BLOCKED_REVIEW","MISSING_REQUIRED_OPERAND","CALCULATION_PREEMPTION_BLOCKED"],"gate_id":"E12-G05","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-12.domain_extension.v1","E-12.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"meeting-only 진술을 증거로 승격하거나 최종 법률결론·청구금액·소장문구를 생성하려 함","emitted_review_codes":["E12_MEETING_ONLY_SUPPORT_REVIEW","FINAL_LEGAL_CONCLUSION_BLOCKED"],"gate_id":"E12-G06","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-12.domain_extension.v1","E-12.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}}],"global_rules":{"fact_ledger_write_continues_with_review":true,"final_legal_conclusion_forbidden":true,"meeting_only_evidence_promotion_forbidden":true,"missing_input_is_not_false":true,"review_count_may_not_decrease_without_resolution_ref":true,"runtime_case_name_lookup_forbidden":true,"statutory_numeric_constant_hardcoding_forbidden":true},"schema_version":"stage1_domain_gates.v1","status":"ACTIVE"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-13/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-13/gates.json new file mode 100644 index 00000000..0361cad5 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-13/gates.json @@ -0,0 +1,130 @@ +{ + "schema_version": "stage1_domain_gates.v1", + "domain_id": "E-13", + "status": "ACTIVE", + "legacy_contract_refs": [ + "S_0_GPT/legacy/part4/deterministic_gates_contract.json", + "S_0_GPT/legacy/part4/structure_projection_contract.json" + ], + "gates": [ + { + "gate_id": "E13-G01", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-13.domain_extension.v2", + "E-13.effect_projection.v1" + ], + "condition": "피보전채권·채무자 행위·책임재산 핵심 슬롯 누락", + "emitted_review_codes": [ + "E13_REQUIRED_ELEMENT_GAP_REVIEW", + "E13_PRESERVED_CLAIM_GAP_REVIEW", + "E13_BALANCE_SHEET_INCOMPLETE_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E13-G02", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-13.domain_extension.v2", + "E-13.effect_projection.v1" + ], + "condition": "처분 기준일의 자산·부채·선순위권·가치 후보가 상충", + "emitted_review_codes": [ + "E13_BALANCE_SHEET_CONFLICT" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E13-G03", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-13.domain_extension.v2", + "E-13.effect_projection.v1" + ], + "condition": "대가·정상거래·수익자 또는 전득자 인식 자료가 상충", + "emitted_review_codes": [ + "E13_CONSIDERATION_OR_GOOD_FAITH_CONFLICT" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E13-G04", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-13.domain_extension.v2", + "E-13.effect_projection.v1" + ], + "condition": "행위일·원인 인지일·제소일 후보가 불완전하거나 충돌", + "emitted_review_codes": [ + "E13_TIME_BAR_DATE_CONFLICT" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "E13-G05", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "E-13.domain_extension.v2", + "E-13.effect_projection.v1" + ], + "condition": "채권자대위·채권자취소 또는 원물반환·가액배상을 근거 없이 병합하거나 확정", + "emitted_review_codes": [ + "E13_THEORY_TRACK_MERGE_BLOCKED", + "FINAL_LEGAL_CONCLUSION_BLOCKED" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + } + ], + "global_rules": { + "review_count_may_not_decrease_without_resolution_ref": true, + "missing_input_is_not_false": true, + "final_legal_conclusion_forbidden": true, + "fact_ledger_write_continues_with_review": true + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-14/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-14/gates.json new file mode 100644 index 00000000..8823c106 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-14/gates.json @@ -0,0 +1,91 @@ +{ + "schema_version": "stage1_s3_domain_gates.v1", + "domain_id": "E-14", + "rules": [ + { + "gate_id": "E14-G01", + "evaluation_mode": "deterministic", + "condition": { + "required_slots_missing_or_conflicting": [ + "e14.el01", + "e14.el02", + "e14.el03" + ] + }, + "emitted_codes": [ + "E14_REQUIRED_ELEMENT_GAP_REVIEW", + "E14_ORDER_SERVICE_FINALITY_REVIEW", + "E14_ATTACHED_CLAIM_IDENTITY_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E14-G02", + "evaluation_mode": "deterministic", + "condition": { + "boundary_or_overlap_detected": [ + "E-02", + "E-09", + "E-10", + "E-13", + "X3" + ] + }, + "emitted_codes": [ + "E14_EXECUTION_OVERLAP_REVIEW", + "E14_PROCEDURE_ONLY_REVIEW", + "TRACK_BOUNDARY_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E14-G03", + "evaluation_mode": "deterministic", + "condition": { + "calculation_operand_state_in": [ + "partial", + "blocked" + ] + }, + "emitted_codes": [ + "MISSING_REQUIRED_OPERAND", + "CALCULATION_BLOCKED_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E14-G04", + "evaluation_mode": "deterministic", + "condition": { + "procedure_only_or_final_conclusion_attempted": true + }, + "emitted_codes": [ + "E14_PROCEDURE_ONLY_REVIEW", + "TRACK_BOUNDARY_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + } + ], + "global_status_rule": "unknown or conflicting material is preserved; no worker may finalize an application, enforcement result, labor status, liability, or amount", + "closure_status": "IMPLEMENTED_LOCAL_DEFINITION" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-15/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-15/gates.json new file mode 100644 index 00000000..c393224a --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-15/gates.json @@ -0,0 +1,94 @@ +{ + "schema_version": "stage1_s2_domain_gates.v1", + "domain_id": "E-15", + "legacy_gate_source": { + "path": "S_0_GPT/legacy/part4/deterministic_gates_contract.json", + "sha256": "2b1b35bf1f28ba8f48401fffc86cebf5addad53570b9f8ab159f32c801d39752" + }, + "rules": [ + { + "gate_id": "E15-G01", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "E15_REQUIRED_ELEMENT_GAP_REVIEW", + "E15_HEIR_SCOPE_CONFLICT_REVIEW", + "E15_ASSET_SCOPE_GAP_REVIEW" + ] + }, + "emitted_codes": [ + "E15_REQUIRED_ELEMENT_GAP_REVIEW", + "E15_HEIR_SCOPE_CONFLICT_REVIEW", + "E15_ASSET_SCOPE_GAP_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E15-G02", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "E15_LIFECYCLE_CONFLICT_REVIEW", + "E15_SUCCESSION_STATUS_DEFENSE_REVIEW", + "E15_WILL_RESERVED_SHARE_REVIEW" + ] + }, + "emitted_codes": [ + "E15_LIFECYCLE_CONFLICT_REVIEW", + "E15_SUCCESSION_STATUS_DEFENSE_REVIEW", + "E15_WILL_RESERVED_SHARE_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": false + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E15-G03", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "E15_LAW_VERSION_REVIEW", + "MISSING_REQUIRED_OPERAND", + "TRACK_BOUNDARY_REVIEW" + ] + }, + "emitted_codes": [ + "E15_LAW_VERSION_REVIEW", + "MISSING_REQUIRED_OPERAND", + "TRACK_BOUNDARY_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + } + ], + "legacy_equivalence_projections": [], + "global_status_rule": "any review code yields READY_WITH_REVIEW; hard input or contract failure yields FAILED/BLOCKED", + "closure_status": "IMPLEMENTED_LOCAL_DEFINITION" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-16/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-16/gates.json new file mode 100644 index 00000000..e544cd27 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-16/gates.json @@ -0,0 +1 @@ +{"domain_id":"E-16","gates":[{"condition":"required element/evidence slot이 누락·부분충족","emitted_review_codes":["E16_REQUIRED_ELEMENT_GAP_REVIEW"],"gate_id":"E16-G01","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-16.domain_extension.v1","E-16.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":false,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"당사자·객체·행위·효력·인과·권리귀속의 핵심 원천이 상충","emitted_review_codes":["E16_INSTRUMENT_FORM_OR_COMPLETION_CONFLICT_REVIEW","E16_ENDORSEMENT_OR_HOLDER_CONFLICT_REVIEW","E16_PRESENTMENT_RECOURSE_OR_CAUSE_CONFLICT_REVIEW"],"gate_id":"E16-G02","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-16.domain_extension.v1","E-16.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":false,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"법률행위·침해·유통·권리행사 기준일 또는 적용법 version이 누락·상충","emitted_review_codes":["E16_LAW_VERSION_OR_PERIOD_INPUT_REVIEW"],"gate_id":"E16-G03","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-16.domain_extension.v1","E-16.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"필요 특별법 profile·전문 leaf·민사/공법/제외 트랙 경계가 닫히지 않음","emitted_review_codes":["E16_SPECIAL_LAW_OR_TRACK_BOUNDARY_REVIEW","SPECIAL_LAW_PROFILE_REQUIRED_REVIEW","TRACK_BOUNDARY_REVIEW","PUBLIC_LAW_NEXUS_REVIEW"],"gate_id":"E16-G04","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-16.domain_extension.v1","E-16.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"계산 operand가 partial 또는 blocked인데 산식·단일값·법정수치를 확정하려 함","emitted_review_codes":["E16_CALCULATION_OPERAND_PARTIAL_REVIEW","E16_CALCULATION_OPERAND_BLOCKED_REVIEW","MISSING_REQUIRED_OPERAND","CALCULATION_PREEMPTION_BLOCKED"],"gate_id":"E16-G05","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-16.domain_extension.v1","E-16.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":"meeting-only 진술을 증거로 승격하거나 최종 법률결론·청구금액·소장문구를 생성하려 함","emitted_review_codes":["E16_MEETING_ONLY_SUPPORT_REVIEW","FINAL_LEGAL_CONCLUSION_BLOCKED"],"gate_id":"E16-G06","implementation_type":"deterministic_predicate_spec","input_contracts":["domain_seed_output.v3","E-16.domain_extension.v1","E-16.effect_projection.v1","stage1_domain_signals.v2"],"row_effect":{"must_consider":true},"unknown_or_missing_policy":"fail_open_to_review","writer_effect":{"blocks_final_drafting":true,"status_on_fire":"READY_WITH_REVIEW","writes_fact_ledger":true}}],"global_rules":{"fact_ledger_write_continues_with_review":true,"final_legal_conclusion_forbidden":true,"meeting_only_evidence_promotion_forbidden":true,"missing_input_is_not_false":true,"review_count_may_not_decrease_without_resolution_ref":true,"runtime_case_name_lookup_forbidden":true,"statutory_numeric_constant_hardcoding_forbidden":true},"schema_version":"stage1_domain_gates.v1","status":"ACTIVE"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-17/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-17/gates.json new file mode 100644 index 00000000..dbd93354 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-17/gates.json @@ -0,0 +1,88 @@ +{ + "schema_version": "stage1_s3_domain_gates.v1", + "domain_id": "E-17", + "rules": [ + { + "gate_id": "E17-G01", + "evaluation_mode": "deterministic", + "condition": { + "required_slots_missing_or_conflicting": [ + "e17.el01", + "e17.el02", + "e17.el03" + ] + }, + "emitted_codes": [ + "E17_REQUIRED_ELEMENT_GAP_REVIEW", + "E17_WORKER_STATUS_CONFLICT_REVIEW", + "E17_WAGE_ITEM_CONFLICT_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E17-G02", + "evaluation_mode": "deterministic", + "condition": { + "boundary_or_overlap_detected": [ + "E-05", + "X3" + ] + }, + "emitted_codes": [ + "E17_INDUSTRIAL_ACCIDENT_OVERLAP_REVIEW", + "E17_PROCEDURE_BOUNDARY_REVIEW", + "TRACK_BOUNDARY_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E17-G03", + "evaluation_mode": "deterministic", + "condition": { + "calculation_operand_state_in": [ + "partial", + "blocked" + ] + }, + "emitted_codes": [ + "MISSING_REQUIRED_OPERAND", + "CALCULATION_BLOCKED_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "E17-G04", + "evaluation_mode": "deterministic", + "condition": { + "procedure_only_or_final_conclusion_attempted": true + }, + "emitted_codes": [ + "E17_PROCEDURE_BOUNDARY_REVIEW", + "TRACK_BOUNDARY_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + } + ], + "global_status_rule": "unknown or conflicting material is preserved; no worker may finalize an application, enforcement result, labor status, liability, or amount", + "closure_status": "IMPLEMENTED_LOCAL_DEFINITION" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-18/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-18/gates.json new file mode 100644 index 00000000..7998345e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-18/gates.json @@ -0,0 +1 @@ +{"closure_status":"IMPLEMENTED_LOCAL_DEFINITION","domain_id":"E-18","global_status_rule":"unknown, conflicting, meeting-only and missing-law material is preserved; no Stage 1 worker may finalize entitlement, liability, validity or amount","rules":[{"condition":{"required_slots_missing_or_conflicting":["e18.el01","e18.el02","e18.el03"]},"emitted_codes":["E18_ENTITY_CONSTITUTION_REVIEW","E18_RESOLUTION_EXISTENCE_VALIDITY_REVIEW","E18_ACTION_PERIOD_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E18-G01","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"boundary_or_overlap_detected":["E-01","E-02","E-04","E-09","X3"]},"emitted_codes":["E18_RELIEF_POSTURE_REVIEW","TRACK_BOUNDARY_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E18-G02","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"calculation_operand_state_in":["partial","blocked","deferred"]},"emitted_codes":["MISSING_REQUIRED_OPERAND","CALCULATION_BLOCKED_REVIEW","DEFERRED_CALCULATION_TRACK_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E18-G03","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"final_conclusion_or_missing_profile_attempted":true},"emitted_codes":["E18_ENTITY_CONSTITUTION_REVIEW","TRACK_BOUNDARY_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E18-G04","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}}],"schema_version":"stage1_s6_domain_gates.v1"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-19/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-19/gates.json new file mode 100644 index 00000000..d0e8e603 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-19/gates.json @@ -0,0 +1 @@ +{"closure_status":"IMPLEMENTED_LOCAL_DEFINITION","domain_id":"E-19","global_status_rule":"unknown, conflicting, meeting-only and missing-law material is preserved; no Stage 1 worker may finalize entitlement, liability, validity or amount","rules":[{"condition":{"required_slots_missing_or_conflicting":["e19.el01","e19.el02","e19.el03"]},"emitted_codes":["E19_POLICY_VERSION_REVIEW","E19_COVERAGE_EXCLUSION_REVIEW","E19_DISCLOSURE_NOTICE_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E19-G01","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"boundary_or_overlap_detected":["E-01","E-05","E-12","E-17","X1","X3"]},"emitted_codes":["E19_SPECIAL_TERMS_INSURANCE_BUSINESS_REVIEW","TRACK_BOUNDARY_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E19-G02","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"calculation_operand_state_in":["partial","blocked","deferred"]},"emitted_codes":["MISSING_REQUIRED_OPERAND","CALCULATION_BLOCKED_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E19-G03","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"final_conclusion_or_missing_profile_attempted":true},"emitted_codes":["E19_POLICY_VERSION_REVIEW","TRACK_BOUNDARY_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E19-G04","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}}],"schema_version":"stage1_s6_domain_gates.v1"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-20/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-20/gates.json new file mode 100644 index 00000000..0918cbba --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-20/gates.json @@ -0,0 +1 @@ +{"closure_status":"IMPLEMENTED_LOCAL_DEFINITION","domain_id":"E-20","global_status_rule":"unknown, conflicting, meeting-only and missing-law material is preserved; no Stage 1 worker may finalize entitlement, liability, validity or amount","rules":[{"condition":{"required_slots_missing_or_conflicting":["e20.el01","e20.el02","e20.el03"]},"emitted_codes":["E20_RIGHT_IDENTITY_VALIDITY_REVIEW","E20_OWNERSHIP_LICENSE_REVIEW","E20_SCOPE_COMPARISON_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E20-G01","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"boundary_or_overlap_detected":["E-01","E-05","E-12","E-16","X3"]},"emitted_codes":["E20_OVERLAP_REVIEW","TRACK_BOUNDARY_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E20-G02","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"calculation_operand_state_in":["partial","blocked","deferred"]},"emitted_codes":["MISSING_REQUIRED_OPERAND","CALCULATION_BLOCKED_REVIEW","DEFERRED_CALCULATION_TRACK_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E20-G03","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"final_conclusion_or_missing_profile_attempted":true},"emitted_codes":["E20_SPECIAL_LAW_PROFILE_REQUIRED","TRACK_BOUNDARY_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E20-G04","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}}],"schema_version":"stage1_s6_domain_gates.v1"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-21/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-21/gates.json new file mode 100644 index 00000000..027ace3b --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/E-21/gates.json @@ -0,0 +1 @@ +{"closure_status":"IMPLEMENTED_LOCAL_DEFINITION","domain_id":"E-21","global_status_rule":"unknown, conflicting, meeting-only and missing-law material is preserved; no Stage 1 worker may finalize entitlement, liability, validity or amount","rules":[{"condition":{"required_slots_missing_or_conflicting":["e21.el01","e21.el02","e21.el03"]},"emitted_codes":["E21_PUBLICATION_VERSION_REVIEW","E21_IDENTIFIABILITY_REVIEW","E21_FACT_OPINION_TRUTH_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E21-G01","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"boundary_or_overlap_detected":["E-01","E-05","E-12","E-20","X1","X3"]},"emitted_codes":["E21_SPECIAL_LAW_PROFILE_REQUIRED","TRACK_BOUNDARY_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E21-G02","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"calculation_operand_state_in":["partial","blocked","deferred"]},"emitted_codes":["MISSING_REQUIRED_OPERAND","CALCULATION_BLOCKED_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E21-G03","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}},{"condition":{"final_conclusion_or_missing_profile_attempted":true},"emitted_codes":["E21_SOURCE_CONFLICT_REVIEW","TRACK_BOUNDARY_REVIEW"],"evaluation_mode":"deterministic","gate_id":"E21-G04","silent_repair_forbidden":true,"writer_effect":{"final_drafting_block":true,"status_when_triggered":"READY_WITH_REVIEW","writes_fact_ledger":true}}],"schema_version":"stage1_s6_domain_gates.v1"} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/X1/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/X1/gates.json new file mode 100644 index 00000000..09163730 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/X1/gates.json @@ -0,0 +1,114 @@ +{ + "schema_version": "stage1_s2_domain_gates.v1", + "domain_id": "X1", + "legacy_gate_source": { + "path": "S_0_GPT/legacy/part4/deterministic_gates_contract.json", + "sha256": "2b1b35bf1f28ba8f48401fffc86cebf5addad53570b9f8ab159f32c801d39752" + }, + "rules": [ + { + "gate_id": "X1-G01", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "X1_REQUIRED_ELEMENT_GAP_REVIEW", + "X1_ARRIVAL_UNPROVEN_REVIEW", + "NOTICE_RECEIPT_UNVERIFIED", + "BLOCK_FINAL_DRAFTING_NOTICE_RECEIPT_MISSING" + ] + }, + "emitted_codes": [ + "X1_REQUIRED_ELEMENT_GAP_REVIEW", + "X1_ARRIVAL_UNPROVEN_REVIEW", + "NOTICE_RECEIPT_UNVERIFIED", + "BLOCK_FINAL_DRAFTING_NOTICE_RECEIPT_MISSING" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "X1-G02", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "X1_LIFECYCLE_DATE_CONFLICT", + "X1_RECIPIENT_OR_CONTENT_CONFLICT", + "NOTICE_RECEIPT_EVIDENCE_WEAK_REVIEW" + ] + }, + "emitted_codes": [ + "X1_LIFECYCLE_DATE_CONFLICT", + "X1_RECIPIENT_OR_CONTENT_CONFLICT", + "NOTICE_RECEIPT_EVIDENCE_WEAK_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": false + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "X1-G03", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "CALCULATION_PREEMPTION_BLOCKED" + ] + }, + "emitted_codes": [ + "CALCULATION_PREEMPTION_BLOCKED" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + } + ], + "legacy_equivalence_projections": [ + { + "legacy_gate_id": "LEGACY-GATE-02", + "successor_gate_id": "X1-G01", + "branch": "receipt_missing", + "condition": "requires_receipt_for_effect_candidate=true and arrival_or_received_candidate is null", + "emitted_codes": [ + "NOTICE_RECEIPT_UNVERIFIED", + "BLOCK_FINAL_DRAFTING_NOTICE_RECEIPT_MISSING" + ], + "equivalence": "preserved" + }, + { + "legacy_gate_id": "LEGACY-GATE-02", + "successor_gate_id": "X1-G02", + "branch": "receipt_evidence_weak", + "condition": "arrival_or_received_candidate is not null and receipt_evidence_refs empty and recipient_admission_candidate null", + "emitted_codes": [ + "NOTICE_RECEIPT_EVIDENCE_WEAK_REVIEW" + ], + "equivalence": "preserved" + } + ], + "global_status_rule": "any review code yields READY_WITH_REVIEW; hard input or contract failure yields FAILED/BLOCKED", + "closure_status": "IMPLEMENTED_LOCAL_DEFINITION" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/X2/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/X2/gates.json new file mode 100644 index 00000000..9fa11d47 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/X2/gates.json @@ -0,0 +1,128 @@ +{ + "schema_version": "stage1_domain_gates.v1", + "domain_id": "X2", + "status": "ACTIVE", + "legacy_contract_refs": [ + "S_0_GPT/legacy/part4/deterministic_gates_contract.json", + "S_0_GPT/legacy/part4/structure_projection_contract.json" + ], + "gates": [ + { + "gate_id": "X2-G01", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "X2.domain_extension.v2", + "X2.effect_projection.v1" + ], + "condition": "선택된 자산 원천에 canonical asset identifier 또는 표시가 없음", + "emitted_review_codes": [ + "X2_ASSET_IDENTITY_GAP_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "X2-G02", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "X2.domain_extension.v2", + "X2.effect_projection.v1" + ], + "condition": "alias·지번·동호수·면적·지분이 복수 자산에 상충 연결", + "emitted_review_codes": [ + "X2_ASSET_IDENTITY_CONFLICT", + "X2_SHARE_OR_AREA_CONFLICT" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "X2-G03", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "X2.domain_extension.v2", + "X2.effect_projection.v1" + ], + "condition": "동일 기준시점의 소유·점유·사용·수익 또는 등기 lineage가 충돌", + "emitted_review_codes": [ + "X2_STATE_OR_LINEAGE_CONFLICT" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "X2-G04", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "X2.domain_extension.v2", + "X2.effect_projection.v1" + ], + "condition": "평가값이 대상 자산 또는 기준시점과 결합되지 않음", + "emitted_review_codes": [ + "X2_VALUATION_BINDING_REVIEW" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": false, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + }, + { + "gate_id": "X2-G05", + "implementation_type": "deterministic_predicate_spec", + "input_contracts": [ + "domain_seed_output.v3", + "X2.domain_extension.v2", + "X2.effect_projection.v1" + ], + "condition": "자산 동일성·권리귀속을 사실 연결을 넘어 최종 확정하려 함", + "emitted_review_codes": [ + "FINAL_LEGAL_CONCLUSION_BLOCKED" + ], + "row_effect": { + "must_consider": true + }, + "writer_effect": { + "status_on_fire": "READY_WITH_REVIEW", + "blocks_final_drafting": true, + "writes_fact_ledger": true + }, + "unknown_or_missing_policy": "fail_open_to_review" + } + ], + "global_rules": { + "review_count_may_not_decrease_without_resolution_ref": true, + "missing_input_is_not_false": true, + "final_legal_conclusion_forbidden": true, + "fact_ledger_write_continues_with_review": true + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/X3/gates.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/X3/gates.json new file mode 100644 index 00000000..22e2404e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/domains/X3/gates.json @@ -0,0 +1,115 @@ +{ + "schema_version": "stage1_s2_domain_gates.v1", + "domain_id": "X3", + "legacy_gate_source": { + "path": "S_0_GPT/legacy/part4/deterministic_gates_contract.json", + "sha256": "2b1b35bf1f28ba8f48401fffc86cebf5addad53570b9f8ab159f32c801d39752" + }, + "rules": [ + { + "gate_id": "X3-G01", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "X3_PARTY_STANDING_GAP_REVIEW", + "X3_PARTY_STANDING_CONFLICT_REVIEW", + "X3_CAPACITY_AUTHORITY_REVIEW" + ] + }, + "emitted_codes": [ + "X3_PARTY_STANDING_GAP_REVIEW", + "X3_PARTY_STANDING_CONFLICT_REVIEW", + "X3_CAPACITY_AUTHORITY_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "X3-G02", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "X3_RELIEF_FORM_GAP_REVIEW", + "X3_EXTRAORDINARY_REMEDY_REVIEW" + ] + }, + "emitted_codes": [ + "X3_RELIEF_FORM_GAP_REVIEW", + "X3_EXTRAORDINARY_REMEDY_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "X3-G03", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "TRACK_BOUNDARY_REVIEW", + "PUBLIC_LAW_NEXUS_REVIEW" + ] + }, + "emitted_codes": [ + "TRACK_BOUNDARY_REVIEW", + "PUBLIC_LAW_NEXUS_REVIEW" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + }, + { + "gate_id": "X3-G04", + "evaluation_mode": "deterministic", + "input_fields": [ + "bo_seed_candidates", + "review_items", + "extensions.domain_payload.payload" + ], + "condition": { + "any_review_code_present": [ + "MISSING_REQUIRED_OPERAND", + "CALCULATION_PREEMPTION_BLOCKED" + ] + }, + "emitted_codes": [ + "MISSING_REQUIRED_OPERAND", + "CALCULATION_PREEMPTION_BLOCKED" + ], + "writer_effect": { + "status_when_triggered": "READY_WITH_REVIEW", + "writes_fact_ledger": true, + "final_drafting_block": true + }, + "silent_repair_forbidden": true + } + ], + "legacy_equivalence_projections": [], + "global_status_rule": "any review code yields READY_WITH_REVIEW; hard input or contract failure yields FAILED/BLOCKED", + "closure_status": "IMPLEMENTED_LOCAL_DEFINITION" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/fact_exception_pack.schema.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/fact_exception_pack.schema.json new file mode 100644 index 00000000..00cc8012 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/fact_exception_pack.schema.json @@ -0,0 +1,93 @@ +{ + "$id": "https://schemas.liti-agent.local/stage1/part4/fact_exception_pack.schema.json", + "title": "stage1_fact_exception_pack.v1", + "description": "Part 4 F0 -> F1 예외 pack. 판정 부류는 정책 f1_exception_types 가 선언하며 unreachable_in_v8 부류가 실리면 PART4_UNREACHABLE_EXCEPTION_TYPE 경성 실패다.", + "type": "object", + "additionalProperties": false, + "required": [ + "fact_exception_pack" + ], + "properties": { + "fact_exception_pack": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "has_exceptions", + "exception_count", + "exceptions", + "budget" + ], + "properties": { + "schema_version": { + "const": "stage1_fact_exception_pack.v1" + }, + "has_exceptions": { + "type": "boolean" + }, + "exception_count": { + "type": "integer", + "minimum": 0 + }, + "exceptions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "exception_id", + "exception_type", + "fact_id", + "source_bo_id", + "conflict_flags", + "compact_payload" + ], + "properties": { + "exception_id": { + "type": "string", + "pattern": "^FX-[0-9]{3}$" + }, + "exception_type": { + "type": "string" + }, + "fact_id": { + "type": "string", + "pattern": "^F-[0-9]{3}$" + }, + "source_bo_id": { + "type": "string", + "minLength": 1 + }, + "conflict_flags": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + } + }, + "compact_payload": { + "type": "object" + }, + "oversize_review": { + "type": "boolean" + } + } + } + }, + "budget": { + "type": "object", + "additionalProperties": false, + "required": [ + "pack_item_char_limit" + ], + "properties": { + "pack_item_char_limit": { + "type": "integer", + "minimum": 1 + } + } + } + } + } + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/fact_ledger_base.schema.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/fact_ledger_base.schema.json new file mode 100644 index 00000000..9aff0851 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/fact_ledger_base.schema.json @@ -0,0 +1,406 @@ +{ + "$id": "https://schemas.liti-agent.local/stage1/part4/fact_ledger_base.schema.json", + "title": "stage1_fact_ledger_base.v1", + "description": "Part 4 최종 산출 — 래핑 없는 배열. 행 29키 = 구 27(순서·이름 불변) + domain_effects·calculation_requests(required 에 올리지 않는다 — Stage 2 호환 D-P4-4). 열거를 하드코딩하지 않는다 — 도메인 ID·검토 코드는 패턴만 강제한다.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "fact_id", + "source_bo_id", + "type", + "date", + "parties", + "object_spec", + "amount", + "action", + "evidence_refs", + "credibility", + "legal_centrality", + "proof_strength", + "legal_effect_roles", + "claim_chain_ref", + "linked_structures", + "must_not_drop_in_claim_types", + "must_consider", + "state_context", + "money_claim_effect", + "commercial_successor_effect", + "actio_roles", + "linked_actio_structures", + "downstream_module_candidates", + "skeleton_validation_codes", + "derived_fact", + "derivation_basis", + "legal_calculation_object" + ], + "properties": { + "fact_id": { + "type": "string", + "pattern": "^F-[0-9]{3}$" + }, + "source_bo_id": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": [ + "string", + "null" + ] + }, + "date": { + "type": [ + "string", + "null" + ] + }, + "parties": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "role", + "value", + "source" + ], + "properties": { + "role": { + "type": "string" + }, + "value": { + "type": [ + "string", + "null" + ] + }, + "source": { + "type": "string" + } + } + } + }, + "object_spec": { + "type": [ + "string", + "null" + ] + }, + "amount": { + "type": [ + "string", + "null" + ] + }, + "action": { + "type": [ + "string", + "null" + ] + }, + "evidence_refs": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "credibility": { + "enum": [ + "high", + "medium", + "low" + ] + }, + "legal_centrality": { + "enum": [ + "critical", + "high", + "medium", + "low" + ] + }, + "proof_strength": { + "enum": [ + "documentary", + "high", + "medium", + "meeting_only_core", + "low" + ] + }, + "legal_effect_roles": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "claim_chain_ref": { + "type": "object", + "additionalProperties": false, + "required": [ + "claim_group_ids", + "liability_group_ids", + "signal_count" + ], + "properties": { + "claim_group_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "liability_group_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "signal_count": { + "type": "integer", + "minimum": 0 + } + } + }, + "linked_structures": { + "type": "object", + "additionalProperties": false, + "required": [ + "structure_ids" + ], + "properties": { + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "must_not_drop_in_claim_types": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "must_consider": { + "type": "boolean" + }, + "state_context": { + "type": "object", + "additionalProperties": false, + "required": [ + "bo_type", + "domain_ids" + ], + "properties": { + "bo_type": { + "type": [ + "string", + "null" + ] + }, + "domain_ids": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + }, + "uniqueItems": true + } + } + }, + "money_claim_effect": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "structure_ids" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "commercial_successor_effect": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "structure_ids" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "actio_roles": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "linked_actio_structures": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "downstream_module_candidates": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "skeleton_validation_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + } + }, + "derived_fact": { + "type": [ + "string", + "null" + ] + }, + "derivation_basis": { + "type": "string" + }, + "legal_calculation_object": { + "type": [ + "string", + "null" + ] + }, + "domain_effects": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "required": [ + "module", + "role_projection", + "structure_ids", + "type_ids", + "review_codes" + ], + "properties": { + "module": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "role_projection": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "type_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "review_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + }, + "uniqueItems": true + } + } + } + }, + "calculation_requests": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "calculation_id" + ], + "properties": { + "calculation_id": { + "type": [ + "string", + "null" + ] + }, + "request_id": { + "type": [ + "string", + "null" + ] + }, + "operand_state": { + "enum": [ + "ready", + "partial", + "blocked", + "deferred", + null + ] + }, + "missing_operands": { + "type": "array", + "items": { + "type": "string" + } + }, + "law_version_refs": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "provenance": { + "type": "object" + } + } + } + } + } + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/fact_ledger_candidate_bundle.schema.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/fact_ledger_candidate_bundle.schema.json new file mode 100644 index 00000000..82cfe4bb --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/fact_ledger_candidate_bundle.schema.json @@ -0,0 +1,471 @@ +{ + "$id": "https://schemas.liti-agent.local/stage1/part4/fact_ledger_candidate_bundle.schema.json", + "title": "stage1_fact_ledger_candidate_bundle.v1", + "description": "Part 4 F0 산출 — 후보 행 묶음. domain_effects·calculation_requests 를 아는 것이 F0 의미 검사(PART4_LEDGER_SCHEMA_STALE)의 대상이다.", + "type": "object", + "additionalProperties": false, + "required": [ + "fact_ledger_candidate_bundle" + ], + "properties": { + "fact_ledger_candidate_bundle": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "status", + "rows", + "gate_report", + "counts", + "signal_manifest_transaction_id", + "created_by" + ], + "properties": { + "schema_version": { + "const": "stage1_fact_ledger_candidate_bundle.v1" + }, + "status": { + "enum": [ + "READY", + "READY_WITH_REVIEW" + ] + }, + "rows": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "fact_id", + "source_bo_id", + "type", + "date", + "parties", + "object_spec", + "amount", + "action", + "evidence_refs", + "credibility", + "legal_centrality", + "proof_strength", + "legal_effect_roles", + "claim_chain_ref", + "linked_structures", + "must_not_drop_in_claim_types", + "must_consider", + "state_context", + "money_claim_effect", + "commercial_successor_effect", + "actio_roles", + "linked_actio_structures", + "downstream_module_candidates", + "skeleton_validation_codes", + "derived_fact", + "derivation_basis", + "legal_calculation_object" + ], + "properties": { + "fact_id": { + "type": "string", + "pattern": "^F-[0-9]{3}$" + }, + "source_bo_id": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": [ + "string", + "null" + ] + }, + "date": { + "type": [ + "string", + "null" + ] + }, + "parties": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "role", + "value", + "source" + ], + "properties": { + "role": { + "type": "string" + }, + "value": { + "type": [ + "string", + "null" + ] + }, + "source": { + "type": "string" + } + } + } + }, + "object_spec": { + "type": [ + "string", + "null" + ] + }, + "amount": { + "type": [ + "string", + "null" + ] + }, + "action": { + "type": [ + "string", + "null" + ] + }, + "evidence_refs": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "credibility": { + "enum": [ + "high", + "medium", + "low" + ] + }, + "legal_centrality": { + "enum": [ + "critical", + "high", + "medium", + "low" + ] + }, + "proof_strength": { + "enum": [ + "documentary", + "high", + "medium", + "meeting_only_core", + "low" + ] + }, + "legal_effect_roles": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "claim_chain_ref": { + "type": "object", + "additionalProperties": false, + "required": [ + "claim_group_ids", + "liability_group_ids", + "signal_count" + ], + "properties": { + "claim_group_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "liability_group_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "signal_count": { + "type": "integer", + "minimum": 0 + } + } + }, + "linked_structures": { + "type": "object", + "additionalProperties": false, + "required": [ + "structure_ids" + ], + "properties": { + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "must_not_drop_in_claim_types": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "must_consider": { + "type": "boolean" + }, + "state_context": { + "type": "object", + "additionalProperties": false, + "required": [ + "bo_type", + "domain_ids" + ], + "properties": { + "bo_type": { + "type": [ + "string", + "null" + ] + }, + "domain_ids": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + }, + "uniqueItems": true + } + } + }, + "money_claim_effect": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "structure_ids" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "commercial_successor_effect": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "structure_ids" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "actio_roles": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "linked_actio_structures": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "downstream_module_candidates": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "skeleton_validation_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + } + }, + "derived_fact": { + "type": [ + "string", + "null" + ] + }, + "derivation_basis": { + "type": "string" + }, + "legal_calculation_object": { + "type": [ + "string", + "null" + ] + }, + "domain_effects": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "required": [ + "module", + "role_projection", + "structure_ids", + "type_ids", + "review_codes" + ], + "properties": { + "module": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "role_projection": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "type_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "review_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + }, + "uniqueItems": true + } + } + } + }, + "calculation_requests": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "calculation_id" + ], + "properties": { + "calculation_id": { + "type": [ + "string", + "null" + ] + }, + "request_id": { + "type": [ + "string", + "null" + ] + }, + "operand_state": { + "enum": [ + "ready", + "partial", + "blocked", + "deferred", + null + ] + }, + "missing_operands": { + "type": "array", + "items": { + "type": "string" + } + }, + "law_version_refs": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "provenance": { + "type": "object" + } + } + } + } + } + } + }, + "gate_report": { + "type": "object" + }, + "counts": { + "type": "object" + }, + "signal_manifest_transaction_id": { + "type": [ + "string", + "null" + ] + }, + "les_transaction_seal": { + "enum": [ + "MATCHED", + "UNSEALED_REVIEW" + ] + }, + "expected_runnable_domain_ids": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + }, + "uniqueItems": true + }, + "alias_resolution": { + "type": "object" + }, + "created_by": { + "type": "string" + } + } + } + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/legal_effect_structures.schema.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/legal_effect_structures.schema.json index 687e98d9..420b1c99 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/legal_effect_structures.schema.json +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/platform/schemas/legal_effect_structures.schema.json @@ -1,150 +1,381 @@ { - "$id": "https://schemas.liti-agent.local/stage1/part3/legal_effect_structures.schema.json", - "title": "stage1_legal_effect_structures.v1", - "description": "Part 3 L2 최종 산출 — 법률효과 구조와 역인덱스 셋(by_domain_id · by_module · by_bo_id). by_claim_form 은 만들지 않는다(claim-form 어휘가 registry 에 없다 — D-4 라). 열거를 하드코딩하지 않는다.", - "type": "object", - "additionalProperties": false, - "required": ["schema_version", "status", "expected_runnable_domain_ids", "theta", - "structure_records", "structure_index", "quality_gate", "created_by"], - "properties": { - "schema_version": {"const": "stage1_legal_effect_structures.v1"}, - "status": {"enum": ["READY", "READY_WITH_REVIEW"]}, - "registry_version": {"type": ["string", "null"]}, - "registry_index_sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"}, - "activation_manifest_sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"}, - "signal_manifest_transaction_id": {"type": "string", "pattern": "^S5TX-[a-f0-9]{20}$"}, - "expected_runnable_domain_ids": { + "$id": "https://schemas.liti-agent.local/stage1/part3/legal_effect_structures.schema.json", + "title": "stage1_legal_effect_structures.v1", + "description": "Part 3 L2 최종 산출 — 법률효과 구조와 역인덱스 셋(by_domain_id · by_module · by_bo_id). by_claim_form 은 만들지 않는다(claim-form 어휘가 registry 에 없다 — D-4 라). 열거를 하드코딩하지 않는다.", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "status", + "expected_runnable_domain_ids", + "theta", + "structure_records", + "structure_index", + "quality_gate", + "created_by", + "signal_manifest_transaction_id" + ], + "properties": { + "schema_version": { + "const": "stage1_legal_effect_structures.v1" + }, + "status": { + "enum": [ + "READY", + "READY_WITH_REVIEW" + ] + }, + "registry_version": { + "type": [ + "string", + "null" + ] + }, + "registry_index_sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "activation_manifest_sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "signal_manifest_transaction_id": { + "type": "string", + "pattern": "^S5TX-[a-f0-9]{20}$" + }, + "expected_runnable_domain_ids": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + } + }, + "theta": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]{1,127}$" + } + }, + "structure_records": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "structure_id", + "domain_id", + "type_id", + "priority_rank", + "order_key", + "module", + "role_projection", + "admission_policy", + "routes", + "route_count", + "evidence_refs", + "meeting_clause_refs", + "calculation_request_refs", + "source_bo_ids", + "review_codes", + "review_required" + ], + "properties": { + "structure_id": { + "type": "string", + "pattern": "^LES-[A-Za-z0-9._:-]{2,64}-[a-z][a-z0-9_]{1,127}$" + }, + "domain_id": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + }, + "type_id": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]{1,127}$" + }, + "priority_rank": { + "type": [ + "integer", + "null" + ] + }, + "order_key": { "type": "array", - "items": {"type": "string", "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$"} - }, - "theta": { - "type": "array", - "items": {"type": "string", "pattern": "^[a-z][a-z0-9_]{1,127}$"} - }, - "structure_records": { + "minItems": 3, + "maxItems": 3 + }, + "module": { + "type": [ + "string", + "null" + ] + }, + "role_projection": { + "type": [ + "string", + "null" + ] + }, + "admission_policy": { + "type": "string", + "minLength": 1 + }, + "admission_rule": { + "type": "string" + }, + "registered_declared": { + "type": "boolean" + }, + "extension_key": { + "type": "string" + }, + "routes": { "type": "array", "items": { - "type": "object", - "additionalProperties": false, - "required": ["structure_id", "domain_id", "type_id", "priority_rank", "order_key", - "module", "role_projection", "admission_policy", "routes", "route_count", - "evidence_refs", "meeting_clause_refs", "calculation_request_refs", - "source_bo_ids", "review_codes", "review_required"], - "properties": { - "structure_id": {"type": "string", "pattern": "^LES-[A-Za-z0-9._:-]{2,64}-[a-z][a-z0-9_]{1,127}$"}, - "domain_id": {"type": "string", "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$"}, - "type_id": {"type": "string", "pattern": "^[a-z][a-z0-9_]{1,127}$"}, - "priority_rank": {"type": ["integer", "null"]}, - "order_key": {"type": "array", "minItems": 3, "maxItems": 3}, - "module": {"type": ["string", "null"]}, - "role_projection": {"type": ["string", "null"]}, - "admission_policy": {"type": "string", "minLength": 1}, - "admission_rule": {"type": "string"}, - "registered_declared": {"type": "boolean"}, - "extension_key": {"type": "string"}, - "routes": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": ["signal_id", "source_seed_id", "registered", "status"], - "properties": { - "signal_id": {"type": ["string", "null"]}, - "source_seed_id": {"type": ["string", "null"]}, - "registered": {"type": ["boolean", "null"]}, - "status": {"type": ["string", "null"]} - } - } - }, - "route_count": {"type": "integer", "minimum": 0}, - "evidence_refs": {"type": "array", "items": {"type": "string"}}, - "meeting_clause_refs": {"type": "array", "items": {"type": "string"}}, - "calculation_request_refs": {"type": "array", "items": {"type": "string"}}, - "source_bo_ids": {"type": "array", "items": {"type": "string"}}, - "review_codes": { - "type": "array", - "items": {"type": "string", "pattern": "^[A-Z][A-Z0-9_]{2,127}$"} - }, - "review_required": {"type": "boolean"} + "type": "object", + "additionalProperties": false, + "required": [ + "signal_id", + "source_seed_id", + "registered", + "status" + ], + "properties": { + "signal_id": { + "type": [ + "string", + "null" + ] + }, + "source_seed_id": { + "type": [ + "string", + "null" + ] + }, + "registered": { + "type": [ + "boolean", + "null" + ] + }, + "status": { + "type": [ + "string", + "null" + ] } + } } + }, + "route_count": { + "type": "integer", + "minimum": 0 + }, + "evidence_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "meeting_clause_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "calculation_request_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "source_bo_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "review_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + } + }, + "review_required": { + "type": "boolean" + } + } + } + }, + "structure_index": { + "type": "object", + "additionalProperties": false, + "required": [ + "by_domain_id", + "by_module", + "by_bo_id" + ], + "properties": { + "by_domain_id": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } }, - "structure_index": { + "by_module": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "by_bo_id": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "quality_gate": { + "type": "object", + "additionalProperties": false, + "required": [ + "declaration_conservation", + "route_conservation", + "bo_conservation", + "review_queue" + ], + "properties": { + "declaration_conservation": { + "type": "object", + "additionalProperties": false, + "required": [ + "structure_records", + "declared_total_from_config", + "conservation_pass" + ], + "properties": { + "structure_records": { + "type": "integer", + "minimum": 0 + }, + "declared_total_from_config": { + "type": "integer", + "minimum": 0 + }, + "conservation_pass": { + "type": "boolean" + } + } + }, + "route_conservation": { + "type": "object", + "additionalProperties": false, + "required": [ + "routes_attached", + "routes_unattached", + "sg13_route_total_from_source", + "conservation_pass" + ], + "properties": { + "routes_attached": { + "type": "integer", + "minimum": 0 + }, + "routes_unattached": { + "type": "integer", + "minimum": 0 + }, + "sg13_route_total_from_source": { + "type": "integer", + "minimum": 0 + }, + "conservation_pass": { + "type": "boolean" + } + } + }, + "bo_conservation": { + "type": "object", + "additionalProperties": false, + "required": [ + "bo_attached", + "bo_unattached", + "bo_total_from_source", + "conservation_pass" + ], + "properties": { + "bo_attached": { + "type": "integer", + "minimum": 0 + }, + "bo_unattached": { + "type": "integer", + "minimum": 0 + }, + "bo_total_from_source": { + "type": "integer", + "minimum": 0 + }, + "conservation_pass": { + "type": "boolean" + } + } + }, + "review_queue": { + "type": "array", + "items": { "type": "object", "additionalProperties": false, - "required": ["by_domain_id", "by_module", "by_bo_id"], + "required": [ + "review_code", + "severity" + ], "properties": { - "by_domain_id": { - "type": "object", - "additionalProperties": {"type": "array", "items": {"type": "string"}} - }, - "by_module": { - "type": "object", - "additionalProperties": {"type": "array", "items": {"type": "string"}} - }, - "by_bo_id": { - "type": "object", - "additionalProperties": {"type": "array", "items": {"type": "string"}} - } + "review_code": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + }, + "severity": { + "enum": [ + "SOFT_WARNING", + "HARD_WARNING" + ] + }, + "domain_id": { + "type": [ + "string", + "null" + ] + }, + "structure_id": { + "type": [ + "string", + "null" + ] + }, + "count": { + "type": "integer", + "minimum": 1 + } } - }, - "quality_gate": { - "type": "object", - "additionalProperties": false, - "required": ["declaration_conservation", "route_conservation", "bo_conservation", - "review_queue"], - "properties": { - "declaration_conservation": { - "type": "object", - "additionalProperties": false, - "required": ["structure_records", "declared_total_from_config", "conservation_pass"], - "properties": { - "structure_records": {"type": "integer", "minimum": 0}, - "declared_total_from_config": {"type": "integer", "minimum": 0}, - "conservation_pass": {"type": "boolean"} - } - }, - "route_conservation": { - "type": "object", - "additionalProperties": false, - "required": ["routes_attached", "routes_unattached", "sg13_route_total_from_source", - "conservation_pass"], - "properties": { - "routes_attached": {"type": "integer", "minimum": 0}, - "routes_unattached": {"type": "integer", "minimum": 0}, - "sg13_route_total_from_source": {"type": "integer", "minimum": 0}, - "conservation_pass": {"type": "boolean"} - } - }, - "bo_conservation": { - "type": "object", - "additionalProperties": false, - "required": ["bo_attached", "bo_unattached", "bo_total_from_source", - "conservation_pass"], - "properties": { - "bo_attached": {"type": "integer", "minimum": 0}, - "bo_unattached": {"type": "integer", "minimum": 0}, - "bo_total_from_source": {"type": "integer", "minimum": 0}, - "conservation_pass": {"type": "boolean"} - } - }, - "review_queue": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": ["review_code", "severity"], - "properties": { - "review_code": {"type": "string", "pattern": "^[A-Z][A-Z0-9_]{2,127}$"}, - "severity": {"enum": ["SOFT_WARNING", "HARD_WARNING"]}, - "domain_id": {"type": ["string", "null"]}, - "structure_id": {"type": ["string", "null"]}, - "count": {"type": "integer", "minimum": 1} - } - } - } - } - }, - "created_by": {"const": "Task_C_LE_L2_final_structure_index_writer"} + } + } + } + }, + "created_by": { + "const": "Task_C_LE_L2_final_structure_index_writer" } + } } diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/runtime_manifest.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/runtime_manifest.json index c5a729bf..2925d252 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/runtime_manifest.json +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/runtime_manifest.json @@ -824,6 +824,10 @@ "path": "contracts/signals/s5_execution_contract.v2.json", "sha256": "311ac8721ec297bc86e54c83f0866d61629bd99f15746f86bbbff32914d35e02" }, + { + "path": "domains/E-00/gates.json", + "sha256": "665499a6eaa2466846633e26c3736379091b52ca7514c8ffc14c742728c84057" + }, { "path": "domains/E-00/module_role_projection.json", "sha256": "278ecdd4ac6c004f9994d6357ef439d089456a5e65bed0f74f49170ba3315932" @@ -832,6 +836,10 @@ "path": "domains/E-00/structure_types.json", "sha256": "ae4a94fdbb0e2a8e0273a259e38c28774a3f2f24985878286f18a9ae05cbabaa" }, + { + "path": "domains/E-01/gates.json", + "sha256": "d3b5acefeaf2fd42cffabad18e1caa82dc5930c4d9638ac9f9caca20e58c7194" + }, { "path": "domains/E-01/module_role_projection.json", "sha256": "cb263ca339f7b749a51b57fa12d915fda3644f09e648d15a84202651a23a84fb" @@ -840,6 +848,10 @@ "path": "domains/E-01/structure_types.json", "sha256": "f2c423a0346a347292b3ca33d3a9f33a82d540e83cbb5f79bb8dae7161c68b26" }, + { + "path": "domains/E-02/gates.json", + "sha256": "fe1bbd285fd0d6fd6116ea8cd4ff9196514be84a64e8891a30fa063d0283eff7" + }, { "path": "domains/E-02/module_role_projection.json", "sha256": "e21465cf806fb080d75f89526f9c142010c392ee87454a5a19248b9a80eb7c28" @@ -848,6 +860,10 @@ "path": "domains/E-02/structure_types.json", "sha256": "e470dd3c96f2f9b8bfa6c2c97b05259281005a84b6eea24c38d322abdf16ad8e" }, + { + "path": "domains/E-03/gates.json", + "sha256": "c1c7fb4b8685592a1bf1e09e78050944ccbafe25ec04164f6f1685b2a2e67820" + }, { "path": "domains/E-03/module_role_projection.json", "sha256": "f693146dfb3ba2ac5fa6a6221c6b2dc7e32084acb06823b1f46f423a56546704" @@ -856,6 +872,10 @@ "path": "domains/E-03/structure_types.json", "sha256": "70c579a03c41226e6383fe7b4aa5e60f4b0f5a723db30f6292dd2a0bde4d17bb" }, + { + "path": "domains/E-04/gates.json", + "sha256": "190bd66c8d1df9d547879fb302b104fddca309f9983ee512932b9668cca9afaa" + }, { "path": "domains/E-04/module_role_projection.json", "sha256": "4c4bd473a12ad1dfcc8c9392503cd057017385a21287a89cb7ea147f3cf7d2d6" @@ -864,6 +884,10 @@ "path": "domains/E-04/structure_types.json", "sha256": "e8b15e9e5a39a488bf9ef660d69c7bc2db832248eb33788ba2ae54a9b5200e73" }, + { + "path": "domains/E-05/gates.json", + "sha256": "118d3b69eeb89b961a8a0bcdb1e011addbec8d24d5279faf0edfe39c907617f8" + }, { "path": "domains/E-05/module_role_projection.json", "sha256": "7fabfa3893a138dccb4e5bc797444f5f999c0e51ec6b0c8fbe0211eff3f03a6c" @@ -872,6 +896,10 @@ "path": "domains/E-05/structure_types.json", "sha256": "22707fbce849991c099a08885cac929d1ba842aa896945f9921c3214e4c701d0" }, + { + "path": "domains/E-06/gates.json", + "sha256": "1f847a976bbcc37e37ed0daf5d141f7d611ed10823b7874e6ad7e6c7ce746926" + }, { "path": "domains/E-06/module_role_projection.json", "sha256": "805b73b2b15cb720b62cd83455de85a67bda434255b69d588eb98f2527f88b71" @@ -880,6 +908,10 @@ "path": "domains/E-06/structure_types.json", "sha256": "a6520f72813caeaf72d62fd63eca10a1f46fd4fb3ffd97f97f222f37c53e8663" }, + { + "path": "domains/E-07/gates.json", + "sha256": "9d6ed4dba2cf63de8688b4c047d01771bbb2d8cdab1674b9ea584ed3d570df32" + }, { "path": "domains/E-07/module_role_projection.json", "sha256": "9920ed415f9768beb517100e97fa7d2eb862ca90ca83f802ea460f8f6bbf2445" @@ -888,6 +920,10 @@ "path": "domains/E-07/structure_types.json", "sha256": "fd4946e24cacc3bcc74c7fee5ce7fca99d3e5f2339fe3a9d698c54e2626a6c01" }, + { + "path": "domains/E-08/gates.json", + "sha256": "b567e5a1862adbce09420dca71ee7cc32f8771f3efb8f2428f6110d3d396b488" + }, { "path": "domains/E-08/module_role_projection.json", "sha256": "4a8c284b5d3bbf8ca224e9f08398e6f563b00cec6de72f5128acc4cd8425eced" @@ -896,6 +932,10 @@ "path": "domains/E-08/structure_types.json", "sha256": "a7ee6782c6541d0c6f6f6107a1d3615c2e7003a979741f2afc9d5d686a59e51d" }, + { + "path": "domains/E-09/gates.json", + "sha256": "8570e2704bdb75225bdb8391a89525a0d121b828d7fecafdc8ebb72c7754e6bf" + }, { "path": "domains/E-09/module_role_projection.json", "sha256": "ab93656808a832cfd73a6d75ed28d75e50e6cc66c67d59d765fcb8bbf638205f" @@ -904,6 +944,10 @@ "path": "domains/E-09/structure_types.json", "sha256": "c374b8b912679cc765b152c58ecd8320898f947b799e44ca509aa622669d7f4c" }, + { + "path": "domains/E-10/gates.json", + "sha256": "31bf82ccc8b543a6e0a4a17275f18a42900bec6161cce41dbdf88100659773cf" + }, { "path": "domains/E-10/module_role_projection.json", "sha256": "80f760d9d8516cc81ebe503a2967418c43e11f8b8410231081111483dfa6477d" @@ -912,6 +956,10 @@ "path": "domains/E-10/structure_types.json", "sha256": "07be97d40e422e14e570574c4e1da875307fe487c39e2ba9862ffd4b1623b748" }, + { + "path": "domains/E-11/gates.json", + "sha256": "a63a84e674e8b0f5919304000dde92f4786fe2fc3eab1214cc4f1b9fbc93fc09" + }, { "path": "domains/E-11/module_role_projection.json", "sha256": "0a55ce91b1845d64d085710110aec22bfcea66967ab5fce3a4acf5179faf65f1" @@ -920,6 +968,10 @@ "path": "domains/E-11/structure_types.json", "sha256": "6e565bab389f59866a0eafa8b7b7ff89f90ef2363ced3d9a4240100d3b3c3b90" }, + { + "path": "domains/E-12/gates.json", + "sha256": "b3013940aa8d496272378f76ddab7f3695f1bbc68a9773dfebf7a567ba693c42" + }, { "path": "domains/E-12/module_role_projection.json", "sha256": "207ef7d4a1300c12aa54cb1defa1110a6a9d18850d0fc4e144baebe6ff2b3c27" @@ -928,6 +980,10 @@ "path": "domains/E-12/structure_types.json", "sha256": "ef97f4b0ed05637165bfb211fc9be0cb3bcf0523e6b5a645c007e8dc2825bea9" }, + { + "path": "domains/E-13/gates.json", + "sha256": "3a9d36fe9c8de15853c6b9ff8d342f6e3d3b30431a4e5fdddb3bf34b13cd9aa9" + }, { "path": "domains/E-13/module_role_projection.json", "sha256": "412df6800be576d7f789bc7fc2045091eb9c49fd346e442ba42d6f29300f7313" @@ -936,6 +992,10 @@ "path": "domains/E-13/structure_types.json", "sha256": "cc18073ca443e98a34e139c08d11121ceb7d4ecd366ad3b3d5cb65ddb53e36f7" }, + { + "path": "domains/E-14/gates.json", + "sha256": "36a08d9c88a6878fe0474b59411e8496a4b9a5674edcaf5e5f290cf75c047934" + }, { "path": "domains/E-14/module_role_projection.json", "sha256": "cead5634615b6b417390b37a948a050ea89cba37485bb79617bd1296c06829a5" @@ -944,6 +1004,10 @@ "path": "domains/E-14/structure_types.json", "sha256": "5b0025a720a7b9f1ad7c74ce8d79f0969b282690abc0e3171e28dbf41933153b" }, + { + "path": "domains/E-15/gates.json", + "sha256": "059c800b75b7afa8836a9eeb1352fe99116d216f858374eb1cc847ec0c298eae" + }, { "path": "domains/E-15/module_role_projection.json", "sha256": "873f921bbbc86fc07a0c1ecfcbcacee67b1d9592b2efd7319290a10e8c1ba018" @@ -952,6 +1016,10 @@ "path": "domains/E-15/structure_types.json", "sha256": "784ceabd17c6de6d95a30f52dd5fd4036946fbedc556f2bad536225057a0f5fe" }, + { + "path": "domains/E-16/gates.json", + "sha256": "eba2368cb418aa574b84b6e088ff73e60869802385505996b731cdeaeb4bc7bc" + }, { "path": "domains/E-16/module_role_projection.json", "sha256": "24ad172b32d4942be6f4031c582f928d3c6feb6e442027e3ee02700519744b11" @@ -960,6 +1028,10 @@ "path": "domains/E-16/structure_types.json", "sha256": "b0bb30602ec424d35359c1a676ccca2bb2eda4a676997753da44d1dbbcc1f0b3" }, + { + "path": "domains/E-17/gates.json", + "sha256": "b517109ee6df850682ada51c16a3c686680b72e50fe3e30b98486c84ca0cae8d" + }, { "path": "domains/E-17/module_role_projection.json", "sha256": "498e2320ad04a907680b8588ee1b1b9ee7487e131bece96a601979d60796f5a4" @@ -968,6 +1040,10 @@ "path": "domains/E-17/structure_types.json", "sha256": "d306dbdf583453d51f643eb8398920efb1bb5bfa82b3a600caa79f45f790b2eb" }, + { + "path": "domains/E-18/gates.json", + "sha256": "c28bd022f7a873f2c48c679ff005749f8128f0eae69a41bbfa320f8b474b6742" + }, { "path": "domains/E-18/module_role_projection.json", "sha256": "b0699a793cdc52e6f74e9df3254f403858e83080be9e402bf3c7dae3cd8ffd0e" @@ -976,6 +1052,10 @@ "path": "domains/E-18/structure_types.json", "sha256": "ce4eae3a54bfe7ac31f16b657cc0a891580e025f7bfecb1812c015e7fd0c2500" }, + { + "path": "domains/E-19/gates.json", + "sha256": "24b6ebcdf38678663890cc960877bf86124582531ede6ae751741fd3002c3704" + }, { "path": "domains/E-19/module_role_projection.json", "sha256": "00950a8c76d4367ccd6dc000476c810df33e49b07fce124545e350530b008b99" @@ -984,6 +1064,10 @@ "path": "domains/E-19/structure_types.json", "sha256": "58b9503558bcab3fb7ae76532659d54694208d813197e97bac148d50795feb22" }, + { + "path": "domains/E-20/gates.json", + "sha256": "40314fa600094cb73634d7bef81804df35e6e713b5b11bd5e300a84add75a00f" + }, { "path": "domains/E-20/module_role_projection.json", "sha256": "7aef9706b50161693005ee61fc576a1562929fdf20830a3187fdda2a94fae8e3" @@ -992,6 +1076,10 @@ "path": "domains/E-20/structure_types.json", "sha256": "6766546fd61d3f46f0d0b71bc3c41d5c63a525954b2568d8003c9f5e90234074" }, + { + "path": "domains/E-21/gates.json", + "sha256": "c3fc58291924b41cb7a22b97ffa5f4f573fab9ff39d3ffddd49587469f51cdd3" + }, { "path": "domains/E-21/module_role_projection.json", "sha256": "de6b0f29ad8100744a1a4fe8393a44a8a921d604b5244f832256450cb547722a" @@ -1000,6 +1088,10 @@ "path": "domains/E-21/structure_types.json", "sha256": "b027ff04f21cc4a86c8285e2cde6f8fd23e0ff3a3aeb3fa61a9bd10142814e36" }, + { + "path": "domains/X1/gates.json", + "sha256": "cd68a09a8e1c99fd2c0ee236bd7754664161d4cab5385cf83df347eaefea13ee" + }, { "path": "domains/X1/module_role_projection.json", "sha256": "1bceacd99f408384fec9547262017d8c857dbdb58ae906446c45ce8f1e322920" @@ -1008,6 +1100,10 @@ "path": "domains/X1/structure_types.json", "sha256": "3344a27beb548c7d0b083b6e442a8f70ca89ce02d22614fda83203d3ff53e7e9" }, + { + "path": "domains/X2/gates.json", + "sha256": "bf9d6fff3c80e0eb31359387bfd231137587a21f5429e5fc5e0e863ebe34c89e" + }, { "path": "domains/X2/module_role_projection.json", "sha256": "9282422d06035a78b592c110c197c1afa58e78294566eecd2666f0c61820cd5f" @@ -1016,6 +1112,10 @@ "path": "domains/X2/structure_types.json", "sha256": "f51c0404bab1c5f4506012460ba02faf2cee6c958d49ee353095366defacd2ec" }, + { + "path": "domains/X3/gates.json", + "sha256": "a3319e942736e35f399c25fdf8b00ef4ee35a44fcb669f7c1e350a2856eaeb98" + }, { "path": "domains/X3/module_role_projection.json", "sha256": "93d3709c7d01132b82f0445d18eab7e666ea8d7fe2afe9b308bf87af5477be10" @@ -1292,6 +1392,10 @@ "path": "platform/reference_catalogs/domain_ids.json", "sha256": "4740d98e6a31bd0e7d9f792189712a8af1d0a87ec0bbd181deba1e34edb08355" }, + { + "path": "platform/reference_catalogs/profile_ids.json", + "sha256": "84f3538229df73e2d7ca58eb32da3d9b38ab1037edb67d4ac43e31e51c20aea3" + }, { "path": "platform/router/s3_sg01_router_config.json", "sha256": "4913b119f5ee08f96b52e10bd1f8376326564f1ab4742909fd2d0f07ded4efc6" @@ -1320,9 +1424,21 @@ "path": "platform/schemas/domain_slice.schema.v2.json", "sha256": "212a405088e7cf7ba2c65528a1c716938c946df7fe3bae3256b613051ed31aa3" }, + { + "path": "platform/schemas/fact_exception_pack.schema.json", + "sha256": "4eba7e51ed46a99e3704bc2333169749f4a16935de26a8c8027c1cac98ea58cf" + }, + { + "path": "platform/schemas/fact_ledger_base.schema.json", + "sha256": "b3f0e79ecb4c2f720f3e07e89154aadbd2327e4129cc703569fb5635240d2fe8" + }, + { + "path": "platform/schemas/fact_ledger_candidate_bundle.schema.json", + "sha256": "4e481504fb795b2be510680a8fa88124f5763a8124462f7870be4125ed9a7730" + }, { "path": "platform/schemas/legal_effect_structures.schema.json", - "sha256": "e2879c7a6d7f163c0729e8b863c5ef79b1094482eace78e6b8e1c7d69ce201e3" + "sha256": "fc962e8ae39f9bede64ba017297eded6413689204a065e00c3b3bdca8f1854df" }, { "path": "platform/schemas/structure_seed_bundle.schema.json", @@ -1640,17 +1756,29 @@ "path": "stage1_runtime/domain_slice_compiler.txt", "sha256": "c865eb281e201c5400d7b0d922b8d70f1df1d7de2001e66e3274fc5550a72526" }, + { + "path": "stage1_runtime/fact_ledger_compiler.py", + "sha256": "f6c4b476f4974104cd57accd559dcd91b31c80391077846dc3920d443c59a996" + }, + { + "path": "stage1_runtime/fact_ledger_compiler.txt", + "sha256": "f6c4b476f4974104cd57accd559dcd91b31c80391077846dc3920d443c59a996" + }, + { + "path": "stage1_runtime/fact_ledger_policy.v1.json", + "sha256": "fea0cea48d862c6c76774d6fee8ef84401a1b9fd753e86ccca76df32259523f7" + }, { "path": "stage1_runtime/prompt_compiler.py", - "sha256": "6922aa5274cea35a98f927839c1db2ea631df467e499963dc35d456dbadac153" + "sha256": "bfd80e90577d1f8be715cd581f8280f422dd8ba7cb526030cd9eaf7cb8151e30" }, { "path": "stage1_runtime/prompt_compiler.txt", - "sha256": "6922aa5274cea35a98f927839c1db2ea631df467e499963dc35d456dbadac153" + "sha256": "bfd80e90577d1f8be715cd581f8280f422dd8ba7cb526030cd9eaf7cb8151e30" }, { "path": "stage1_runtime/prompt_composition_policy.json", - "sha256": "06dbc4de60ab30525c2d4172f201e0b692210d9ff0a6c621d5c1caeb403f4e74" + "sha256": "8d3f003ecc5e53eb7112115126e325b77292a27b0a542a10c8cecfe6c6bbfdea" }, { "path": "stage1_runtime/registry_loader.py", @@ -1684,6 +1812,10 @@ "path": "stage1_runtime/schema_subset_validator.txt", "sha256": "18d470ff8dc775ce4b7da490e1f503913b821f7d95ad3d22ad0b8061d18aa377" }, + { + "path": "stage1_runtime/seed_admission_policy.v1.json", + "sha256": "af048b2d1c9d01c7943658046dc3e8e9f3caee0901635dc344095982a7e05ba4" + }, { "path": "stage1_runtime/stage_a_context_builder.py", "sha256": "68d984f9d565034c73721dc97c5aab0f9cdb8a8b46e1bf13eabd8c136ec4bcc0" @@ -1730,6 +1862,6 @@ } ], "program_release_status": "STAGE1_NOT_RELEASE_READY", - "runtime_artifact_count": 432, + "runtime_artifact_count": 465, "schema_version": "stage1_runtime_manifest.v1" } diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/stage1_runtime/fact_ledger_compiler.py b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/stage1_runtime/fact_ledger_compiler.py new file mode 100644 index 00000000..641e4871 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/stage1_runtime/fact_ledger_compiler.py @@ -0,0 +1,910 @@ +#!/usr/bin/env python3 +"""Compile v.8 Fact Ledger candidate rows from BO, LES, signals, envelopes, and gate sidecars. + +순수 함수 모듈 — I/O·시각·난수 없음. 규칙 선언은 fact_ledger_policy.v1.json 이 유일 정본이며 +이 모듈은 그것을 읽어 dispatch 만 한다. gate_id·27필드·registry type_id 리터럴을 코드에 두지 +않는다(정적 검사 11·12). 사건 종류 이름을 어디에도 두지 않는다(137종 원칙). + +공개 API + normalize_gate_rules(sidecar_doc, policy) 17축 방언 정규화 리더 + resolve_alias_table(alias_declarations, sidecars, policy) + compile_fact_ledger_candidates(...) 조립 본체 (아래 시그니처) +""" +from __future__ import annotations + +import json +import re +from typing import Any + +_WS_RE = re.compile(r"\s+") +_CODE_RE = re.compile(r"^[A-Z][A-Z0-9_]{2,127}$") +_DOMAIN_RE = re.compile(r"^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$") + + +# --------------------------------------------------------------------------- +# 공용 헬퍼 +# --------------------------------------------------------------------------- + +def _dict(value: Any) -> dict: + return value if isinstance(value, dict) else {} + + +def _list(value: Any) -> list: + return value if isinstance(value, list) else [] + + +def _strings(value: Any) -> list[str]: + return [item for item in _list(value) if isinstance(item, str)] + + +def _nonblank(value: Any) -> bool: + return value not in (None, "", [], {}) + + +def _text(value: Any, limit: int) -> str | None: + """키 오름차순 JSON -> 공백열 단일 공백 -> limit 절단 -> 빈 문자열이면 None.""" + if value is None: + return None + if isinstance(value, str): + rendered = value + else: + rendered = json.dumps(value, ensure_ascii=False, sort_keys=True) + rendered = _WS_RE.sub(" ", rendered).strip() + if not rendered: + return None + return rendered[:limit] + + +def _first_value(*values: Any) -> Any: + for value in values: + if _nonblank(value): + return value + return None + + +def _first_key(record: dict, keys: list[str]) -> Any: + for key in keys: + if _nonblank(record.get(key)): + return record[key] + return None + + +def _dedupe_keep_order(items: list[str]) -> list[str]: + seen: set[str] = set() + out: list[str] = [] + for item in items: + if item not in seen: + seen.add(item) + out.append(item) + return out + + +# --------------------------------------------------------------------------- +# F0-0c — BO 압축·정규화 (구본 _compact_bo 등가 계층. (가) 9필드 전부의 전제) +# --------------------------------------------------------------------------- + +def compact_bo(bo: dict, policy: dict) -> dict: + norm = _dict(policy.get("bo_normalization")) + key_map = _dict(norm.get("key_map")) + keep = _strings(norm.get("domain_payload_keep_keys")) + out: dict[str, Any] = {} + for key, value in bo.items(): + out[key_map.get(key, key)] = value + payload = _dict(_dict(bo.get("extensions")).get("domain_payload")) + out["domain_payload"] = {k: payload[k] for k in keep + if k in payload and _nonblank(payload[k])} + out["core"] = _dict(bo.get("core_field_base")) + return out + + +# --------------------------------------------------------------------------- +# (다) — 증거력 산정 (정책 evidence_scoring_da_2 의 5갈래 · 순서 그대로) +# --------------------------------------------------------------------------- + +def _flatten_evidence(document: Any, depth: int = 3) -> list[dict]: + rows: list[dict] = [] + + def visit(node: Any, level: int) -> None: + if level > depth: + return + if isinstance(node, dict): + if any(k in node for k in ("evidence_index", "id", "evidence_id", + "eid", "index", "doc_id")): + rows.append(node) + return + for value in node.values(): + visit(value, level + 1) + elif isinstance(node, list): + for item in node: + visit(item, level + 1) + + visit(document, 0) + return rows + + +def _evidence_id(record: dict) -> str | None: + value = _first_key(record, ["evidence_index", "id", "evidence_id", + "eid", "index", "doc_id"]) + return value if isinstance(value, str) else None + + +def _excerpt_from_indexed(record: dict) -> str: + facts = _strings(record.get("key_facts")) + if facts: + return " ".join(facts) + parts = _strings(record.get("key_dates")) + _strings(record.get("key_amounts")) + if parts: + return " ".join(parts) + for value in record.values(): + if isinstance(value, str) and value.strip(): + return value + return "" + + +def build_evidence_authority(evidence_document: Any) -> dict[str, dict]: + authority: dict[str, dict] = {} + for record in _flatten_evidence(evidence_document): + eid = _evidence_id(record) + if not eid: + continue + authority[eid] = { + "evidence_index": eid, + "title": _first_key(record, ["title", "Title", "name", "doc_title"]), + "excerpt": _excerpt_from_indexed(record), + "priority": _first_key(record, ["priority", "Priority", "weight"]), + "authentication": _first_key(record, ["authentication", "auth", + "source_authentication"]), + "source": "evidence_indexed.json", + } + return authority + + +def score_evidence(evidence_refs: list[str], authority: dict[str, dict], + policy: dict, reviews: list[dict], + fact_id: str) -> tuple[str, str, int]: + """(credibility, proof_strength, 절단여유 최소값) — 정책 5갈래 순서 그대로.""" + scoring = _dict(policy.get("evidence_scoring_da_2")) + markers = _strings(scoring.get("markers_documentary")) + rows: list[dict] = [] + for ref in evidence_refs: + row = authority.get(ref) + if row is None: + # 우주 밖 참조 — stub 도 비어 있지 않은 증거 행으로 계수된다(정책 승계). + reviews.append({"review_code": "EVIDENCE_REF_NOT_IN_EVIDENCE_UNIVERSE", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + row = {"evidence_index": ref, "missing": True} + rows.append(row) + if not rows: + return "low", "low", -1 + pieces: list[str] = [] + slack = 10 ** 9 + for row in rows: + rendered = _WS_RE.sub(" ", json.dumps(row, ensure_ascii=False, + sort_keys=True)).strip() + slack = min(slack, 300 - len(rendered)) + pieces.append(rendered[:300]) + text = " ".join(pieces) + if any(marker in text for marker in markers): + return "high", "documentary", slack + if len(rows) >= 2: + return "high", "high", slack + if ("meeting" in text) or ("회의" in text) or ("면담" in text): + return "medium", "meeting_only_core", slack + return "medium", "medium", slack + + +# --------------------------------------------------------------------------- +# F0-0 — 17축 사이드카 방언 정규화 리더 +# --------------------------------------------------------------------------- + +def normalize_gate_rules(sidecar_doc: Any, policy: dict) -> dict: + """사이드카 한 파일을 정규화한다. {rules: {gate_id: norm}, file_meta, warnings}.""" + axes = _dict(_dict(policy.get("gate_execution")).get("normalization_axes_17")) + array_keys = _strings(axes.get("rule_array_keys")) or ["rules", "gates"] + code_keys = _strings(axes.get("emitted_code_keys")) + doc = _dict(sidecar_doc) + raw_rules: list[dict] = [] + array_key = None + for key in array_keys: + if isinstance(doc.get(key), list): + array_key = key + raw_rules = [r for r in doc[key] if isinstance(r, dict)] + break + warnings: list[dict] = [] + norm_rules: dict[str, dict] = {} + for rule in raw_rules: + gate_id = rule.get("gate_id") + if not isinstance(gate_id, str): + warnings.append({"code": "GATE_RULE_WITHOUT_ID", "detail": str(rule)[:120]}) + continue + writer = _dict(rule.get("writer_effect")) + row_eff = _dict(rule.get("row_effect")) + on_true = _dict(rule.get("on_true")) + condition = rule.get("condition") + cond_kind = "dict" if isinstance(condition, dict) else "str" + cond_key = next(iter(condition), None) if cond_kind == "dict" else None + cond_value = condition.get(cond_key) if cond_kind == "dict" else condition + emitted: list[str] = [] + for key in code_keys: + value = rule.get(key) + if isinstance(value, str): + emitted.append(value) + else: + emitted.extend(_strings(value)) + blocks = None + for container, key in (("writer_effect", "final_drafting_block"), + ("writer_effect", "blocks_final_drafting"), + ("on_true", "blocks_final_drafting")): + holder = writer if container == "writer_effect" else on_true + if key in holder: + blocks = bool(holder[key]) + break + status_fire = (writer.get("status_when_triggered") + or writer.get("status_on_fire") + or on_true.get("writer_status")) + evaluation = (rule.get("evaluation_mode") or rule.get("implementation_type") + or rule.get("evaluation")) + norm_rules[gate_id] = { + "gate_id": gate_id, + "condition_kind": cond_kind, + "condition_key": cond_key, + "condition_value": cond_value, + "emitted_codes": _dedupe_keep_order(emitted), + "blocks_final_drafting": blocks, + "status_when_triggered": status_fire, + "row_must_consider": bool(row_eff.get("must_consider")), + "preserve_seed": bool(on_true.get("preserve_seed")), + "unknown_or_missing_policy": rule.get("unknown_or_missing_policy"), + "evaluation": evaluation, + "writes_fact_ledger": writer.get("writes_fact_ledger"), + "silent_repair_forbidden": rule.get("silent_repair_forbidden"), + "source_membership_required": rule.get("source_membership_required"), + "input_declaration": rule.get("input_fields") or rule.get("input_contracts"), + } + if doc.get("global_status_rule") is not None: + warnings.append({"code": "SIDECAR_GLOBAL_STATUS_RULE_IGNORED", + "detail": str(doc.get("global_status_rule"))[:120]}) + return {"rules": norm_rules, + "file_meta": {"array_key": array_key, + "schema_version": doc.get("schema_version"), + "status_key": ("closure_status" if "closure_status" in doc + else ("status" if "status" in doc else None))}, + "warnings": warnings} + + +# --------------------------------------------------------------------------- +# F0-0b — 별칭 해소 (D-P4-7 — 전수 자기선언 대조 · 침묵 탈락 금지) +# --------------------------------------------------------------------------- + +def resolve_alias_table(alias_declarations: Any, structure_type_sidecars: dict, + policy: dict) -> dict: + root = _dict(alias_declarations) + root = _dict(root.get("extension_payload_key_declarations", root)) + table = {k: _strings(v) for k, v in _dict(root.get("legacy_alias_targets")).items()} + derived_mismatches = _list(root.get("legacy_declaration_mismatches")) + abbrev = _dict(_dict(policy.get("legacy_alias")).get("abbreviation_map")) + self_decl: dict[str, set] = {} + for domain_id, doc in sorted(_dict(structure_type_sidecars).items()): + doc = _dict(doc) + rows = None + for key in ("types", "structure_types", "records", "entries"): + if isinstance(doc.get(key), list): + rows = doc[key] + break + for row in rows or []: + if not isinstance(row, dict): + continue + value = row.get("legacy_source") or row.get("legacy_source_domain") + if not isinstance(value, str): + continue + head = value.split("_")[0] + if head not in abbrev: + continue # 'Part3 fallback' 등 비 B* 값은 선언이 아니다 + self_decl.setdefault(abbrev[head], set()).add(domain_id) + reviews: list[dict] = [] + mismatch_map: dict[str, dict] = {} + for legacy in sorted(set(table) | set(self_decl)): + declared = self_decl.get(legacy, set()) + aliased = set(table.get(legacy, [])) + missing = sorted(declared - aliased) + excess = sorted(aliased - declared) + if missing: + mismatch_map[legacy] = {"missing": missing, "excess": excess} + reviews.append({"review_code": "LEGACY_ALIAS_SELF_MISMATCH_REVIEW", + "severity": "SOFT_WARNING", "legacy": legacy, + "missing_domain_ids": missing, + "excess_domain_ids": excess}) + derived_missing = sorted({str(_dict(m).get("domain_id")) + for m in derived_mismatches if _dict(m).get("domain_id")}) + actual_missing = sorted({d for m in mismatch_map.values() for d in m["missing"]}) + if derived_missing != actual_missing: + reviews.append({"review_code": "LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", + "derived_list": derived_missing, + "actual_missing": actual_missing}) + return {"table": table, "self_declarations": {k: sorted(v) for k, v in self_decl.items()}, + "mismatches": mismatch_map, "reviews": reviews} + + +# --------------------------------------------------------------------------- +# 조립 본체 +# --------------------------------------------------------------------------- + +def compile_fact_ledger_candidates( + bo_document: Any, + les_document: Any, + signal_documents: dict, + domain_envelopes: dict, + p2_handoff_document: Any, + evidence_document: Any, + registry_index: Any, + domain_configs: dict, + gate_sidecars: dict, + structure_type_sidecars: dict, + activation_document: Any, + alias_declarations: Any, + policy: dict) -> dict: + reviews: list[dict] = [] + audit: dict[str, Any] = {} + + activation_root = _dict(activation_document) + activation_root = _dict(activation_root.get("domain_activation_manifest", + activation_root)) + expected_runnable = _strings(activation_root.get("expected_runnable_domain_ids")) + + # ---- F0-0 리더 · F0-0b 별칭 ---------------------------------------- + normalized: dict[str, dict] = {} + dialect_counts: dict[str, Any] = {"array_key": {}, "files": 0} + reader_warnings: list[dict] = [] + for domain_id, sidecar in sorted(_dict(gate_sidecars).items()): + result = normalize_gate_rules(sidecar, policy) + normalized[domain_id] = result + dialect_counts["files"] += 1 + ak = result["file_meta"]["array_key"] + dialect_counts["array_key"][ak] = dialect_counts["array_key"].get(ak, 0) + 1 + reader_warnings.extend(result["warnings"]) + audit["sidecar_dialects"] = dialect_counts + audit["reader_warnings"] = reader_warnings + + alias = resolve_alias_table(alias_declarations, structure_type_sidecars, policy) + reviews.extend(alias["reviews"]) + + # ---- LES 적재 ------------------------------------------------------- + les_root = _dict(les_document) + structure_records = [r for r in _list(les_root.get("structure_records")) + if isinstance(r, dict)] + record_by_id = {r.get("structure_id"): r for r in structure_records + if isinstance(r.get("structure_id"), str)} + index = _dict(les_root.get("structure_index")) + by_bo_id = {k: _strings(v) for k, v in _dict(index.get("by_bo_id")).items()} + + # ---- SG 적재 -------------------------------------------------------- + def signal_records(sg: str) -> list[dict]: + doc = _dict(_dict(signal_documents).get(sg)) + return [r for r in _list(doc.get("records")) if isinstance(r, dict)] + + sg11_records = signal_records("SG-11") + sg11_by_signal_id = {r.get("signal_id"): r for r in sg11_records + if isinstance(r.get("signal_id"), str)} + sg04_by_domain: dict[str, list[str]] = {} + for record in signal_records("SG-04"): + for domain_id in _strings(record.get("domain_ids")): + if isinstance(record.get("signal_id"), str): + sg04_by_domain.setdefault(domain_id, []).append(record["signal_id"]) + + # ---- F0-7b — 게이트 입력 정규화 ------------------------------------ + handoff_root = _dict(p2_handoff_document) + handoff_root = _dict(handoff_root.get("stage1_part2_review_handoff", handoff_root)) + handoff_items = [r for r in _list(handoff_root.get("review_items")) + if isinstance(r, dict)] + p2_rules = _dict(policy.get("p2_handoff_rules")) + code_pool: dict[str, list[str]] = {} + gate_input_counts = {"envelope_review_items": 0, "handoff_codes": 0, + "handoff_not_a_code": 0, "handoff_domain_unresolved": 0, + "slot_entries": 0, "operand_states": 0} + for domain_id in expected_runnable: + env = _dict(_dict(domain_envelopes).get(domain_id)) + env = _dict(env.get("domain_signal_envelope", env)) + for item in _list(env.get("review_items")): + code = _dict(item).get("review_code") or _dict(item).get("code") + if isinstance(code, str) and _CODE_RE.match(code): + code_pool.setdefault(domain_id, []).append(code) + gate_input_counts["envelope_review_items"] += 1 + for item in handoff_items: + raw_code = item.get("source_review_code") + if not (isinstance(raw_code, str) and _CODE_RE.match(raw_code)): + if raw_code is not None: + gate_input_counts["handoff_not_a_code"] += 1 + reviews.append({"review_code": "P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW", + "severity": "SOFT_WARNING", + "detail": str(raw_code)[:80]}) + continue + source_domain = item.get("source_domain") + targets: list[str] = [] + if isinstance(source_domain, str): + if _DOMAIN_RE.match(source_domain): + targets = [source_domain] + elif source_domain in alias["table"]: + targets = alias["table"][source_domain] + if not targets: + gate_input_counts["handoff_domain_unresolved"] += 1 + reviews.append({"review_code": "P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW", + "severity": "SOFT_WARNING", + "detail": str(source_domain)[:80]}) + continue + for domain_id in targets: + code_pool.setdefault(domain_id, []).append(raw_code) + gate_input_counts["handoff_codes"] += 1 + slot_status: dict[str, list[dict]] = {} + for domain_id in expected_runnable: + env = _dict(_dict(domain_envelopes).get(domain_id)) + env = _dict(env.get("domain_signal_envelope", env)) + entries = [e for e in _list(env.get("evidence_slot_status")) + if isinstance(e, dict)] + if entries: + slot_status[domain_id] = entries + gate_input_counts["slot_entries"] += len(entries) + operand_by_domain: dict[str, list[str]] = {} + for record in sg11_records: + details = _dict(record.get("details")) + state = record.get("operand_state") or details.get("operand_state") + if not isinstance(state, str): + continue + for domain_id in (_strings(record.get("domain_ids")) + or _strings(details.get("domain_ids"))): + operand_by_domain.setdefault(domain_id, []).append(state) + gate_input_counts["operand_states"] += 1 + audit["gate_input_counts"] = gate_input_counts + + # ---- F0-1 ~ F0-6b — 행 생성 ---------------------------------------- + bo_records = [b for b in _list(bo_document) if isinstance(b, dict)] + bo_sorted = sorted(bo_records, key=lambda b: str(b.get("BO_ID") or "")) + authority = build_evidence_authority(evidence_document) + calc_types = set(_strings(policy.get("legal_calculation_object_types"))) + na_defaults = _dict(policy.get("field_synthesis_na_15")) + rows: list[dict] = [] + attach_stats = {"attached_bo": 0, "unattached_bo": 0} + used_structure_ids: set[str] = set() + used_sg11: set[str] = set() + slack_min = 10 ** 9 + for idx, bo in enumerate(bo_sorted, 1): + compact = compact_bo(bo, policy) + core = compact["core"] + fact_id = "F-%03d" % idx + bo_id = str(bo.get("BO_ID") or "") + # (가) 9필드 + parties = [] + if _nonblank(core.get("Performer")): + parties.append({"role": "actor", "value": _text(core.get("Performer"), 160), + "source": "BO.core_field_base"}) + if _nonblank(core.get("Subject")): + parties.append({"role": "counterparty", + "value": _text(core.get("Subject"), 160), + "source": "BO.core_field_base"}) + amount = _text(_first_value(compact.get("amount"), core.get("Amount")), 240) + action = _first_value(compact.get("action"), core.get("Action")) + evidence_refs = _strings(bo.get("source_evidence_indexes")) + # F0-3 — LES 조인 + structure_ids = sorted(set(by_bo_id.get(bo_id, []))) + attached = [record_by_id[s] for s in structure_ids if s in record_by_id] + if attached: + attach_stats["attached_bo"] += 1 + else: + attach_stats["unattached_bo"] += 1 + reviews.append({"review_code": "FACT_ROW_UNATTACHED_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": bo_id}) + used_structure_ids.update(s for s in structure_ids if s in record_by_id) + domain_effects: dict[str, dict] = {} + for record in attached: + domain_id = record.get("domain_id") + if not isinstance(domain_id, str): + continue + slot = domain_effects.setdefault(domain_id, { + "module": set(), "role_projection": set(), "structure_ids": set(), + "type_ids": set(), "review_codes": set()}) + if isinstance(record.get("module"), str): + slot["module"].add(record["module"]) + if isinstance(record.get("role_projection"), str): + slot["role_projection"].add(record["role_projection"]) + if isinstance(record.get("structure_id"), str): + slot["structure_ids"].add(record["structure_id"]) + if isinstance(record.get("type_id"), str): + slot["type_ids"].add(record["type_id"]) + slot["review_codes"].update( + c for c in _strings(record.get("review_codes")) if _CODE_RE.match(c)) + domain_effects = {d: {k: sorted(v) for k, v in slot.items()} + for d, slot in sorted(domain_effects.items())} + modules = sorted({m for slot in domain_effects.values() for m in slot["module"]}) + roles = sorted({r for slot in domain_effects.values() + for r in slot["role_projection"]}) + type_ids = sorted({t for slot in domain_effects.values() + for t in slot["type_ids"]}) + # F0-5 — SG-11 조인 + operand_state 승계 (재산정 금지) + calculation_requests: list[dict] = [] + route_refs: list[str] = [] + for record in attached: + for ref in _strings(record.get("calculation_request_refs")): + route_refs.append(ref) + sg = sg11_by_signal_id.get(ref) + if sg is None: + calculation_requests.append({"calculation_id": None, + "request_id": ref, + "provenance": {"signal_id": ref, + "dangling": True}}) + reviews.append({"review_code": "CALCULATION_REF_DANGLING_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + continue + used_sg11.add(ref) + details = _dict(sg.get("details")) + calc_id = details.get("calculation_id") + if not isinstance(calc_id, str) or not calc_id: + calc_id = ref + reviews.append({"review_code": "CALCULATION_ID_SUBSTITUTED_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + law_refs = sorted(set( + _strings(sg.get("law_version_refs")) + + _strings(details.get("law_version_refs")) + + [ref2 for d in _strings(sg.get("domain_ids")) + for ref2 in sg04_by_domain.get(d, [])])) + calculation_requests.append({ + "calculation_id": calc_id, + "request_id": ref, + "operand_state": (sg.get("operand_state") + or details.get("operand_state")), + "missing_operands": _strings(details.get("missing_operands")), + "law_version_refs": law_refs, + "provenance": {"signal_id": ref, + "structure_ids": sorted( + s for s in structure_ids + if ref in _strings( + _dict(record_by_id.get(s)).get( + "calculation_request_refs")))}, + }) + # (다) 증거력 + credibility, proof_strength, slack = score_evidence( + evidence_refs, authority, policy, reviews, fact_id) + slack_min = min(slack_min, slack) + # (나) 15필드 + object_spec = _text(core.get("Object"), 160) + calc_obj_candidates = sorted(calc_types & set(type_ids)) + legal_calculation_object = calc_obj_candidates[0] if calc_obj_candidates else None + alias_vocab = _dict(policy.get("alias_module_vocabulary")) + mod_actio = alias_vocab.get("actio_fields") + mod_money = alias_vocab.get("money_claim_effect") + mod_successor = alias_vocab.get("commercial_successor_effect") + actio_ids = sorted({s for slot in domain_effects.values() + for s in slot["structure_ids"] + if mod_actio in slot["module"]}) + actio_roles = sorted({r for slot in domain_effects.values() + if mod_actio in slot["module"] + for r in slot["role_projection"]}) + money_ids = sorted({s for slot in domain_effects.values() + if mod_money in slot["module"] + for s in slot["structure_ids"]}) + successor_ids = sorted({s for slot in domain_effects.values() + if mod_successor in slot["module"] + for s in slot["structure_ids"]}) + row = { + "fact_id": fact_id, + "source_bo_id": bo_id, + "type": compact.get("bo_type") if isinstance(compact.get("bo_type"), str) else None, + "date": _first_value(core.get("BehaviorTime"), compact.get("date")), + "parties": parties, + "object_spec": object_spec, + "amount": amount, + "action": action if isinstance(action, str) else _text(action, 240), + "evidence_refs": evidence_refs, + "credibility": credibility, + "legal_centrality": "high" if attached else "medium", + "proof_strength": proof_strength, + "legal_effect_roles": roles, + "claim_chain_ref": {"claim_group_ids": [], "liability_group_ids": [], + "signal_count": len(route_refs)}, + "linked_structures": {"structure_ids": structure_ids}, + "must_not_drop_in_claim_types": modules, + "must_consider": bool(attached), + "state_context": {"bo_type": compact.get("bo_type") + if isinstance(compact.get("bo_type"), str) else None, + "domain_ids": sorted(domain_effects)}, + "money_claim_effect": {"enabled": bool(money_ids), + "structure_ids": money_ids}, + "commercial_successor_effect": {"enabled": bool(successor_ids), + "structure_ids": successor_ids}, + "actio_roles": actio_roles, + "linked_actio_structures": actio_ids, + "downstream_module_candidates": modules, + "skeleton_validation_codes": [], + "derived_fact": action if isinstance(action, str) else _text(action, 240), + "derivation_basis": ("structure_ids=" + ",".join(structure_ids) + if structure_ids else "bo_only"), + "legal_calculation_object": legal_calculation_object, + "domain_effects": domain_effects, + "calculation_requests": calculation_requests, + } + rows.append(row) + audit["evidence_truncation_slack_min"] = (None if slack_min == 10 ** 9 else slack_min) + + # ---- F0-8 — 게이트 합집합 실행 (§4.4 그대로) ------------------------ + gate_exec = _dict(policy.get("gate_execution")) + implemented = set(_strings(gate_exec.get("implemented_condition_keys"))) + default_missing_policy = str(gate_exec.get("unknown_or_missing_policy_default") + or "fail_open_to_review") + declared_gates: dict[str, dict] = {} + for domain_id in expected_runnable: + config = _dict(_dict(domain_configs).get(domain_id)) + config_root = _dict(config.get("domain_config", config)) + for gate in _list(config_root.get("gates")): + gate = _dict(gate) + gate_id = gate.get("gate_id") + if isinstance(gate_id, str): + declared_gates[gate_id] = {"domain_id": domain_id, "declared": gate} + all_rules: dict[str, dict] = {} + for domain_id, result in normalized.items(): + for gate_id, rule in result["rules"].items(): + all_rules[gate_id] = {"domain_id": domain_id, "rule": rule} + gate_report: list[dict] = [] + fired_by_domain: dict[str, list[dict]] = {} + for gate_id in sorted(declared_gates): + info = declared_gates[gate_id] + domain_id = info["domain_id"] + declared = info["declared"] + registry_codes = [c for c in _strings(declared.get("review_codes")) + if _CODE_RE.match(c)] + entry: dict[str, Any] = {"gate_id": gate_id, "domain_id": domain_id, + "fired": False, "input_state": "n/a"} + rule_info = all_rules.get(gate_id) + if rule_info is None: + entry["disposition"] = "GATE_SPEC_NOT_DEPLOYED_REVIEW" + reviews.append({"review_code": "GATE_SPEC_NOT_DEPLOYED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + gate_report.append(entry) + continue + rule = rule_info["rule"] + fired = False + if rule["condition_kind"] == "dict": + key = rule["condition_key"] + if key in implemented: + entry["disposition"] = "implemented" + value = rule["condition_value"] + if key == "any_review_code_present": + pool = code_pool.get(domain_id) + if pool is None and domain_id not in _dict(domain_envelopes): + entry["input_state"] = "input_missing" + entry["missing_policy"] = ( + rule["unknown_or_missing_policy"] or default_missing_policy) + else: + entry["input_state"] = "supplied" + fired = bool(set(_strings(value)) & set(pool or [])) + elif key == "required_slots_missing_or_conflicting": + entries = slot_status.get(domain_id) + if entries is None and domain_id not in _dict(domain_envelopes): + entry["input_state"] = "input_missing" + entry["missing_policy"] = ( + rule["unknown_or_missing_policy"] or default_missing_policy) + else: + entry["input_state"] = "supplied" + wanted = set(_strings(value)) + fired = any( + (e.get("slot_id") in wanted) + and (e.get("status") in ("missing", "conflicted")) + for e in entries or []) + elif key == "calculation_operand_state_in": + states = operand_by_domain.get(domain_id) + entry["input_state"] = ("supplied" if states else "input_missing") + fired = bool(set(_strings(value)) & set(states or [])) + else: + entry["disposition"] = "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW" + entry["condition_key"] = key + reviews.append({"review_code": "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": str(key)}) + else: + entry["disposition"] = "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW" + entry["predicate_spec"] = _text(rule["condition_value"], 200) + reviews.append({"review_code": "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": _text(rule["condition_value"], 160)}) + # 차단 표지 대조 — registry ↔ 정규화된 사이드카. + declared_blocks = declared.get("blocks_final_drafting") + if (rule["blocks_final_drafting"] is not None + and declared_blocks is not None + and bool(declared_blocks) != rule["blocks_final_drafting"]): + reviews.append({"review_code": "GATE_BLOCK_FLAG_MISMATCH_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + entry["block_flag_mismatch"] = True + emitted = _dedupe_keep_order(rule["emitted_codes"] + registry_codes) + excess = [c for c in rule["emitted_codes"] if c not in registry_codes] + if excess: + entry["codes_not_in_registry"] = excess + reviews.append({"review_code": "GATE_EMITTED_CODE_NOT_IN_REGISTRY", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": ",".join(excess)[:160]}) + entry["fired"] = fired + entry["blocks_final_drafting"] = (rule["blocks_final_drafting"] + if rule["blocks_final_drafting"] is not None + else bool(declared_blocks)) + if fired: + projection_rows = [r["fact_id"] for r in rows + if domain_id in r["domain_effects"]] + entry["projected_row_count"] = len(projection_rows) + fired_by_domain.setdefault(domain_id, []).append({ + "gate_id": gate_id, "codes": emitted, + "row_must_consider": rule["row_must_consider"], + "preserve_seed": rule["preserve_seed"], + "blocks_final_drafting": entry["blocks_final_drafting"], + "status_when_triggered": rule["status_when_triggered"], + "projected_rows": projection_rows, + }) + gate_report.append(entry) + orphan_rules = sorted(set(all_rules) - set(declared_gates)) + for gate_id in orphan_rules: + reviews.append({"review_code": "GATE_RULE_NOT_IN_REGISTRY", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + + # ---- 행 투영 3분기 + F0-8b 스켈레톤 -------------------------------- + skeleton_order = _strings(_dict(policy.get("skeleton_checks")).get("order")) + for row in rows: + codes: list[str] = [] + if not _nonblank(row["derived_fact"]): + codes.append(skeleton_order[0]) + if not row["evidence_refs"]: + codes.append(skeleton_order[1]) + if not row["linked_structures"]["structure_ids"]: + codes.append(skeleton_order[2]) + gate_codes: list[str] = [] + for domain_id in sorted(row["domain_effects"]): + for firing in sorted(fired_by_domain.get(domain_id, []), + key=lambda f: f["gate_id"]): + gate_codes.extend(firing["codes"]) + if firing["row_must_consider"]: + row["must_consider"] = row["must_consider"] or True + row["skeleton_validation_codes"] = _dedupe_keep_order(codes + gate_codes) + + # ---- STRUCTURE_UNATTACHED_REVIEW ----------------------------------- + unattached_structures = sorted(set(record_by_id) - used_structure_ids) + for structure_id in unattached_structures: + reviews.append({"review_code": "STRUCTURE_UNATTACHED_REVIEW", + "severity": "SOFT_WARNING", "detail": structure_id}) + + # ---- F0-9 — 예외 pack ---------------------------------------------- + pack_policy = _dict(policy.get("exception_pack")) + char_limit = int(pack_policy.get("pack_item_char_limit") or 2000) + exceptions: list[dict] = [] + for row in rows: + if row["legal_calculation_object"] is None: + continue + bo = next((b for b in bo_sorted + if str(b.get("BO_ID") or "") == row["source_bo_id"]), {}) + core = _dict(bo.get("core_field_base")) + amount_candidates = [v for v in (bo.get("amount"), core.get("Amount")) + if _nonblank(v)] + if len(amount_candidates) >= 2: + payload = {"amount_candidates": [_text(v, 240) for v in amount_candidates], + "object_spec": row["object_spec"], + "calculation_request_ids": sorted( + str(c.get("calculation_id")) for c in + row["calculation_requests"] if c.get("calculation_id"))} + item = {"exception_id": "FX-%03d" % (len(exceptions) + 1), + "exception_type": "legal_calculation_required", + "fact_id": row["fact_id"], + "source_bo_id": row["source_bo_id"], + "conflict_flags": ["CALCULATION_BASIS_CONFLICT"], + "compact_payload": payload} + rendered = json.dumps(item, ensure_ascii=False) + if len(rendered) > char_limit: + item["oversize_review"] = True + reviews.append({"review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", + "fact_id": row["fact_id"], + "detail": "pack item oversize %d>%d" % (len(rendered), + char_limit)}) + exceptions.append(item) + elif (not _nonblank(row["amount"])) or (not _nonblank(row["object_spec"])): + reviews.append({"review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", "fact_id": row["fact_id"], + "detail": "amount_or_object_spec_blank"}) + unreachable = {name for name, meta in + _dict(policy.get("f1_exception_types")).items() + if _dict(meta).get("unreachable_in_v8")} + for item in exceptions: + if item["exception_type"] in unreachable: + raise ValueError(json.dumps({ + "reason_code": "PART4_UNREACHABLE_EXCEPTION_TYPE", + "exception_type": item["exception_type"]}, ensure_ascii=False)) + + # ---- F0-10 — 보존 등식 계수 ---------------------------------------- + effect_left = sum(len(r["domain_effects"]) for r in rows) + effect_right = 0 + calc_right = 0 + for bo in bo_sorted: + bo_id = str(bo.get("BO_ID") or "") + attached = [record_by_id[s] for s in by_bo_id.get(bo_id, []) + if s in record_by_id] + effect_right += len({r.get("domain_id") for r in attached + if isinstance(r.get("domain_id"), str)}) + calc_right += len({ref for r in attached + for ref in _strings(r.get("calculation_request_refs"))}) + calc_left = sum(len(r["calculation_requests"]) for r in rows) + sg11_total = len(sg11_records) + sg11_unused = sg11_total - len(used_sg11) + equations = { + "row": {"left": len(rows), "right": len(bo_sorted), + "holds": len(rows) == len(bo_sorted)}, + "attachment_coverage": { + "attached": attach_stats["attached_bo"], + "unattached": attach_stats["unattached_bo"], + "total": len(bo_sorted), + "holds": (attach_stats["attached_bo"] + attach_stats["unattached_bo"] + == len(bo_sorted))}, + "effect": {"left": effect_left, "right": effect_right, + "holds": effect_left == effect_right}, + "calc_row": {"left": calc_left, "right": calc_right, + "holds": calc_left == calc_right}, + "calc_source": {"attached_distinct": len(used_sg11), + "unattached_distinct": sg11_unused, + "total": sg11_total, + "holds": len(used_sg11) + sg11_unused == sg11_total}, + } + + # ---- 상태·집계 ------------------------------------------------------ + les_review_codes = set(_strings(policy.get("les_inherited_review_codes"))) + les_review_present = any( + (set(slot["review_codes"]) & les_review_codes) + for row in rows for slot in row["domain_effects"].values()) or any( + _dict(r).get("review_required") for r in structure_records) + gate_fired_any = any(f for firings in fired_by_domain.values() for f in firings) + status = ("READY_WITH_REVIEW" + if (reviews or exceptions or les_review_present or gate_fired_any) + else "READY") + counts = { + "rows": len(rows), + "bo_records": len(bo_sorted), + "structure_records": len(structure_records), + "gates_declared": len(declared_gates), + "gates_implemented": sum(1 for e in gate_report + if e.get("disposition") == "implemented"), + "gates_visualized_str": sum( + 1 for e in gate_report + if e.get("disposition") == "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW"), + "gates_visualized_unsupported": sum( + 1 for e in gate_report + if e.get("disposition") == "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW"), + "gates_fired": sum(1 for e in gate_report if e.get("fired")), + "orphan_rules": len(orphan_rules), + "review_items": len(reviews), + "exceptions": len(exceptions), + "conservation": equations, + } + bundle = { + "schema_version": "stage1_fact_ledger_candidate_bundle.v1", + "status": status, + "rows": rows, + "gate_report": {"gates": gate_report, + "fired_by_domain": {d: f for d, f in + sorted(fired_by_domain.items())}, + "orphan_rule_ids": orphan_rules}, + "counts": counts, + "signal_manifest_transaction_id": None, + "expected_runnable_domain_ids": expected_runnable, + "alias_resolution": {"table": alias["table"], + "self_declarations": alias["self_declarations"], + "mismatches": alias["mismatches"]}, + "created_by": "fact_ledger_compiler", + } + pack = { + "schema_version": "stage1_fact_exception_pack.v1", + "has_exceptions": bool(exceptions), + "exception_count": len(exceptions), + "exceptions": exceptions, + "budget": {"pack_item_char_limit": char_limit}, + } + audit["unattached_structure_ids"] = unattached_structures + audit["equations"] = equations + return {"fact_ledger_candidate_bundle": bundle, + "fact_exception_pack": pack, + "review_items": reviews, + "audit": audit} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/stage1_runtime/fact_ledger_compiler.txt b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/stage1_runtime/fact_ledger_compiler.txt new file mode 100644 index 00000000..641e4871 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/stage1_runtime/fact_ledger_compiler.txt @@ -0,0 +1,910 @@ +#!/usr/bin/env python3 +"""Compile v.8 Fact Ledger candidate rows from BO, LES, signals, envelopes, and gate sidecars. + +순수 함수 모듈 — I/O·시각·난수 없음. 규칙 선언은 fact_ledger_policy.v1.json 이 유일 정본이며 +이 모듈은 그것을 읽어 dispatch 만 한다. gate_id·27필드·registry type_id 리터럴을 코드에 두지 +않는다(정적 검사 11·12). 사건 종류 이름을 어디에도 두지 않는다(137종 원칙). + +공개 API + normalize_gate_rules(sidecar_doc, policy) 17축 방언 정규화 리더 + resolve_alias_table(alias_declarations, sidecars, policy) + compile_fact_ledger_candidates(...) 조립 본체 (아래 시그니처) +""" +from __future__ import annotations + +import json +import re +from typing import Any + +_WS_RE = re.compile(r"\s+") +_CODE_RE = re.compile(r"^[A-Z][A-Z0-9_]{2,127}$") +_DOMAIN_RE = re.compile(r"^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$") + + +# --------------------------------------------------------------------------- +# 공용 헬퍼 +# --------------------------------------------------------------------------- + +def _dict(value: Any) -> dict: + return value if isinstance(value, dict) else {} + + +def _list(value: Any) -> list: + return value if isinstance(value, list) else [] + + +def _strings(value: Any) -> list[str]: + return [item for item in _list(value) if isinstance(item, str)] + + +def _nonblank(value: Any) -> bool: + return value not in (None, "", [], {}) + + +def _text(value: Any, limit: int) -> str | None: + """키 오름차순 JSON -> 공백열 단일 공백 -> limit 절단 -> 빈 문자열이면 None.""" + if value is None: + return None + if isinstance(value, str): + rendered = value + else: + rendered = json.dumps(value, ensure_ascii=False, sort_keys=True) + rendered = _WS_RE.sub(" ", rendered).strip() + if not rendered: + return None + return rendered[:limit] + + +def _first_value(*values: Any) -> Any: + for value in values: + if _nonblank(value): + return value + return None + + +def _first_key(record: dict, keys: list[str]) -> Any: + for key in keys: + if _nonblank(record.get(key)): + return record[key] + return None + + +def _dedupe_keep_order(items: list[str]) -> list[str]: + seen: set[str] = set() + out: list[str] = [] + for item in items: + if item not in seen: + seen.add(item) + out.append(item) + return out + + +# --------------------------------------------------------------------------- +# F0-0c — BO 압축·정규화 (구본 _compact_bo 등가 계층. (가) 9필드 전부의 전제) +# --------------------------------------------------------------------------- + +def compact_bo(bo: dict, policy: dict) -> dict: + norm = _dict(policy.get("bo_normalization")) + key_map = _dict(norm.get("key_map")) + keep = _strings(norm.get("domain_payload_keep_keys")) + out: dict[str, Any] = {} + for key, value in bo.items(): + out[key_map.get(key, key)] = value + payload = _dict(_dict(bo.get("extensions")).get("domain_payload")) + out["domain_payload"] = {k: payload[k] for k in keep + if k in payload and _nonblank(payload[k])} + out["core"] = _dict(bo.get("core_field_base")) + return out + + +# --------------------------------------------------------------------------- +# (다) — 증거력 산정 (정책 evidence_scoring_da_2 의 5갈래 · 순서 그대로) +# --------------------------------------------------------------------------- + +def _flatten_evidence(document: Any, depth: int = 3) -> list[dict]: + rows: list[dict] = [] + + def visit(node: Any, level: int) -> None: + if level > depth: + return + if isinstance(node, dict): + if any(k in node for k in ("evidence_index", "id", "evidence_id", + "eid", "index", "doc_id")): + rows.append(node) + return + for value in node.values(): + visit(value, level + 1) + elif isinstance(node, list): + for item in node: + visit(item, level + 1) + + visit(document, 0) + return rows + + +def _evidence_id(record: dict) -> str | None: + value = _first_key(record, ["evidence_index", "id", "evidence_id", + "eid", "index", "doc_id"]) + return value if isinstance(value, str) else None + + +def _excerpt_from_indexed(record: dict) -> str: + facts = _strings(record.get("key_facts")) + if facts: + return " ".join(facts) + parts = _strings(record.get("key_dates")) + _strings(record.get("key_amounts")) + if parts: + return " ".join(parts) + for value in record.values(): + if isinstance(value, str) and value.strip(): + return value + return "" + + +def build_evidence_authority(evidence_document: Any) -> dict[str, dict]: + authority: dict[str, dict] = {} + for record in _flatten_evidence(evidence_document): + eid = _evidence_id(record) + if not eid: + continue + authority[eid] = { + "evidence_index": eid, + "title": _first_key(record, ["title", "Title", "name", "doc_title"]), + "excerpt": _excerpt_from_indexed(record), + "priority": _first_key(record, ["priority", "Priority", "weight"]), + "authentication": _first_key(record, ["authentication", "auth", + "source_authentication"]), + "source": "evidence_indexed.json", + } + return authority + + +def score_evidence(evidence_refs: list[str], authority: dict[str, dict], + policy: dict, reviews: list[dict], + fact_id: str) -> tuple[str, str, int]: + """(credibility, proof_strength, 절단여유 최소값) — 정책 5갈래 순서 그대로.""" + scoring = _dict(policy.get("evidence_scoring_da_2")) + markers = _strings(scoring.get("markers_documentary")) + rows: list[dict] = [] + for ref in evidence_refs: + row = authority.get(ref) + if row is None: + # 우주 밖 참조 — stub 도 비어 있지 않은 증거 행으로 계수된다(정책 승계). + reviews.append({"review_code": "EVIDENCE_REF_NOT_IN_EVIDENCE_UNIVERSE", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + row = {"evidence_index": ref, "missing": True} + rows.append(row) + if not rows: + return "low", "low", -1 + pieces: list[str] = [] + slack = 10 ** 9 + for row in rows: + rendered = _WS_RE.sub(" ", json.dumps(row, ensure_ascii=False, + sort_keys=True)).strip() + slack = min(slack, 300 - len(rendered)) + pieces.append(rendered[:300]) + text = " ".join(pieces) + if any(marker in text for marker in markers): + return "high", "documentary", slack + if len(rows) >= 2: + return "high", "high", slack + if ("meeting" in text) or ("회의" in text) or ("면담" in text): + return "medium", "meeting_only_core", slack + return "medium", "medium", slack + + +# --------------------------------------------------------------------------- +# F0-0 — 17축 사이드카 방언 정규화 리더 +# --------------------------------------------------------------------------- + +def normalize_gate_rules(sidecar_doc: Any, policy: dict) -> dict: + """사이드카 한 파일을 정규화한다. {rules: {gate_id: norm}, file_meta, warnings}.""" + axes = _dict(_dict(policy.get("gate_execution")).get("normalization_axes_17")) + array_keys = _strings(axes.get("rule_array_keys")) or ["rules", "gates"] + code_keys = _strings(axes.get("emitted_code_keys")) + doc = _dict(sidecar_doc) + raw_rules: list[dict] = [] + array_key = None + for key in array_keys: + if isinstance(doc.get(key), list): + array_key = key + raw_rules = [r for r in doc[key] if isinstance(r, dict)] + break + warnings: list[dict] = [] + norm_rules: dict[str, dict] = {} + for rule in raw_rules: + gate_id = rule.get("gate_id") + if not isinstance(gate_id, str): + warnings.append({"code": "GATE_RULE_WITHOUT_ID", "detail": str(rule)[:120]}) + continue + writer = _dict(rule.get("writer_effect")) + row_eff = _dict(rule.get("row_effect")) + on_true = _dict(rule.get("on_true")) + condition = rule.get("condition") + cond_kind = "dict" if isinstance(condition, dict) else "str" + cond_key = next(iter(condition), None) if cond_kind == "dict" else None + cond_value = condition.get(cond_key) if cond_kind == "dict" else condition + emitted: list[str] = [] + for key in code_keys: + value = rule.get(key) + if isinstance(value, str): + emitted.append(value) + else: + emitted.extend(_strings(value)) + blocks = None + for container, key in (("writer_effect", "final_drafting_block"), + ("writer_effect", "blocks_final_drafting"), + ("on_true", "blocks_final_drafting")): + holder = writer if container == "writer_effect" else on_true + if key in holder: + blocks = bool(holder[key]) + break + status_fire = (writer.get("status_when_triggered") + or writer.get("status_on_fire") + or on_true.get("writer_status")) + evaluation = (rule.get("evaluation_mode") or rule.get("implementation_type") + or rule.get("evaluation")) + norm_rules[gate_id] = { + "gate_id": gate_id, + "condition_kind": cond_kind, + "condition_key": cond_key, + "condition_value": cond_value, + "emitted_codes": _dedupe_keep_order(emitted), + "blocks_final_drafting": blocks, + "status_when_triggered": status_fire, + "row_must_consider": bool(row_eff.get("must_consider")), + "preserve_seed": bool(on_true.get("preserve_seed")), + "unknown_or_missing_policy": rule.get("unknown_or_missing_policy"), + "evaluation": evaluation, + "writes_fact_ledger": writer.get("writes_fact_ledger"), + "silent_repair_forbidden": rule.get("silent_repair_forbidden"), + "source_membership_required": rule.get("source_membership_required"), + "input_declaration": rule.get("input_fields") or rule.get("input_contracts"), + } + if doc.get("global_status_rule") is not None: + warnings.append({"code": "SIDECAR_GLOBAL_STATUS_RULE_IGNORED", + "detail": str(doc.get("global_status_rule"))[:120]}) + return {"rules": norm_rules, + "file_meta": {"array_key": array_key, + "schema_version": doc.get("schema_version"), + "status_key": ("closure_status" if "closure_status" in doc + else ("status" if "status" in doc else None))}, + "warnings": warnings} + + +# --------------------------------------------------------------------------- +# F0-0b — 별칭 해소 (D-P4-7 — 전수 자기선언 대조 · 침묵 탈락 금지) +# --------------------------------------------------------------------------- + +def resolve_alias_table(alias_declarations: Any, structure_type_sidecars: dict, + policy: dict) -> dict: + root = _dict(alias_declarations) + root = _dict(root.get("extension_payload_key_declarations", root)) + table = {k: _strings(v) for k, v in _dict(root.get("legacy_alias_targets")).items()} + derived_mismatches = _list(root.get("legacy_declaration_mismatches")) + abbrev = _dict(_dict(policy.get("legacy_alias")).get("abbreviation_map")) + self_decl: dict[str, set] = {} + for domain_id, doc in sorted(_dict(structure_type_sidecars).items()): + doc = _dict(doc) + rows = None + for key in ("types", "structure_types", "records", "entries"): + if isinstance(doc.get(key), list): + rows = doc[key] + break + for row in rows or []: + if not isinstance(row, dict): + continue + value = row.get("legacy_source") or row.get("legacy_source_domain") + if not isinstance(value, str): + continue + head = value.split("_")[0] + if head not in abbrev: + continue # 'Part3 fallback' 등 비 B* 값은 선언이 아니다 + self_decl.setdefault(abbrev[head], set()).add(domain_id) + reviews: list[dict] = [] + mismatch_map: dict[str, dict] = {} + for legacy in sorted(set(table) | set(self_decl)): + declared = self_decl.get(legacy, set()) + aliased = set(table.get(legacy, [])) + missing = sorted(declared - aliased) + excess = sorted(aliased - declared) + if missing: + mismatch_map[legacy] = {"missing": missing, "excess": excess} + reviews.append({"review_code": "LEGACY_ALIAS_SELF_MISMATCH_REVIEW", + "severity": "SOFT_WARNING", "legacy": legacy, + "missing_domain_ids": missing, + "excess_domain_ids": excess}) + derived_missing = sorted({str(_dict(m).get("domain_id")) + for m in derived_mismatches if _dict(m).get("domain_id")}) + actual_missing = sorted({d for m in mismatch_map.values() for d in m["missing"]}) + if derived_missing != actual_missing: + reviews.append({"review_code": "LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", + "derived_list": derived_missing, + "actual_missing": actual_missing}) + return {"table": table, "self_declarations": {k: sorted(v) for k, v in self_decl.items()}, + "mismatches": mismatch_map, "reviews": reviews} + + +# --------------------------------------------------------------------------- +# 조립 본체 +# --------------------------------------------------------------------------- + +def compile_fact_ledger_candidates( + bo_document: Any, + les_document: Any, + signal_documents: dict, + domain_envelopes: dict, + p2_handoff_document: Any, + evidence_document: Any, + registry_index: Any, + domain_configs: dict, + gate_sidecars: dict, + structure_type_sidecars: dict, + activation_document: Any, + alias_declarations: Any, + policy: dict) -> dict: + reviews: list[dict] = [] + audit: dict[str, Any] = {} + + activation_root = _dict(activation_document) + activation_root = _dict(activation_root.get("domain_activation_manifest", + activation_root)) + expected_runnable = _strings(activation_root.get("expected_runnable_domain_ids")) + + # ---- F0-0 리더 · F0-0b 별칭 ---------------------------------------- + normalized: dict[str, dict] = {} + dialect_counts: dict[str, Any] = {"array_key": {}, "files": 0} + reader_warnings: list[dict] = [] + for domain_id, sidecar in sorted(_dict(gate_sidecars).items()): + result = normalize_gate_rules(sidecar, policy) + normalized[domain_id] = result + dialect_counts["files"] += 1 + ak = result["file_meta"]["array_key"] + dialect_counts["array_key"][ak] = dialect_counts["array_key"].get(ak, 0) + 1 + reader_warnings.extend(result["warnings"]) + audit["sidecar_dialects"] = dialect_counts + audit["reader_warnings"] = reader_warnings + + alias = resolve_alias_table(alias_declarations, structure_type_sidecars, policy) + reviews.extend(alias["reviews"]) + + # ---- LES 적재 ------------------------------------------------------- + les_root = _dict(les_document) + structure_records = [r for r in _list(les_root.get("structure_records")) + if isinstance(r, dict)] + record_by_id = {r.get("structure_id"): r for r in structure_records + if isinstance(r.get("structure_id"), str)} + index = _dict(les_root.get("structure_index")) + by_bo_id = {k: _strings(v) for k, v in _dict(index.get("by_bo_id")).items()} + + # ---- SG 적재 -------------------------------------------------------- + def signal_records(sg: str) -> list[dict]: + doc = _dict(_dict(signal_documents).get(sg)) + return [r for r in _list(doc.get("records")) if isinstance(r, dict)] + + sg11_records = signal_records("SG-11") + sg11_by_signal_id = {r.get("signal_id"): r for r in sg11_records + if isinstance(r.get("signal_id"), str)} + sg04_by_domain: dict[str, list[str]] = {} + for record in signal_records("SG-04"): + for domain_id in _strings(record.get("domain_ids")): + if isinstance(record.get("signal_id"), str): + sg04_by_domain.setdefault(domain_id, []).append(record["signal_id"]) + + # ---- F0-7b — 게이트 입력 정규화 ------------------------------------ + handoff_root = _dict(p2_handoff_document) + handoff_root = _dict(handoff_root.get("stage1_part2_review_handoff", handoff_root)) + handoff_items = [r for r in _list(handoff_root.get("review_items")) + if isinstance(r, dict)] + p2_rules = _dict(policy.get("p2_handoff_rules")) + code_pool: dict[str, list[str]] = {} + gate_input_counts = {"envelope_review_items": 0, "handoff_codes": 0, + "handoff_not_a_code": 0, "handoff_domain_unresolved": 0, + "slot_entries": 0, "operand_states": 0} + for domain_id in expected_runnable: + env = _dict(_dict(domain_envelopes).get(domain_id)) + env = _dict(env.get("domain_signal_envelope", env)) + for item in _list(env.get("review_items")): + code = _dict(item).get("review_code") or _dict(item).get("code") + if isinstance(code, str) and _CODE_RE.match(code): + code_pool.setdefault(domain_id, []).append(code) + gate_input_counts["envelope_review_items"] += 1 + for item in handoff_items: + raw_code = item.get("source_review_code") + if not (isinstance(raw_code, str) and _CODE_RE.match(raw_code)): + if raw_code is not None: + gate_input_counts["handoff_not_a_code"] += 1 + reviews.append({"review_code": "P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW", + "severity": "SOFT_WARNING", + "detail": str(raw_code)[:80]}) + continue + source_domain = item.get("source_domain") + targets: list[str] = [] + if isinstance(source_domain, str): + if _DOMAIN_RE.match(source_domain): + targets = [source_domain] + elif source_domain in alias["table"]: + targets = alias["table"][source_domain] + if not targets: + gate_input_counts["handoff_domain_unresolved"] += 1 + reviews.append({"review_code": "P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW", + "severity": "SOFT_WARNING", + "detail": str(source_domain)[:80]}) + continue + for domain_id in targets: + code_pool.setdefault(domain_id, []).append(raw_code) + gate_input_counts["handoff_codes"] += 1 + slot_status: dict[str, list[dict]] = {} + for domain_id in expected_runnable: + env = _dict(_dict(domain_envelopes).get(domain_id)) + env = _dict(env.get("domain_signal_envelope", env)) + entries = [e for e in _list(env.get("evidence_slot_status")) + if isinstance(e, dict)] + if entries: + slot_status[domain_id] = entries + gate_input_counts["slot_entries"] += len(entries) + operand_by_domain: dict[str, list[str]] = {} + for record in sg11_records: + details = _dict(record.get("details")) + state = record.get("operand_state") or details.get("operand_state") + if not isinstance(state, str): + continue + for domain_id in (_strings(record.get("domain_ids")) + or _strings(details.get("domain_ids"))): + operand_by_domain.setdefault(domain_id, []).append(state) + gate_input_counts["operand_states"] += 1 + audit["gate_input_counts"] = gate_input_counts + + # ---- F0-1 ~ F0-6b — 행 생성 ---------------------------------------- + bo_records = [b for b in _list(bo_document) if isinstance(b, dict)] + bo_sorted = sorted(bo_records, key=lambda b: str(b.get("BO_ID") or "")) + authority = build_evidence_authority(evidence_document) + calc_types = set(_strings(policy.get("legal_calculation_object_types"))) + na_defaults = _dict(policy.get("field_synthesis_na_15")) + rows: list[dict] = [] + attach_stats = {"attached_bo": 0, "unattached_bo": 0} + used_structure_ids: set[str] = set() + used_sg11: set[str] = set() + slack_min = 10 ** 9 + for idx, bo in enumerate(bo_sorted, 1): + compact = compact_bo(bo, policy) + core = compact["core"] + fact_id = "F-%03d" % idx + bo_id = str(bo.get("BO_ID") or "") + # (가) 9필드 + parties = [] + if _nonblank(core.get("Performer")): + parties.append({"role": "actor", "value": _text(core.get("Performer"), 160), + "source": "BO.core_field_base"}) + if _nonblank(core.get("Subject")): + parties.append({"role": "counterparty", + "value": _text(core.get("Subject"), 160), + "source": "BO.core_field_base"}) + amount = _text(_first_value(compact.get("amount"), core.get("Amount")), 240) + action = _first_value(compact.get("action"), core.get("Action")) + evidence_refs = _strings(bo.get("source_evidence_indexes")) + # F0-3 — LES 조인 + structure_ids = sorted(set(by_bo_id.get(bo_id, []))) + attached = [record_by_id[s] for s in structure_ids if s in record_by_id] + if attached: + attach_stats["attached_bo"] += 1 + else: + attach_stats["unattached_bo"] += 1 + reviews.append({"review_code": "FACT_ROW_UNATTACHED_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": bo_id}) + used_structure_ids.update(s for s in structure_ids if s in record_by_id) + domain_effects: dict[str, dict] = {} + for record in attached: + domain_id = record.get("domain_id") + if not isinstance(domain_id, str): + continue + slot = domain_effects.setdefault(domain_id, { + "module": set(), "role_projection": set(), "structure_ids": set(), + "type_ids": set(), "review_codes": set()}) + if isinstance(record.get("module"), str): + slot["module"].add(record["module"]) + if isinstance(record.get("role_projection"), str): + slot["role_projection"].add(record["role_projection"]) + if isinstance(record.get("structure_id"), str): + slot["structure_ids"].add(record["structure_id"]) + if isinstance(record.get("type_id"), str): + slot["type_ids"].add(record["type_id"]) + slot["review_codes"].update( + c for c in _strings(record.get("review_codes")) if _CODE_RE.match(c)) + domain_effects = {d: {k: sorted(v) for k, v in slot.items()} + for d, slot in sorted(domain_effects.items())} + modules = sorted({m for slot in domain_effects.values() for m in slot["module"]}) + roles = sorted({r for slot in domain_effects.values() + for r in slot["role_projection"]}) + type_ids = sorted({t for slot in domain_effects.values() + for t in slot["type_ids"]}) + # F0-5 — SG-11 조인 + operand_state 승계 (재산정 금지) + calculation_requests: list[dict] = [] + route_refs: list[str] = [] + for record in attached: + for ref in _strings(record.get("calculation_request_refs")): + route_refs.append(ref) + sg = sg11_by_signal_id.get(ref) + if sg is None: + calculation_requests.append({"calculation_id": None, + "request_id": ref, + "provenance": {"signal_id": ref, + "dangling": True}}) + reviews.append({"review_code": "CALCULATION_REF_DANGLING_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + continue + used_sg11.add(ref) + details = _dict(sg.get("details")) + calc_id = details.get("calculation_id") + if not isinstance(calc_id, str) or not calc_id: + calc_id = ref + reviews.append({"review_code": "CALCULATION_ID_SUBSTITUTED_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + law_refs = sorted(set( + _strings(sg.get("law_version_refs")) + + _strings(details.get("law_version_refs")) + + [ref2 for d in _strings(sg.get("domain_ids")) + for ref2 in sg04_by_domain.get(d, [])])) + calculation_requests.append({ + "calculation_id": calc_id, + "request_id": ref, + "operand_state": (sg.get("operand_state") + or details.get("operand_state")), + "missing_operands": _strings(details.get("missing_operands")), + "law_version_refs": law_refs, + "provenance": {"signal_id": ref, + "structure_ids": sorted( + s for s in structure_ids + if ref in _strings( + _dict(record_by_id.get(s)).get( + "calculation_request_refs")))}, + }) + # (다) 증거력 + credibility, proof_strength, slack = score_evidence( + evidence_refs, authority, policy, reviews, fact_id) + slack_min = min(slack_min, slack) + # (나) 15필드 + object_spec = _text(core.get("Object"), 160) + calc_obj_candidates = sorted(calc_types & set(type_ids)) + legal_calculation_object = calc_obj_candidates[0] if calc_obj_candidates else None + alias_vocab = _dict(policy.get("alias_module_vocabulary")) + mod_actio = alias_vocab.get("actio_fields") + mod_money = alias_vocab.get("money_claim_effect") + mod_successor = alias_vocab.get("commercial_successor_effect") + actio_ids = sorted({s for slot in domain_effects.values() + for s in slot["structure_ids"] + if mod_actio in slot["module"]}) + actio_roles = sorted({r for slot in domain_effects.values() + if mod_actio in slot["module"] + for r in slot["role_projection"]}) + money_ids = sorted({s for slot in domain_effects.values() + if mod_money in slot["module"] + for s in slot["structure_ids"]}) + successor_ids = sorted({s for slot in domain_effects.values() + if mod_successor in slot["module"] + for s in slot["structure_ids"]}) + row = { + "fact_id": fact_id, + "source_bo_id": bo_id, + "type": compact.get("bo_type") if isinstance(compact.get("bo_type"), str) else None, + "date": _first_value(core.get("BehaviorTime"), compact.get("date")), + "parties": parties, + "object_spec": object_spec, + "amount": amount, + "action": action if isinstance(action, str) else _text(action, 240), + "evidence_refs": evidence_refs, + "credibility": credibility, + "legal_centrality": "high" if attached else "medium", + "proof_strength": proof_strength, + "legal_effect_roles": roles, + "claim_chain_ref": {"claim_group_ids": [], "liability_group_ids": [], + "signal_count": len(route_refs)}, + "linked_structures": {"structure_ids": structure_ids}, + "must_not_drop_in_claim_types": modules, + "must_consider": bool(attached), + "state_context": {"bo_type": compact.get("bo_type") + if isinstance(compact.get("bo_type"), str) else None, + "domain_ids": sorted(domain_effects)}, + "money_claim_effect": {"enabled": bool(money_ids), + "structure_ids": money_ids}, + "commercial_successor_effect": {"enabled": bool(successor_ids), + "structure_ids": successor_ids}, + "actio_roles": actio_roles, + "linked_actio_structures": actio_ids, + "downstream_module_candidates": modules, + "skeleton_validation_codes": [], + "derived_fact": action if isinstance(action, str) else _text(action, 240), + "derivation_basis": ("structure_ids=" + ",".join(structure_ids) + if structure_ids else "bo_only"), + "legal_calculation_object": legal_calculation_object, + "domain_effects": domain_effects, + "calculation_requests": calculation_requests, + } + rows.append(row) + audit["evidence_truncation_slack_min"] = (None if slack_min == 10 ** 9 else slack_min) + + # ---- F0-8 — 게이트 합집합 실행 (§4.4 그대로) ------------------------ + gate_exec = _dict(policy.get("gate_execution")) + implemented = set(_strings(gate_exec.get("implemented_condition_keys"))) + default_missing_policy = str(gate_exec.get("unknown_or_missing_policy_default") + or "fail_open_to_review") + declared_gates: dict[str, dict] = {} + for domain_id in expected_runnable: + config = _dict(_dict(domain_configs).get(domain_id)) + config_root = _dict(config.get("domain_config", config)) + for gate in _list(config_root.get("gates")): + gate = _dict(gate) + gate_id = gate.get("gate_id") + if isinstance(gate_id, str): + declared_gates[gate_id] = {"domain_id": domain_id, "declared": gate} + all_rules: dict[str, dict] = {} + for domain_id, result in normalized.items(): + for gate_id, rule in result["rules"].items(): + all_rules[gate_id] = {"domain_id": domain_id, "rule": rule} + gate_report: list[dict] = [] + fired_by_domain: dict[str, list[dict]] = {} + for gate_id in sorted(declared_gates): + info = declared_gates[gate_id] + domain_id = info["domain_id"] + declared = info["declared"] + registry_codes = [c for c in _strings(declared.get("review_codes")) + if _CODE_RE.match(c)] + entry: dict[str, Any] = {"gate_id": gate_id, "domain_id": domain_id, + "fired": False, "input_state": "n/a"} + rule_info = all_rules.get(gate_id) + if rule_info is None: + entry["disposition"] = "GATE_SPEC_NOT_DEPLOYED_REVIEW" + reviews.append({"review_code": "GATE_SPEC_NOT_DEPLOYED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + gate_report.append(entry) + continue + rule = rule_info["rule"] + fired = False + if rule["condition_kind"] == "dict": + key = rule["condition_key"] + if key in implemented: + entry["disposition"] = "implemented" + value = rule["condition_value"] + if key == "any_review_code_present": + pool = code_pool.get(domain_id) + if pool is None and domain_id not in _dict(domain_envelopes): + entry["input_state"] = "input_missing" + entry["missing_policy"] = ( + rule["unknown_or_missing_policy"] or default_missing_policy) + else: + entry["input_state"] = "supplied" + fired = bool(set(_strings(value)) & set(pool or [])) + elif key == "required_slots_missing_or_conflicting": + entries = slot_status.get(domain_id) + if entries is None and domain_id not in _dict(domain_envelopes): + entry["input_state"] = "input_missing" + entry["missing_policy"] = ( + rule["unknown_or_missing_policy"] or default_missing_policy) + else: + entry["input_state"] = "supplied" + wanted = set(_strings(value)) + fired = any( + (e.get("slot_id") in wanted) + and (e.get("status") in ("missing", "conflicted")) + for e in entries or []) + elif key == "calculation_operand_state_in": + states = operand_by_domain.get(domain_id) + entry["input_state"] = ("supplied" if states else "input_missing") + fired = bool(set(_strings(value)) & set(states or [])) + else: + entry["disposition"] = "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW" + entry["condition_key"] = key + reviews.append({"review_code": "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": str(key)}) + else: + entry["disposition"] = "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW" + entry["predicate_spec"] = _text(rule["condition_value"], 200) + reviews.append({"review_code": "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": _text(rule["condition_value"], 160)}) + # 차단 표지 대조 — registry ↔ 정규화된 사이드카. + declared_blocks = declared.get("blocks_final_drafting") + if (rule["blocks_final_drafting"] is not None + and declared_blocks is not None + and bool(declared_blocks) != rule["blocks_final_drafting"]): + reviews.append({"review_code": "GATE_BLOCK_FLAG_MISMATCH_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + entry["block_flag_mismatch"] = True + emitted = _dedupe_keep_order(rule["emitted_codes"] + registry_codes) + excess = [c for c in rule["emitted_codes"] if c not in registry_codes] + if excess: + entry["codes_not_in_registry"] = excess + reviews.append({"review_code": "GATE_EMITTED_CODE_NOT_IN_REGISTRY", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": ",".join(excess)[:160]}) + entry["fired"] = fired + entry["blocks_final_drafting"] = (rule["blocks_final_drafting"] + if rule["blocks_final_drafting"] is not None + else bool(declared_blocks)) + if fired: + projection_rows = [r["fact_id"] for r in rows + if domain_id in r["domain_effects"]] + entry["projected_row_count"] = len(projection_rows) + fired_by_domain.setdefault(domain_id, []).append({ + "gate_id": gate_id, "codes": emitted, + "row_must_consider": rule["row_must_consider"], + "preserve_seed": rule["preserve_seed"], + "blocks_final_drafting": entry["blocks_final_drafting"], + "status_when_triggered": rule["status_when_triggered"], + "projected_rows": projection_rows, + }) + gate_report.append(entry) + orphan_rules = sorted(set(all_rules) - set(declared_gates)) + for gate_id in orphan_rules: + reviews.append({"review_code": "GATE_RULE_NOT_IN_REGISTRY", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + + # ---- 행 투영 3분기 + F0-8b 스켈레톤 -------------------------------- + skeleton_order = _strings(_dict(policy.get("skeleton_checks")).get("order")) + for row in rows: + codes: list[str] = [] + if not _nonblank(row["derived_fact"]): + codes.append(skeleton_order[0]) + if not row["evidence_refs"]: + codes.append(skeleton_order[1]) + if not row["linked_structures"]["structure_ids"]: + codes.append(skeleton_order[2]) + gate_codes: list[str] = [] + for domain_id in sorted(row["domain_effects"]): + for firing in sorted(fired_by_domain.get(domain_id, []), + key=lambda f: f["gate_id"]): + gate_codes.extend(firing["codes"]) + if firing["row_must_consider"]: + row["must_consider"] = row["must_consider"] or True + row["skeleton_validation_codes"] = _dedupe_keep_order(codes + gate_codes) + + # ---- STRUCTURE_UNATTACHED_REVIEW ----------------------------------- + unattached_structures = sorted(set(record_by_id) - used_structure_ids) + for structure_id in unattached_structures: + reviews.append({"review_code": "STRUCTURE_UNATTACHED_REVIEW", + "severity": "SOFT_WARNING", "detail": structure_id}) + + # ---- F0-9 — 예외 pack ---------------------------------------------- + pack_policy = _dict(policy.get("exception_pack")) + char_limit = int(pack_policy.get("pack_item_char_limit") or 2000) + exceptions: list[dict] = [] + for row in rows: + if row["legal_calculation_object"] is None: + continue + bo = next((b for b in bo_sorted + if str(b.get("BO_ID") or "") == row["source_bo_id"]), {}) + core = _dict(bo.get("core_field_base")) + amount_candidates = [v for v in (bo.get("amount"), core.get("Amount")) + if _nonblank(v)] + if len(amount_candidates) >= 2: + payload = {"amount_candidates": [_text(v, 240) for v in amount_candidates], + "object_spec": row["object_spec"], + "calculation_request_ids": sorted( + str(c.get("calculation_id")) for c in + row["calculation_requests"] if c.get("calculation_id"))} + item = {"exception_id": "FX-%03d" % (len(exceptions) + 1), + "exception_type": "legal_calculation_required", + "fact_id": row["fact_id"], + "source_bo_id": row["source_bo_id"], + "conflict_flags": ["CALCULATION_BASIS_CONFLICT"], + "compact_payload": payload} + rendered = json.dumps(item, ensure_ascii=False) + if len(rendered) > char_limit: + item["oversize_review"] = True + reviews.append({"review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", + "fact_id": row["fact_id"], + "detail": "pack item oversize %d>%d" % (len(rendered), + char_limit)}) + exceptions.append(item) + elif (not _nonblank(row["amount"])) or (not _nonblank(row["object_spec"])): + reviews.append({"review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", "fact_id": row["fact_id"], + "detail": "amount_or_object_spec_blank"}) + unreachable = {name for name, meta in + _dict(policy.get("f1_exception_types")).items() + if _dict(meta).get("unreachable_in_v8")} + for item in exceptions: + if item["exception_type"] in unreachable: + raise ValueError(json.dumps({ + "reason_code": "PART4_UNREACHABLE_EXCEPTION_TYPE", + "exception_type": item["exception_type"]}, ensure_ascii=False)) + + # ---- F0-10 — 보존 등식 계수 ---------------------------------------- + effect_left = sum(len(r["domain_effects"]) for r in rows) + effect_right = 0 + calc_right = 0 + for bo in bo_sorted: + bo_id = str(bo.get("BO_ID") or "") + attached = [record_by_id[s] for s in by_bo_id.get(bo_id, []) + if s in record_by_id] + effect_right += len({r.get("domain_id") for r in attached + if isinstance(r.get("domain_id"), str)}) + calc_right += len({ref for r in attached + for ref in _strings(r.get("calculation_request_refs"))}) + calc_left = sum(len(r["calculation_requests"]) for r in rows) + sg11_total = len(sg11_records) + sg11_unused = sg11_total - len(used_sg11) + equations = { + "row": {"left": len(rows), "right": len(bo_sorted), + "holds": len(rows) == len(bo_sorted)}, + "attachment_coverage": { + "attached": attach_stats["attached_bo"], + "unattached": attach_stats["unattached_bo"], + "total": len(bo_sorted), + "holds": (attach_stats["attached_bo"] + attach_stats["unattached_bo"] + == len(bo_sorted))}, + "effect": {"left": effect_left, "right": effect_right, + "holds": effect_left == effect_right}, + "calc_row": {"left": calc_left, "right": calc_right, + "holds": calc_left == calc_right}, + "calc_source": {"attached_distinct": len(used_sg11), + "unattached_distinct": sg11_unused, + "total": sg11_total, + "holds": len(used_sg11) + sg11_unused == sg11_total}, + } + + # ---- 상태·집계 ------------------------------------------------------ + les_review_codes = set(_strings(policy.get("les_inherited_review_codes"))) + les_review_present = any( + (set(slot["review_codes"]) & les_review_codes) + for row in rows for slot in row["domain_effects"].values()) or any( + _dict(r).get("review_required") for r in structure_records) + gate_fired_any = any(f for firings in fired_by_domain.values() for f in firings) + status = ("READY_WITH_REVIEW" + if (reviews or exceptions or les_review_present or gate_fired_any) + else "READY") + counts = { + "rows": len(rows), + "bo_records": len(bo_sorted), + "structure_records": len(structure_records), + "gates_declared": len(declared_gates), + "gates_implemented": sum(1 for e in gate_report + if e.get("disposition") == "implemented"), + "gates_visualized_str": sum( + 1 for e in gate_report + if e.get("disposition") == "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW"), + "gates_visualized_unsupported": sum( + 1 for e in gate_report + if e.get("disposition") == "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW"), + "gates_fired": sum(1 for e in gate_report if e.get("fired")), + "orphan_rules": len(orphan_rules), + "review_items": len(reviews), + "exceptions": len(exceptions), + "conservation": equations, + } + bundle = { + "schema_version": "stage1_fact_ledger_candidate_bundle.v1", + "status": status, + "rows": rows, + "gate_report": {"gates": gate_report, + "fired_by_domain": {d: f for d, f in + sorted(fired_by_domain.items())}, + "orphan_rule_ids": orphan_rules}, + "counts": counts, + "signal_manifest_transaction_id": None, + "expected_runnable_domain_ids": expected_runnable, + "alias_resolution": {"table": alias["table"], + "self_declarations": alias["self_declarations"], + "mismatches": alias["mismatches"]}, + "created_by": "fact_ledger_compiler", + } + pack = { + "schema_version": "stage1_fact_exception_pack.v1", + "has_exceptions": bool(exceptions), + "exception_count": len(exceptions), + "exceptions": exceptions, + "budget": {"pack_item_char_limit": char_limit}, + } + audit["unattached_structure_ids"] = unattached_structures + audit["equations"] = equations + return {"fact_ledger_candidate_bundle": bundle, + "fact_exception_pack": pack, + "review_items": reviews, + "audit": audit} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/stage1_runtime/fact_ledger_policy.v1.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/stage1_runtime/fact_ledger_policy.v1.json new file mode 100644 index 00000000..8d428c5e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/Default_Agent/stage1_runtime/fact_ledger_policy.v1.json @@ -0,0 +1,401 @@ +{ + "schema_version": "stage1_fact_ledger_policy.v1", + "status": "ACTIVE", + "purpose": "Part 4 Fact Ledger 의 규칙 선언 정본. 27필드 목록·순서·합성 규칙, 증거력 산정, 게이트 방언 정규화와 구현 범위, 별칭 파생, 보존 등식, 루트 래핑을 코드 밖에 둔다. 코드는 이 선언을 읽어 dispatch 만 한다. registry 가 말하지 않는 값은 만들지 않는다 — 137종 사건 이름을 어디에도 두지 않는다.", + "authority": { + "strategy": "stage_1_part_4_개정전략서.md", + "row_baseline": "선행구축/S_2_GPT_v2/fixtures/legacy_equivalence/run_{1,2}/fact_ledger/Fact_Ledger_base.json (59행 x 27키 실측)", + "gate_sidecars": "domains//gates.json x25 (조립본 바이트 동일 복사 · 127규칙 · registry 121 전건 커버)", + "case_kind_names_forbidden": true + }, + "final_fields_27": [ + "fact_id", + "source_bo_id", + "type", + "date", + "parties", + "object_spec", + "amount", + "action", + "evidence_refs", + "credibility", + "legal_centrality", + "proof_strength", + "legal_effect_roles", + "claim_chain_ref", + "linked_structures", + "must_not_drop_in_claim_types", + "must_consider", + "state_context", + "money_claim_effect", + "commercial_successor_effect", + "actio_roles", + "linked_actio_structures", + "downstream_module_candidates", + "skeleton_validation_codes", + "derived_fact", + "derivation_basis", + "legal_calculation_object" + ], + "new_fields_2": [ + "domain_effects", + "calculation_requests" + ], + "new_fields_note": "29키 = 구 27 + 신규 2. 신규 2키는 required 로 올리지 않는다(D-P4-4 · Stage 2 호환). domain_effects 의 module·role_projection 은 정렬된 배열이다 — 한 도메인이 복수 module 을 선언할 수 있어 스칼라로 두면 정보가 소실된다(D-P4-e).", + "fact_id_format": "F-{idx:03d}", + "fact_id_rule": "BO_ID 사전순 1-기반 연속. 연속성은 F2 가 경성 단언한다.", + "row_cardinality": "BO 1건 = 1행", + "bo_normalization": { + "key_map": { + "BOType": "bo_type", + "Action": "action", + "BO_ID": "bo_id" + }, + "domain_payload_keep_keys": [ + "money_claim_details", + "possession_state", + "lien_state", + "notice_lifecycle", + "asset_alias", + "fractional_effect_events" + ], + "nonblank_only": true, + "note": "구본 _compact_bo(369-374행) 등가 계층. 정규화 없이 원본 대문자 키를 소문자로 찾으면 전건 None 이 된다 — (가) 9필드 전부의 전제." + }, + "field_synthesis_ga_9": { + "note": "(가) BO 유래 9필드 — 골든 59행 전건 값등가 요구. 우변을 바꾸면 정상 구현에서 G-r 이 깨진다.", + "fact_id": "F-{idx:03d} (BO_ID 사전순 1-기반)", + "source_bo_id": "BO_ID 그대로", + "type": "compact.bo_type 단독 (LES types[0] 폴백 금지)", + "date": "first_value(core_field_base.BehaviorTime, bo.date) — bo.date 는 BO 18키에 없어 항상 None. 골든은 BehaviorTime 0/59 라 전건 null", + "parties": "core_field_base 단독 — Performer -> role: actor · Subject -> role: counterparty · 값은 text(party, 160) · source: 'BO.core_field_base'. 구 signal 경로 폐지", + "amount": "text(first_value(bo.amount, core_field_base.Amount), 240) or None — 키 오름차순 JSON(ensure_ascii=False) -> 공백열 단일 공백 정규화 -> 240자 절단 -> 빈 문자열이면 None", + "action": "first_value(compact.action, core_field_base.Action) — 정규화 계층 통과 후의 소문자 키", + "evidence_refs": "BO source_evidence_indexes 단독 — LES 증거는 합집합하지 않는다(합집합하면 v.8 에서 깨진다). LES 증거는 domain_effects 쪽에만 싣는다", + "derived_fact": "action 복제 단독 — 구본 4항 서술 분기는 삭제 확정(deprecation 절)" + }, + "field_synthesis_na_15": { + "note": "(나) LES 유래 15필드 — 골든 등가를 요구하지 않는다(EXPECTED_DELTA-04). v.8 LES 부착에서 결정론으로 재산출한다. 부착 0 이면 아래 기본값이 그대로 남는다.", + "object_spec": { + "rule": "core_field_base.Object 단독 · 없으면 null · 폴백 없음", + "decision": "D-P4-d=(가) — LES 폴백 대상(asset_cluster_ids)이 Part 3 v.8 에서 소멸했으므로 BO 단독 원천으로 확정한다" + }, + "legal_centrality": "부착 있으면 high · 없으면 medium", + "legal_effect_roles": "부착 레코드 role_projection 정렬 합집합 · 기본 []", + "claim_chain_ref": "{claim_group_ids: [], liability_group_ids: [], signal_count: 부착 route signal 수} — 구 그룹 ID 는 v.8 에 존재하지 않으므로 상시 빈 배열", + "linked_structures": "{structure_ids: 정렬 합집합} · 기본 {structure_ids: []}", + "must_not_drop_in_claim_types": "부착 레코드 module 정렬 합집합 · 기본 []", + "must_consider": "1차 = 부착 존재 여부(bool) · 게이트 행 투영이 OR 로 갱신한다 — (나)∩(라) 겸용 필드", + "state_context": "{bo_type, domain_ids: domain_effects 키 정렬} · 기본 {bo_type, domain_ids: []}", + "money_claim_effect": "{enabled: module 'money_claim' 부착 여부, structure_ids: 해당 정렬 목록}", + "commercial_successor_effect": "{enabled: module 'commercial_successor' 부착 여부, structure_ids: 해당 정렬 목록}", + "actio_roles": "module 'actio' 부착 레코드의 role_projection 정렬 합집합 · 기본 []", + "linked_actio_structures": "module 'actio' 부착 레코드의 structure_id 정렬 목록 · 기본 []", + "downstream_module_candidates": "must_not_drop_in_claim_types 와 같은 파생(골든 59/59 동일값 실측 승계)", + "derivation_basis": "부착 있으면 'structure_ids=' + 콤마결합 · 없으면 'bo_only'", + "legal_calculation_object": "부착 type_id ∩ legal_calculation_object_types 의 사전순 최소 · 없으면 null" + }, + "legal_calculation_object_types": [ + "fraudulent_transfer", + "land_use_gain", + "money_claim", + "secured_debt", + "valuation" + ], + "legal_calculation_object_note": "구본 677행 5-원소 집합 승계(D-P4-1 값 이관). calculation_bindings 비공백 도메인으로 대체하면 항등식으로 퇴화한다(라운드 12 실측 — 25도메인 x type_id 56종 전건).", + "evidence_scoring_da_2": { + "note": "(다) credibility·proof_strength — 골든 59행 전건 값등가 요구. 갈래 순서를 바꾸면 재현이 깨진다(문서 초안 4갈래 = 29/59 실측).", + "authority_row_keys": { + "evidence_index": "id 키 6종(evidence_index·id·evidence_id·eid·index·doc_id) 첫 적중", + "title": "first_key(title, Title, name, doc_title)", + "excerpt": "key_facts -> key_dates/key_amounts -> 대표 문자열 폴백", + "priority": "first_key(priority, Priority, weight)", + "authentication": "first_key(authentication, auth, source_authentication)", + "source": "고정 문자열 'evidence_indexed.json'" + }, + "flatten_depth": 3, + "universe_missing_stub": "{evidence_index: eid, missing: true} — stub 도 비어 있지 않은 증거 행으로 계수한다(구본 1136행 승계)", + "text_rule": "행마다 json.dumps(sort_keys=True, ensure_ascii=False) -> 공백열 단일 공백 -> 300자 절단 -> ' '.join 으로 합쳐 1회 대조(합친 뒤 절단이 아니다). sort_keys 에서 title 이 최후미라 절단이 마커를 먼저 지운다 — 절단 여유를 영수증에 적는다", + "markers_documentary": [ + "등기", + "계약", + "판결", + "공문", + "금융", + "송금", + "계좌", + "registry", + "contract", + "judgment" + ], + "branches_in_order": [ + { + "order": 1, + "condition": "증거 행 0건", + "credibility": "low", + "proof_strength": "low" + }, + { + "order": 2, + "condition": "마커 10종 중 하나라도 text 에 존재", + "credibility": "high", + "proof_strength": "documentary" + }, + { + "order": 3, + "condition": "증거 행 2건 이상", + "credibility": "high", + "proof_strength": "high" + }, + { + "order": 4, + "condition": "text 에 meeting 또는 회의 또는 면담", + "credibility": "medium", + "proof_strength": "meeting_only_core" + }, + { + "order": 5, + "condition": "그 외 전부", + "credibility": "medium", + "proof_strength": "medium" + } + ] + }, + "skeleton_checks": { + "order": [ + "EMPTY_DERIVED_FACT", + "NO_EVIDENCE_REFS", + "NO_LINKED_LEGAL_EFFECT_STRUCTURE" + ], + "note": "스켈레톤 3코드를 이 고정 순서로 먼저, 그 뒤 게이트 코드를 gate_id 오름차순으로 append, 중복은 첫 출현 위치 보존 dedupe. G-n(바이트 동일)·G-r 이 순서에 의존한다." + }, + "gate_execution": { + "implemented_condition_keys": [ + "any_review_code_present", + "required_slots_missing_or_conflicting", + "calculation_operand_state_in" + ], + "visualized_condition_keys": { + "boundary_or_overlap_detected": "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW — 정의처(domain_extension.schema · Stage2_handoff_contract)가 배포 트리에 없다(D-P4-c=(가) · source_refs 배포 선언 0회 실측)" + }, + "str_condition_disposition": "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW — 술어 명세를 코드로 옮기는 것은 판정식 창작이다(D-P4-b=(가))", + "orphan_rule_disposition": "GATE_RULE_NOT_IN_REGISTRY", + "unknown_or_missing_policy_default": "fail_open_to_review", + "block_semantics": "blocks_final_drafting: true 는 Stage 1 기록을 막지 않는다. 행은 기록되고 final_writer_status 가 READY_WITH_REVIEW 가 되며 차단 표지는 Stage 2 로 전달된다.", + "row_projection": { + "target_rows": "발화 게이트의 도메인 d 에 대해 R(d) = { row | d ∈ row.domain_effects 키 }", + "codes": "R(d) 전 행의 skeleton_validation_codes 에 합집합 부착(순서 규칙은 skeleton_checks)", + "must_consider": "row_effect.must_consider 보유 규칙 발화 시 R(d) 전 행 OR 갱신(기존 True 미소실)", + "preserve_seed": "on_true.preserve_seed 는 행 필드가 아니라 writer report 게이트별 발화 목록에만 기록", + "empty_target": "R(d) 공집합이면 행에 닿지 않고 writer report 에만 계수(침묵 아님)" + }, + "normalization_axes_17": { + "rule_array_keys": [ + "rules", + "gates" + ], + "emitted_code_keys": [ + "emitted_codes", + "emitted_review_codes", + "review_code" + ], + "effect_containers": [ + "writer_effect", + "row_effect", + "on_true" + ], + "block_flag_paths": [ + "writer_effect.final_drafting_block", + "writer_effect.blocks_final_drafting", + "on_true.blocks_final_drafting" + ], + "status_when_triggered_paths": [ + "writer_effect.status_when_triggered", + "writer_effect.status_on_fire", + "on_true.writer_status" + ], + "evaluation_keys": [ + "evaluation_mode", + "implementation_type", + "evaluation" + ], + "row_effect_keys": [ + "row_effect.must_consider", + "on_true.preserve_seed" + ], + "file_status_keys": [ + "closure_status", + "status" + ], + "input_declaration_keys": [ + "input_fields", + "input_contracts" + ], + "optional_flags": [ + "writes_fact_ledger", + "unknown_or_missing_policy", + "silent_repair_forbidden", + "source_membership_required" + ], + "global_status_rule_note": "사이드카 global_status_rule 3방언은 채택하지 않는다 — 경고로만 기록하고 전역 규칙은 아래 global_status_rule 하나를 쓴다" + }, + "emitted_code_registry_check_surface": "gates[].review_codes (게이트 단위) — 도메인 어휘 면을 쓰면 실측 초과 2건(E18-G03·E20-G03)이 사라진다" + }, + "legacy_alias": { + "source": "routing/extension_payload_key_declarations.v1.json legacy_alias_targets", + "self_declaration_scan": "배포 domains//structure_types.json 전수 — 배열 키 방언(types·structure_types·entries) 정규화 · legacy_source 약칭(B1~B5)을 완전명으로 정규화 · B* 형이 아닌 값(예: 'Part3 fallback')은 선언이 아니다", + "mismatch_disposition": "누락 도메인은 해소 결과에 추가하지 않고 LEGACY_ALIAS_SELF_MISMATCH_REVIEW 로 누락 도메인 ID·영향 항목 수를 계수한다. 자산의 legacy_declaration_mismatches 는 파생 목록이므로 대조용으로만 쓰고 불일치 시 LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW 를 계수한다.", + "abbreviation_map": { + "B1": "B1_Money_Successor", + "B2": "B2_Secured_Registry", + "B3": "B3_Land_Valuation", + "B4": "B4_Actio", + "B5": "B5_Succession_Notice_Lien_Asset_Defense" + } + }, + "p2_handoff_rules": { + "code_key": "source_review_code — issue_type 은 enum 밖 값을 뭉갠다", + "code_pattern": "^[A-Z][A-Z0-9_]{2,127}$", + "not_a_code_disposition": "P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW", + "domain_attribution": "source_domain 을 별칭 해소표로 registry 도메인 ID 로 옮긴 뒤 해당 도메인 게이트에만 공급한다(전역 병합 금지 — 교차 오발화)", + "domain_unresolved_disposition": "P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW — 어느 게이트에도 공급하지 않는다" + }, + "calculation_join": { + "join_key": "signal_id (details.request_id 는 보장되지 않는 키)", + "operand_state": "Part 2 산출을 재산정 없이 승계한다(D-P4-6). deferred 는 registry 선언값이다.", + "calculation_id_missing": "signal_id 로 대체하고 CALCULATION_ID_SUBSTITUTED_REVIEW 계수 — 버리지 않는다", + "dangling_ref": "calculation_id 없는 stub 항목으로 적재(버리면 등식 ④가 정상 실행에서 발화) · CALCULATION_REF_DANGLING_REVIEW 계수", + "law_version_refs": "SG-11 레코드 law_version_refs ∪ details.law_version_refs ∪ 해당 도메인 SG-04 레코드 signal_id — 버전 확정 판단 금지(SG-04 는 참조만)" + }, + "conservation_equations_5": { + "row": "len(rows) == len(BO.json) -> PART4_ROW_CONSERVATION_BROKEN", + "attachment_coverage": "부착 BO 수 + 미부착 BO 수 == len(BO.json) -> PART4_BO_ATTACHMENT_COVERAGE_BROKEN", + "effect": "Σ_bo |domain_effects 키| == Σ_bo |by_bo_id 레코드 distinct domain_id| -> PART4_EFFECT_CONSERVATION_BROKEN", + "calc_row": "Σ_bo |calculation_requests| == Σ_bo |by_bo_id 레코드 distinct calculation_request_refs| -> PART4_CALC_CONSERVATION_BROKEN", + "calc_source": "부착 distinct SG-11 + 미부착 distinct == SG-11 레코드 총수 -> PART4_CALC_SOURCE_CONSERVATION_BROKEN", + "note": "④와 ⑤는 차원이 다르다 — 컴파일러가 SG-11 signal_id 를 도메인 전 레코드에 복사하므로 행 항목 수 ≠ 원천 레코드 수. by_bo_id == {} 는 정상 입력이다(골든 경로 부착 0/59)." + }, + "unattached_review": { + "bo": "FACT_ROW_UNATTACHED_REVIEW", + "structure": "STRUCTURE_UNATTACHED_REVIEW", + "note": "보존 등식은 손실을 세지 흡수를 세지 않는다 — 침묵 금지를 등식 위에 따로 건다" + }, + "exception_pack": { + "surviving_type": "legal_calculation_required", + "review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "trigger": "구본 866·899-925행 승계 — calculation_required AND amount 후보 2개 이상(bo.amount·core.Amount 양쪽 비공백)이면 pack 적재 + conflict_flags [CALCULATION_BASIS_CONFLICT] · elif amount 또는 object_spec 공백이면 pack 미적재 + CALCULATION_BASIS_INCOMPLETE_REVIEW 검토(if/elif 배타)", + "exception_id_format": "FX-{n:03d}", + "pack_item_char_limit": 2000, + "oversize_disposition": "자동 차단이 아니라 exception_id 유지한 채 review", + "unreachable_in_v8": { + "unresolved_asset_cluster_projection": "asset_cluster 계열이 Part 3 v.8 에서 소멸(P2·P3·LES 스키마·컴파일러 0회)", + "unresolved_structure_role_projection": "registry structure_types 73/73 이 module·role_projection 전건 선언(결손 0)" + }, + "unreachable_disposition": "pack 에 실리면 PART4_UNREACHABLE_EXCEPTION_TYPE 경성 실패" + }, + "adjudication": { + "accepted_status": [ + "READY", + "READY_NO_EXCEPTIONS", + "READY_WITH_BLOCKS" + ], + "allowed_patch_fields": [ + "amount" + ], + "note": "허용 필드 밖 수정은 PART4_PATCH_REJECTED. F1 의 유일 생존 부류가 계산 근거 다중성 판정이므로 패치 가능 필드는 amount 하나다." + }, + "global_status_rule": "blocked item · rejected patch · deterministic review · LES 승계 검토(review_codes 2종 ∪ review_required 플래그) · gate review code 중 하나라도 있으면 READY_WITH_REVIEW, 아니면 READY (동결 계약 승계 — 사이드카 3방언 값은 경고로만 기록)", + "les_inherited_review_codes": [ + "UNREGISTERED_EFFECT_TYPE_REVIEW", + "PART3_REGISTRY_SIDECAR_MISMATCH" + ], + "root_wrapping_table": { + "signals/signal_manifest.json": null, + "routing/domain_activation_manifest.json": "domain_activation_manifest", + "BO.json": "(array)", + "legal_effect_structures.json": null, + "signals/domain_signals/.json": "domain_signal_envelope", + "quality_gates/stage1_part2_review_handoff.json": null, + "evidence_indexed.json": null, + "stage1_tmp/fact_ledger/fact_ledger_candidate_bundle.json": "fact_ledger_candidate_bundle", + "stage1_tmp/fact_ledger/fact_exception_pack.json": "fact_exception_pack", + "stage1_tmp/fact_ledger/fact_adjudication.json": "fact_exception_adjudication", + "Fact_Ledger_base.json": "(array)", + "stage1_tmp/fact_ledger/fact_ledger_writer_report.json": null, + "quality_gates/stage1_part4_review_handoff.json": "stage1_part4_review_handoff", + "validation_assets/routing/part4_receipt.json": null, + "signals/governing_law_version_signals.json": null, + "signals/timeline_notice_condition_signals.json": null, + "signals/asset_right_state_signals.json": null, + "signals/evidence_proof_conflict_signals.json": null, + "signals/calculation_requirements.json": null + }, + "review_codes_soft": [ + "GATE_SPEC_NOT_DEPLOYED_REVIEW", + "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW", + "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW", + "GATE_RULE_NOT_IN_REGISTRY", + "GATE_EMITTED_CODE_NOT_IN_REGISTRY", + "GATE_BLOCK_FLAG_MISMATCH_REVIEW", + "FACT_ROW_UNATTACHED_REVIEW", + "STRUCTURE_UNATTACHED_REVIEW", + "CALCULATION_REF_DANGLING_REVIEW", + "CALCULATION_ID_SUBSTITUTED_REVIEW", + "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "LEGACY_ALIAS_SELF_MISMATCH_REVIEW", + "LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW", + "P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW", + "P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW", + "EVIDENCE_REF_NOT_IN_EVIDENCE_UNIVERSE", + "EMPTY_DERIVED_FACT", + "NO_EVIDENCE_REFS", + "NO_LINKED_LEGAL_EFFECT_STRUCTURE" + ], + "hard_failure_codes": [ + "PART4_ASSET_DEPLOYMENT_INCOMPLETE", + "MODULE_MIRROR_UNREGISTERED", + "MODULE_MIRROR_HASH_MISMATCH", + "PART4_POLICY_INVALID", + "PART4_LEDGER_SCHEMA_STALE", + "PART4_COMPILER_STALE", + "PART4_SIGNAL_SEAL_FAILED", + "PART4_REGISTRY_INDEX_SEAL_FAILED", + "PART4_LES_TRANSACTION_MISMATCH", + "PART4_ROW_CONSERVATION_BROKEN", + "PART4_BO_ATTACHMENT_COVERAGE_BROKEN", + "PART4_EFFECT_CONSERVATION_BROKEN", + "PART4_CALC_CONSERVATION_BROKEN", + "PART4_CALC_SOURCE_CONSERVATION_BROKEN", + "PART4_COVERAGE_BROKEN", + "PART4_PATCH_REJECTED", + "PART4_BO_CONSERVATION_BROKEN", + "PART4_FACT_ID_SEQUENCE_BROKEN", + "PART4_ADJUDICATION_MISSING", + "PART4_DOMAIN_EFFECT_NOT_ACTIVE", + "PART4_LEDGER_SCHEMA_INVALID", + "PART4_UNREACHABLE_EXCEPTION_TYPE", + "PART4_WRITE_REREAD_MISMATCH" + ], + "deprecation": { + "old_4_fields_alias": "money_claim_effect·commercial_successor_effect·actio_roles·linked_actio_structures 는 v.8 한 버전 동안 domain_effects 로부터의 결정론 별칭이다. v.9 에서 제거 예정(D-P4-a=(가)).", + "derived_fact_4part_branch": "구본의 date·actor·obj·action 4항 서술 분기는 삭제 확정 — object_spec 참조가 끊겨 조건이 영구 거짓이 되는 사문이다.", + "old_signal_3_root_names": "actio_case_signals·case_liability_signals·legal_effect_signals 뿌리 이름을 읽지 않는다(C-7 폐쇄)." + }, + "f1_exception_types": { + "legal_calculation_required": { + "unreachable_in_v8": false + }, + "unresolved_asset_cluster_projection": { + "unreachable_in_v8": true + }, + "unresolved_structure_role_projection": { + "unreachable_in_v8": true + } + }, + "alias_module_vocabulary": { + "money_claim_effect": "money_claim", + "commercial_successor_effect": "commercial_successor", + "actio_fields": "actio", + "note": "구 4필드 별칭이 참조하는 module 어휘. 코드에 리터럴을 두지 않는다(정적 검사 11) — registry structure_types[].module 이 원천이고 이 선언은 그중 별칭 파생이 쓰는 세 값을 고정한다." + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/part_4_yaml_construction_summary.md b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/part_4_yaml_construction_summary.md new file mode 100644 index 00000000..a9d99446 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/part_4_yaml_construction_summary.md @@ -0,0 +1,65 @@ +# Stage 1 — Part 4 YAML 신규 작성 요약서 + +- 작성일: 2026-08-23 +- 정본 명세: `stage_1_part_4_개정전략서.md` (구 `Stage_1_Part_4_Claude_v3.yml`과 무관한 신작) +- 검증: 독립 sub-agent 2회 왕복 — 1차 FAIL(MAJOR 1·MINOR 2) → 전건 수정 → **재검증 PASS · DEFECTS 없음** + +## 1. 최종 산출물 + +| 산출물 | 위치 | sha256(16) · 크기 | +|---|---|---| +| Part 4 YAML | `ver_8_yaml_candidates/stage_1_part_4_v.8.yml` | `fc60011042f1159a` · 69,004 B | +| 사실원장 컴파일러 | `Default_Agent/stage1_runtime/fact_ledger_compiler.py` (+`.txt` 바이트 동일 미러) | `f6c4b476f4974104` · 45,335 B | +| 사실원장 정책 | `Default_Agent/stage1_runtime/fact_ledger_policy.v1.json` | `fea0cea48d862c6c` · 21,350 B | +| 원장 스키마 | `Default_Agent/platform/schemas/fact_ledger_base.schema.json` | `b3f0e79ecb4c2f72` · 7,415 B | +| 후보 번들 스키마 | `Default_Agent/platform/schemas/fact_ledger_candidate_bundle.schema.json` | `4e481504fb795b2b` · 10,105 B | +| 예외 팩 스키마 | `Default_Agent/platform/schemas/fact_exception_pack.schema.json` | `4eba7e51ed46a99e` · 2,087 B | +| 게이트 사이드카 | `Default_Agent/domains//gates.json` ×25 (조립본 바이트 동일 복사, 81,430 B) | — | +| 매니페스트 | `Default_Agent/runtime_manifest.json` 434→465항 (+31 = 신설 6 + 사이드카 25) | `8964593a64a9b1bc` | +| 인계면 | `선행구축/Default_Agent_Stage_1/handoffs/stage1_part_interface.v1.json` 45→55행, `stage1_stage_chain.v1.json` Part 4 구간 | — | +| LES 스키마 개정 | `Default_Agent/platform/schemas/legal_effect_structures.schema.json` — `signal_manifest_transaction_id` required 승격 (§8-4b) | — | + +신설 자산 6종 + 개정 LES 스키마·정책은 조립본(`선행구축/Default_Agent_Stage_1/`)에 파일 동기화 완료(빌더 등록 없음 — §9-11 규율). + +## 2. YAML 구조 (task 3종, 완전 직렬 DAG) + +- **F0 `Task_C_FL_F0_fact_ledger_reducer`** (code-executor, timeout 300): 배포 게이트 13자산 → 모듈 5종 물질화 → 정책/스키마 능력 확인 → 봉인 3종(signal_manifest tx+파일 sha · SG-01 registry sha+config_sha 사슬 · LES 트랜잭션 2갈래) → 입력 적재(BO 경성, P2 handoff·evidence는 `read_json(상수)` + try/except 부재 계수) → `compile_fact_ledger_candidates(13인자)` → 번들·팩·handoff(OPEN)·영수증(실측값) 기록. +- **F1 `Task_C_FL_F1_fact_exception_adjudicator`** (LLM gemini-3.1-flash-lite / reasoning low / max_iterations 1): 예외 팩만 읽고 `fact_adjudication.json` 기록 (SELECT_AMOUNT | BLOCK_REVIEW, patch_field는 "amount" 한정). +- **F2 `Task_C_FL_F2_final_fact_ledger_gate_and_writer`** (code, timeout 240): 재정판정 적용(수용 status·허용 패치 필드는 정책에서 읽음) → domain_effects ⊆ 활성 도메인 검사(등식보다 먼저 — G-g 순서) → 보존 등식 5종 원본 재계수 → BO 보존·fact_id 연속성·닫힌 스키마 → `Fact_Ledger_base.json`(맨몸 배열) + writer 보고서 + handoff FINALIZED. + +행 계약: BO 1:1, `F-{idx:03d}` 연속, 29키 = 구 27키(이름·순서 보존) + `domain_effects`·`calculation_requests`(required 제외). 게이트 실행: 사이드카 25종 127규칙, 17축 방언 정규화, dict 구현 3부류(25건) 실행 · str 90+경계 6 시각화 · 고아 6 review — 코드에 gate_id/모듈 어휘 리터럴 0(정책이 유일 출처). + +## 3. 검증 실측 (재검증 PASS 시점) + +- 인터페이스 검사기 4-yaml 전수(part 1~4, 정본 선언표 55행): `status: PASS` · `finding_count: 0` ×4. +- 골든 재현: (가)9+(다)2 = 11필드 × 59행 불일치 0 · (라) 차분 EXPECTED_DELTA-03∪-05 정확 일치 · 결정성 2회 바이트 동일 · 보존 등식 5/5. +- 26-A 전수: 규칙 121 / 사이드카 25 / str 90 / 경계 6 / 고아 6. 발화 양·음성 테스트 전건 통과. +- F2 파괴 시나리오 8종: 실패 코드 전건 정확 발화, 정상 amount 패치 반영. +- F0 스텁 전체 실행: 배포 게이트 13 · 봉인 3종 통과 · 4파일 기록 · 2회 바이트 동일. +- 부재 의미론: evidence+P2 handoff 부재 → 경고 2건 계수 후 59행 완주. adjudication 부재+예외 0 → 진행, 예외 1+부재 → `PART4_ADJUDICATION_MISSING`. +- 매니페스트 465항 전수 재계산: match 188 · MISMATCH 0 · absent 277(절대경로 참조 자산). + +## 4. 검증 1차 FAIL → 수정 내역 + +| # | 결함 | 수정 | +|---|---|---| +| D1 (MAJOR) | 소비 선언 3건이 간접 래퍼(`try_read_json`)로 읽혀 검사기에 비가시(U4 ×3). 내 "finding 0" 주장도 존재하지 않는 출력 키를 읽은 허위였음 | 3건 모두 `read_json(경로상수)` 직접 호출 + try/except로 부재 의미론 보존. 검사기 재실행은 실재 키(`finding_count`·`counts_by_code`)로 판독 | +| D2 (MINOR) | LES 스키마 required에 `signal_manifest_transaction_id` 누락(§8-4b 미이행) | required 승격, 매니페스트 해시 갱신, 조립본 동기화 | +| D4 (관찰) | F2가 수용 status 리터럴 중복 보유, 정책 root_wrapping_table에 SG 5종 누락 | F2는 `policy.adjudication.accepted_status` 참조로 전환, SG 5종 진입행 추가 | +| D3 (문서) | 아래 §5 | 본 요약서에 정정 기록 | + +## 5. 전략서 §7.4 정정 (D3) + +전략서 §7.4의 "골든-S2 봉인 사슬이 현 배포본에서 그대로 통과한다"는 서술은 **현재 트리에서 거짓**이다. 골든-S2의 SG-01 사본이 담은 `registry_index_sha256`(`a47a76aa…`)은 현 배포 `domains/_registry_index.json` 실측 sha(`9f177ebf…`)와 다르다. 산출 코드는 봉인을 올바르게 검사했기 때문에 이 불일치에 걸린 것이며, 결함이 아니다. + +**골든-S2 픽스처로 F0를 돌릴 때의 재봉인 레시피**: SG-01 사본의 `registry_index_sha256` 필드 **하나만** 현 배포 `domains/_registry_index.json`의 실측 sha(`9f177ebf…`)로 치환한다. 다른 필드는 무변경 — config_sha256 사슬은 현 registry entries에서 읽으므로 추가 보정이 불필요하다. (검증자가 이 방식으로 봉인 셋 통과를 2회 재현.) + +## 6. 미측정·이월 항목 + +1. **실주행**: code-executor·localdocs 실환경 실행 미실시(서버에 신설 31자산 + 매니페스트 465 배포 필요). +2. **F1 실 LLM 품질**(§9-14): flash-lite 재정판정 품질은 스텁으로만 검증. +3. **게이트 실발화**: dict 3부류의 실사건 발화는 §9-3·5·6·13 해소 후 관측 가능. +4. **(나) 15필드 재산출 기준선**(G-r2/26-B): 골든 LES가 비어 있어 값 등가 미검증(현재는 공 LES에서 규칙 실행만 확인). +5. **G-w2** 비자명 등식 경로, **SUPP-calc6** 행 단위 승계 완결형. +6. F2 블록의 `try_read_json` 정의는 사문 헬퍼로 잔존(무해) — v.9 정리 후보. +7. 관례 수립: 검사기류 출력 소비 시 키 존재 단언(`assert "finding_count" in r`)을 선행할 것 — 1차 MAJOR의 뿌리였던 "부재 키 기본값을 성공으로 읽는" 유형 차단. diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/stage_1_part_4_개정전략서.md b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/stage_1_part_4_개정전략서.md new file mode 100644 index 00000000..08347880 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/stage_1_part_4_개정전략서.md @@ -0,0 +1,1362 @@ +# Stage 1 Part 4 — Fact Ledger 개정 전략서 + +- 문서 위치: `YAML_Prompts/1. Stage_1/v.7/extension_research/stage_1_part_4_개정전략서.md` +- 상위 지시 정본: `stage_1_update_strategy.md` §0.3 확정 계약표 · §1 전체 DAG(Part 4 구간) · §5 Part 4 세부 워크플로우 · §6.8 6단계 +- 상류 계약: `stage_1_part_1_and_2_updated_yaml_analysis.md`(Part 1·2) · `stage_1_part_3_updated_yaml_analysis.md`(Part 3) · `ver_8_yaml_candidates/stage_1_part_{1,2,3}_v.8.yml` 원문 +- 절대 규칙: `ensemble_v2.md`(실체 registry 22 = leaf 20 + 공통층 2 · 상시 4 → **배포 `domain_config` 실측 26** · 계산 17 · signal 13) · 137종 registry 기반 +- 작성 규칙: YAML은 `v.7/SKILL.md`, code-executor Python은 `extension_research/test_code_executor.ipynb` +- 산출 대상: `ver_8_yaml_candidates/stage_1_part_4_v.8.yml` (신작) + `Default_Agent/` 배포 자산 +- 실측 기준: 모든 수치·경로·키·행 번호는 통합본 원문·배포 원본 트리·**조립본 트리**·골든 fixture·인계면 선언표를 직접 파싱하여 얻었다. 근거는 §11. +- 검증 이력: §12 (적대적 sub-agent **12라운드**) + +--- + +## 0. 한눈에 — 결론 먼저 + +**Part 4는 구본의 diff가 아니라 신작이다.** 구본이 기대하는 `legal_effect_structures.json` 계약과 Part 3 v.8이 실제로 내는 계약이 어긋난다(§1.1). + +**게이트는 창작하지 않는다.** 조립본에 `domains//gates.json` **25개(127규칙)**가 있고 registry gate_id **121개를 전건 커버**한다(dangling 0). Part 3가 사이드카를 바이트 동일 복사로 배포한 절차 그대로 배포하면, **판정식을 한 줄도 새로 쓰지 않고 registry∩dict 31건을 실행**할 수 있다(§1.5·§4.4). + +**그 31건이 실제로 발화하려면 입력 하나가 더 필요하다** — `signals/domain_signals/.json` 봉투의 `review_items`·`evidence_slot_status`. 이 파일은 **인계면 선언표 행 39에 이미 있고**(생산자 P2-S0, 소비자 Part 3 L0), Part 4는 **소비자 한 줄 추가**로 입력을 연다(§1.4·§6.5). + +| 축 | Part 4 (개정 후 목표) | +|---|---| +| 스테이지 이름 | `stage1_사실원장_생성` | +| Agent 이름 | `Liti-agent_Civil_Suit_Plaintiff_Stage_1_Part_4` | +| task 수 | **3** (F0 · F1 · F2) | +| DAG 노드 수 | **5** (IN + 3 + OUT) · 완전 직렬 | +| LLM / code | **1 / 2** | +| fan-out | 없음 | +| mcpServers | `{localdocs, code-executor}` | +| signal 진입 계약 | `downstream_read_sets.part4_F0` = **SG-04·SG-06·SG-07·SG-10·SG-11** | +| 게이트 입력 | 조건 키마다 다르다 — SG-11 `details` · 봉투 `review_items`/`evidence_slot_status` · BO `extensions.domain_payload`(§1.5b 도달성 표) | +| 게이트 실행 | registry 121 = **구현 25 · 가시화 96**(str 90 + boundary 6) · 사이드카 고아 6 검토. **골든-S2 기준 실발화 0**(입력이 비었고, 설령 발화해도 골든 경로는 `domain_effects` 공집합이라 **행 투영 대상이 0**이다) — 입력이 비어 있다(§1.5b). **이 수치는 D-P4-c=(가) 가정 하의 값이다**(§10 파급표) | +| 최종 산출 | **`Fact_Ledger_base.json`** (+ writer report · review handoff · receipt) | +| 로직 거처 | 신설 모듈 `stage1_runtime/fact_ledger_compiler.py` + 정책 `fact_ledger_policy.v1.json` | +| 골든 기준선 | **59행 × 27필드** · `fact_id` = `F-001`~`F-059` · 2-run 바이트 동일 | +| 골든 등가 판정 | **필드 4부류**(BO 유래 **9** 값등가 · LES·구signal 유래 **15** 재산출대조 · 증거 유래 **2** 값등가 · 스켈레톤 **1** 차분) · 등가 예외 **5건**(§7.1b) | + +### 0.1 픽스처 사전 — "골든"이 가리키는 것을 먼저 못박는다 + +이 문서에서 픽스처 이름은 **경로와 분모를 동반해야만** 쓴다. 라운드 4에서 서로 다른 픽스처의 값을 같은 이름으로 불러 도달성 판정을 잘못한 자리가 있었다. + +| 이름 | 경로 | 무엇을 재는가 | 실측 | **주지 않는 것** | +|---|---|---|---|---| +| **골든-FL** | `선행구축/S_2_GPT_v2/fixtures/legacy_equivalence/run_{1,2}/` | Fact Ledger 등가 기준선 | 파일 **5종**(`canonical/BO.json` · `fact_ledger/Fact_Ledger_base.json` 59행×27키 · `legal_effects/legal_effect_structures.json` **레거시 형태** · `gates/stage1_part2_review_handoff.json` · `migration/bo_domain_routes.json`) · 2-run 바이트 동일 | **`signal_manifest` · SG 5종 · 봉투 · `domain_activation_manifest` · `evidence_indexed` 전부 없음.** LES도 v.8 형태가 아니다. **그리고 BO 자체가 v.8 조인 규격이 아니다** — `Legal_Keywords`가 한국어 법률용어 26종(`가압류`·`도급`·`근저당권설정`…)인데 v.8 Part 2는 이 칸에 registry `type_id`(`effect_type_ids`, Part 2 v.8 1262행)를 싣는다. **registry `type_id` 56종과 교집합 0** | +| **골든-S2** | `S_5_GPT{,_v2}/e2e/run_{1,2}/golden_s2/` + `S_5_GPT_v2/validation/formal_asset_root/e2e/run_1/golden_s2/` (5트리) | Part 2 산출 골든(봉투·SG) | 봉투 **distinct 11도메인**(5트리 바이트 동일 복제라 합계로 세면 55) · `review_items`·`evidence_slot_status` **전건 공백** · SG-11 **0레코드**(`status: EMPTY`) · **SG-01 `expected_runnable_domain_ids` = 그 11도메인**(E-02·03·04·**09**·10·11·13·15·**X1**·X2·X3) · `registry_index_sha256` 실적재 | **BO 없음 · Fact Ledger 기준선 없음.** 그 외에는 다 있다 — SG-01(`domain_activation_manifest.json`) · **봉인된 `signal_manifest.json`**(`transaction_id: S5TX-ebaeba7766d30f38480b` · `files[]` 27) · **SG 13종 전부** · 봉투 11 | +| **e2e 시나리오** | `S_5_GPT*/e2e/*/{s3_adapter,s4_states,positive,blocked,review,empty,generic_8,forbidden_conclusion}/` | 비골든 검증 픽스처 | 5개 run 트리에 **중복 배치** — 합계로 세면 5배 | 골든 기준선 아님 | + +**두 골든은 상호 배타다.** (경로의 `golden_s2` 디렉터리는 저장소에 9개 있으나 봉투를 담은 것은 5트리다.) 골든-FL은 BO와 기준선을 주지만 F0의 진입 봉인(§4.1④)과 적재(§4.1⑤)에 필요한 신호 자산이 하나도 없고, 골든-S2는 신호를 주지만 BO도 Fact Ledger 기준선도 없다. **어느 한쪽으로도 G-r을 돌릴 수 없다** — 조립 절차가 필요하다(§7.4 `SUPP-golden59`). + +**분모 규약** — "골든에서 n건"은 **골든-FL 또는 골든-S2 한정**이다. e2e 시나리오 값을 인용할 때는 **distinct 기준**(중복 run 제거)과 시나리오명을 함께 적는다. + +예 1: SG-11 `details.operand_state`는 전 트리 합계로 80레코드 중 30건이지만, **distinct로는 16레코드 중 6건**(`s4_states` 4 · `positive` 1 · `blocked` 1)이고 **골든-S2에서는 0레코드**다. + +예 2: 봉투 `review_items` 비공백은 전 트리 합계 28이지만 **distinct 8봉투**(전부 `s3_adapter`)이고 골든-S2에서는 0이다. + +--- + +**Part 4가 하는 일 한 문장** — Part 2가 만든 BO·도메인 봉투와 Part 3가 만든 법률효과 구조를, registry가 선언한 효과 투영과 게이트 사이드카 위에 얹어, **BO 한 건마다 사실 행 하나**를 결정론으로 세우고 `domain_effects{도메인ID}`·`calculation_requests[]`로 일반화한 뒤 활성 도메인 게이트 합집합을 실행해 Stage 2에 넘긴다. + +--- + +## 1. 착수 전 실측 + +### 1.1 [치명] 구본 F0의 LES 계약 ≠ Part 3 v.8 실산출 + +| 축 | 구본 F0가 기대 | Part 3 v.8 실산출 | 판정 | +|---|---|---|---| +| `schema_version` | `stage1_legal_effect_structures.v1` (403–404행 const) | **동일** | **일치 — 유일** | +| 레코드 배열 키 | `legal_effect_structures` (407행) | **`structure_records`** | 불일치 | +| 색인 필수 | `by_bo_id`·`by_issue_cluster_id`·`by_asset_cluster_id`·`by_evidence_index`·`by_candidate_structure_type` **5종** (411–413행) | **`by_domain_id`·`by_module`·`by_bo_id` 3종** | 4종 소멸·2종 신설 | +| 레코드 유형 키 | `candidate_structure_type` | **`type_id`** | 불일치 | +| 레코드 부가 | `structure_label`·`issue_cluster_ids`·`asset_cluster_ids`·`evidence_indexes`·`linked_claim_group_ids`·`linked_liability_group_ids`·`linked_actio_signal_ids`·`legal_effect_roles`(446행) | 없음 | 전부 소멸 | +| 레코드 필수 | — | 16키(`structure_id`·`domain_id`·`type_id`·`priority_rank`·`order_key`·`module`·`role_projection`·`admission_policy`·`routes`·`route_count`·`evidence_refs`·`meeting_clause_refs`·`calculation_request_refs`·`source_bo_ids`·`review_codes`·`review_required`) | 신설 | +| 최상위 필수 | 래핑 없음 + `status` | + `expected_runnable_domain_ids`·`theta`·`quality_gate`·`created_by` | 확장 | + +**실패의 형태가 두 겹이다.** 403–410행은 **전부 통과한다**. 407행 `_as_list(doc.get("legal_effect_structures"))`가 없는 키에 `[]`를 **조용히 반환**해 구조가 0건으로 접히고, 경성 실패는 **411–413행**에서 비로소 난다. 색인 검사가 없었다면 **"완주했는데 구조 0건"**이 되었을 것이다. + +### 1.2 [필수] 구 signal 3종 뿌리 이름 읽기 → 정본 집합 이관 (C-7 폐쇄) + +구본 F0는 `actio_case_signals.json`·`case_liability_signals.json`·`legal_effect_signals.json` **3종을 뿌리 이름으로 읽는다**(82–84행 `INPUT_FILES` · 550행 dispatch · 1114–1116행 pack). Part 2 v.8 3203–3204행이 "Part 3·4는 `downstream_read_sets`가 선언한 정본 집합으로 옮겨야 한다 / 그 이관은 Part 3·4 개정의 몫"이라고 명시한다. + +정본 집합은 `signals/compiler/signal_compiler.py` **166–170행**(part4_F0 = **168행**): + +```python +"downstream_read_sets": { # 166 + "part3_L0": ["SG-02", "SG-05", "SG-07", "SG-11", "SG-13"], # 167 + "part4_F0": ["SG-04", "SG-06", "SG-07", "SG-10", "SG-11"], # 168 + "stage2": ["ALL"], # 169 +}, # 170 +``` + +`stage_1_update_strategy.md` 163행 `signal_manifest(SG-04·06·07·10·11)`와 **글자까지 일치**한다. + +| SG | 파일명 | 쓰임 | 비고 | +|---|---|---|---| +| SG-04 | `governing_law_version_signals.json` | `calculation_requests[].law_version_refs[]` | **버전 확정 판단 금지**(§4.5) | +| SG-06 | `timeline_notice_condition_signals.json` | 통지 lifecycle **보조 참조** | 게이트 원천 아님(§1.4) | +| SG-07 | `asset_right_state_signals.json` | 자산·권리 상태 **보조 참조** | **레코드 0이 정상** — 인계면 행 39 인접 SG-07 행 `optional: true`, note `"SG-07 — 레코드 0 이 정상. SG-02 와 같은 처리."` | +| SG-10 | `evidence_proof_conflict_signals.json` | 증거력 산정 **근거 참조** | 값 자체는 정하지 않음(§4.6) | +| SG-11 | `calculation_requirements.json` | `calculation_requests[]` | 조인 키 `signal_id` | + +**SG 5종은 게이트의 입력이 아니다.** 게이트 입력은 §1.4가 정한다. + +### 1.3 [치명] 게이트의 사실 원천은 BO 페이로드다 + +구 게이트 3종의 스캔 함수(구본 768·809·825행)는 **전부 `_domain_payload_compact(bo_by_id, row)`에서 읽는다.** + +``` +768 def _possession_conflict_scan(rows, bo_by_id): +772 possession = _as_dict(_domain_payload_compact(...).get("possession_state")) +809 def _notice_receipt_gate_scan(rows, bo_by_id): +811 notice = _as_dict(_domain_payload_compact(...).get("notice_lifecycle")) +825 def _lien_extinction_notice_scan(rows, bo_by_id): +830 notice = _as_dict(payload.get("notice_lifecycle")) +138–140 DOMAIN_PAYLOAD_KEEP_KEYS = ["money_claim_details","possession_state","lien_state", + "notice_lifecycle","asset_alias","fractional_effect_events"] +``` + +BO 최상위는 닫힌 18키(`ALLOWED_TOP_LEVEL`)이고 `extensions`는 `domain_payload` 단일 키만 허용된다(Part 2 v.8 1954–1959·2411행). 골든 BO 실측에서 payload 키는 `possession_state` **키 존재 13건 · 비공백 8건**, `notice_lifecycle` **9건/9건**이다. 게이트가 보는 것은 비공백분이다(구본 394행이 `None`·`{}`·`[]`를 걸러낸다) — **"선언 존재"와 "실측 비공백"을 따로 적는다**(§7.5-4). + +**주의** — X1 사이드카의 `input_fields`가 적은 `extensions.domain_payload.payload`는 **실물과 한 단 어긋난다**(실제는 `extensions.domain_payload.<키>`). 사이드카의 경로 표기를 그대로 코드에 옮기지 않는다. + +### 1.4 [치명] dict 게이트의 판정 입력 — 봉투가 정본이고 인계면에 이미 있다 + +사이드카 dict `condition`은 사실을 재판정하지 않고 **상태·코드를 집계**한다. 그 입력은 BO에 없다 — BO 18키에 `review_items`가 없고, LES가 나르는 검토 코드는 `structure_index_compiler.py` 25·27행의 **두 개뿐**(`UNREGISTERED_EFFECT_TYPE_REVIEW`·`PART3_REGISTRY_SIDECAR_MISMATCH`)이다. + +**정본은 도메인 봉투다.** `signals/schemas/domain_signal_envelope.schema.v2.json` 루트 `domain_signal_envelope`의 **필수 13키**에 `evidence_slot_status`와 `review_items`가 들어 있다. + +**그리고 이 파일은 인계면 선언표에 이미 있다**(0-based index 39 = 40번째 행): + +```json +{"path": "signals/domain_signals/.json", "path_is_pattern": true, + "root_key": "domain_signal_envelope", "schema_version": "stage1_domain_signals.v2", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", "optional": true, + "consumer_tasks": ["Task_C_LE_L0_structure_seed_reducer"], + "note": "expected_runnable 도메인 중 실재하는 것만 읽는다 — 부재는 영수증 계수."} +``` + +**Part 4는 신설이 아니라 소비자 한 줄 추가로 입력을 연다.** 같은 방식으로 행 26 `quality_gates/stage1_part2_review_handoff.json`(P2-R0 생산 · F0 공동 기록)도 소비자를 추가한다. + +**`downstream_read_sets.part4_F0`와의 관계** — 그 선언은 **SG 정본 5종**의 read set이지 Part 4 입력 전체가 아니다. 봉투와 P2 handoff는 SG가 아니므로 그 목록 밖에서 인계면 선언으로 계약된다(Part 3 L0도 봉투를 같은 방식으로 읽는다). + +### 1.5 [최대 발견] 게이트 사이드카 25개가 조립본에 실재한다 — dangling 0 + +registry는 25개 도메인에 **게이트 121개**를 선언하고(EC-00은 0), 각 게이트는 `{gate_id, implementation_ref, review_codes[], blocks_final_drafting}` 4키다. + +**배포 원본 트리에는 그 25개가 없다. 조립본 트리에는 있다.** + +``` +선행구축/Default_Agent_Stage_1/domains/*/gates.json → 25개 · 81,430 B · 127규칙 +registry에만(dangling): 0 ← registry 121 전건 커버 +사이드카에만(고아): 6 ← E14-G04·E17-G04·E18-G04·E19-G04·E20-G04·E21-G04 +정규화 후 blocks 불일치: 0 (미판정 0) +발화코드 ⊄ registry review_codes: 2 ← E18-G03·E20-G03 (DEFERRED_CALCULATION_TRACK_REVIEW) +``` + +**`implementation_ref`는 dangling이 아니라 미배포다.** 문제의 성격이 "판정식이 없다"가 아니라 "복사가 안 됐다"이며, 후자는 **Part 3가 이미 해결한 유형**이다 — Part 3 분석서 §6.2: "복사 배포 — registry 사이드카 2종 ×25(합 50 파일 · 39,254 B) … 조립본의 동명 파일을 **바이트 동일 복사**만 했다(내용 수정 0 — 방언 통일은 registry 소유자 몫)." + +#### 1.5a 사이드카 방언 지도 — 17축 (정규화 리더 필수) + +| # | 축 | 방언 · 건수 | +|---|---|---| +| 1 | 배열 키 | `rules` 16 · `gates` 9 (파일 수) | +| 2 | `schema_version` | 6종 (`stage1_s2_domain_gates.v1` 4 · `stage1_domain_gates.v1` 9 · `s2_domain_gates.v1` 4 · `s3_domain_gates.v1` 2 · `stage1_s3_domain_gates.v1` 2 · `stage1_s6_domain_gates.v1` 4) | +| 3 | `condition` 형 | **dict 37 · str 90** | +| 4 | `condition` dict 키 | **6종** — §1.5b | +| 5 | 발화 코드 키 | `emitted_codes` **37** · `emitted_review_codes` **48** · `review_code` **42** (합 127) | +| 6 | 효과 컨테이너 | `writer_effect` **85** · `row_effect` **48** · `on_true` **42** | +| 7 | 차단 플래그 경로 | `writer_effect.final_drafting_block` **37** · `writer_effect.blocks_final_drafting` **48** · `on_true.blocks_final_drafting` **42** (합 127) | +| 8 | 파일 status 키 | `closure_status` 16 · `status` 9 | +| 9 | `global_status_rule` | 10파일 · **서로 다른 3값** — 동결 계약 값과도 다르다 | +| 10 | 발화 시 status 키 | `writer_effect.status_when_triggered` **37** · `writer_effect.status_on_fire` **48** · `on_true.writer_status` **42** (합 127) — §3.2 전역 status 규칙의 직접 입력 | +| 11 | 평가 방식 키 | `evaluation_mode` **37** · `implementation_type` **48** · `evaluation` **42** (합 127) | +| 12 | row 효과 키 | `row_effect.must_consider` **48** · `on_true.preserve_seed` **42** · 나머지 37건은 row 효과 없음 — **합 90/127이 행 효과를 갖는다**(§4.4 투영 ②③) | +| 13 | `writes_fact_ledger` · `unknown_or_missing_policy` | 전자는 `writer_effect` **85건에만**(= `on_true` 42건에는 부재) · 후자는 **48건에만** | +| 14 | 입력 선언 키 | `input_fields` **37** · `input_contracts` **48** · 나머지 42건은 선언 없음 — **§4.2 F0-7b가 손으로 고른 4재료를 쓸지, 이 선언을 읽을지 정책이 정한다** | +| 15 | `silent_repair_forbidden` | **37건에만** | +| 16 | `source_membership_required` | **42건에만** — 발화 가부에 걸릴 수 있는 미측정 플래그다 | +| 17 | `implementation_type` / `evaluation_mode` / `evaluation` | 축 11과 같은 3방언(37/48/42) — 리더가 하나로 정규화한다 | + +**축 6의 합은 배타가 아니다** — `writer_effect` 85 = 37+48이고 `row_effect` 48은 그 48과 겹친다. 배타 분할은 `writer_effect ∪ on_true` = 85+42 = **127**이다. + +**축 5·6·7이 결정적이다.** `writer_effect`만 보는 리더는 `on_true` 계열 **42규칙의 발화코드와 차단 표지를 못 읽는다.** 그 42에 **`E10-G05`·`E10-G06`이 들어 있고**, 둘은 §7.2 G-j(필수)의 등가 판정 대상이다. 실측 예: + +```json +E10-G05 {"review_code": "POSSESSION_CAUSE_CONFLICT", + "on_true": {"preserve_seed": true, "writer_status": "READY_WITH_REVIEW", + "blocks_final_drafting": true}} +``` + +`writer_effect`만 읽으면 "blocks 불일치 0"이 **미측정 42건 위의 공허한 0**이 된다. 17축 리더로 재야 "**정규화 후 121/121 판정 · 미판정 0**"이라는 참인 0이 나온다. + +**축을 섞어 읽지 않는다** — `E10-G05`가 `review_code`+`on_true` 계열인 것은 축 5·6·7의 값이며, 그 게이트의 **축 3(`condition` 형)은 str이다**(§1.6). 방언 지도와 게이트별 표는 반드시 같은 축 이름으로 조인한다. + +MEMORY.md 교훈 — "**방언이 갈린 자료에서는 (1) 모든 방언을 읽는 정규화 리더를 먼저 만들고 (2) 그 리더로 잰 값만 근거로 삼으며 (3) 세지 않은 필드를 공통 필드로 승격하지 않는다.**" + +#### 1.5b `condition` dict 6종 — 구현 가능성 · 입력 도달성 · 정의처 배포는 **서로 다른 축이다** + +| `condition` 키 | 건수 | 입력 정본 | 정의처 배포 | **도메인 귀속 후 도달**(골든-S2 / 최량 픽스처) | 이번 회차 | 해소 이월 | +|---|---|---|---|---|---|---| +| `any_review_code_present` | 13 | 봉투 `review_items` + P2 handoff `source_review_code`(도메인 귀속 후 — §4.2 F0-7b) | **배포됨** | **0 / 0** — 봉투 distinct 11도메인 전건 공백 | **구현** · 발화 0 계수 | §9-3 | +| `required_slots_missing_or_conflicting` | 6 | 봉투 `evidence_slot_status`(`missing`·`conflicted`). 슬롯 항목 스키마에 `review_code` 필드가 있어 **발화 코드를 슬롯 자신이 나른다** | **배포됨** | **0 / 0** — 봉투 distinct 11도메인 전건 공백 | **구현** · 발화 0 계수 | §9-6 | +| `calculation_operand_state_in` | 6 | SG-11 레코드 `details.operand_state` — **Part 2가 산출**(D-P4-6) | **배포됨** | **0** / **0** — 값은 distinct 16레코드 중 6건 존재하나 `domain_ids`가 전부 `E-05`이고 게이트는 E-14~21에 산다. **도메인 귀속 후 도달 0**(재라벨 합성본 `SUPP-calc6` 필요) | **구현** · 골든 발화 0 | §9-13 | +| `boundary_or_overlap_detected` | 6 | `boundary_routes`(E-14·17·18·19·20·21) · `track_boundary_candidates`(X3) · `boundary_domain_candidates`(E-07·08) | **미배포** — 형태는 `domains//domain_extension.schema.json`, 의미는 `domains//Stage2_handoff_contract.json`, **둘 다 조립본 전용** | 0 / 0 — 골든 BO payload 0건 | **가시화** `GATE_CONDITION_KEY_UNSUPPORTED_REVIEW` | §9-5 · §9-10 | +| `procedure_only_or_final_conclusion_attempted` | 2 | — | — | — | **고아**(registry 밖) | §9-2 | +| `final_conclusion_or_missing_profile_attempted` | 4 | — | — | — | **고아** | §9-2 | + +**`boundary_or_overlap_detected` 6건을 구현하지 않는 이유 — 정의처가 배포 트리에 없다.** 조건 값은 도메인 ID 목록이지만(`E14-G02 → ["E-02","E-09","E-10","E-13","X3"]`), 그 의미는 `domains/E-18/Stage2_handoff_contract.json`의 `conditional_boundary_handoffs[].activation = "source-backed overlap only"`(**근거 있는 중첩**)이고 입력 형태는 `domain_extension.schema.json`이 정한다. **두 파일 모두 배포 트리에 없다.** 정의처 없이 술어를 쓰면 그것이 곧 판정식 창작이며, D-P4-b가 str 90건을 미루며 쓴 논리("술어 명세를 코드로 옮기는 것은 창작")와 같은 기준을 적용하면 이 6건도 미룬다. 단순 공활성 교집합으로 읽으면 복합 사건에서 매 행 발화하고 6건 전부 `blocks_final_drafting: true`이므로 **차단 표지를 대량 오발**한다. + +**`any_review_code_present`의 코드 통로는 둘이다.** slice `domain_declarations`(`domain_slice_compiler.py` 277행)에는 `review_codes`가 없다 — 즉 **registry가 선언한 도메인 검토코드 어휘(`domain_config.review_codes`)는 워커에 전달되지 않는다.** 그러나 같은 컴파일러 447–450·497행이 `part1_review_guard.review_item_ids`로 **Part 1 활성 매니페스트가 실제로 발행한 인스턴스 코드**를 전달한다. 통로가 아주 없는 것이 아니라 **registry 어휘 통로가 없다**. §9-3의 처방은 두 통로 중 어느 쪽을 열 것인가의 결정을 포함한다. + +**registry 기준 분해** + +``` +registry 121 = registry∩dict 31 + registry∩str 90 +registry∩dict 31 = any_review 13 + required_slots 6 + calc_operand 6 + boundary 6 +이번 회차 구현 25 = any_review 13 + required_slots 6 + calc_operand 6 +이번 회차 가시화 96 = str 90 + boundary 6 +사이드카 127 = registry 121 + 고아 6 (고아는 전부 dict · procedure_only 2 · final_conclusion 4) +``` + +**"창작 0"의 정확한 범위** — 구현 **25건**의 판정식은 창작하지 않는다(전부 배포된 정의처 위의 기계적 평가). 그러나 **골든-S2 기준 실발화는 0건**이다. 25건은 구현해 두고 발화 0을 **계수·가시화**한다(침묵 금지) — 상류 보강(§9-3·5·6·13)이 끝나면 코드 수정 없이 발화가 시작된다. + +### 1.6 구 게이트 3종 → registry gate_id 역매핑 (선언 정본 + 교집합 2중) + +**선언 정본** — X1/gates.json `legacy_equivalence_projections`: `LEGACY-GATE-02` → **`X1-G01`**(branch `receipt_missing`, `equivalence: preserved`) · **`X1-G02`**(branch `receipt_evidence_weak`). E-10/gates.json `legacy_gate_projection`: `["LEGACY-GATE-01","LEGACY-GATE-03"]` — **도메인 수준까지만**. (비어 있지 않은 legacy 투영은 이 **두 파일뿐**이다. 나머지 6파일은 빈 배열.) + +**교집합 보완 + 타이브레이크** — 동결 계약 `emitted_codes` ∩ registry `review_codes`만으로는 `POSSESSION_CAUSE_CONFLICT`의 귀속이 **2후보로 열린다**(사이드카 발화원이 `E10-G05`·`E11-G01` 둘이고, 동결 계약 `registry_successor_candidates`는 `["E-11","E-10","X2"]`). 결정론 규칙은 다음과 같다 — **부수 코드 `LEGAL_CAUSE_STATE_UNKNOWN_REVIEW`의 발화원이 `E11-G01` 단일이므로 그것을 먼저 고정하고, 잔여 코드를 나머지 후보 중 실제 발화원인 `E10-G05`에 귀속시킨다. `X2`는 두 코드 어느 쪽의 발화원도 아니어서 교집합에서 탈락한다.** + +| 구 게이트 | 발화 코드 | registry 대응 | blocks | 근거 | 축3 `condition` 형 | 축5·6 발화/효과 키 | +|---|---|---|---|---|---|---| +| LEGACY-GATE-01 | `POSSESSION_CAUSE_CONFLICT` | **E-10 `E10-G05`** | true | 교집합 | **str** | `review_code`+`on_true` | +| 〃 (부수) | `LEGAL_CAUSE_STATE_UNKNOWN_REVIEW` | **E-11 `E11-G01`** | false | 교집합 | **str** | `emitted_review_codes`+`writer_effect` | +| LEGACY-GATE-02 / receipt_missing | `NOTICE_RECEIPT_UNVERIFIED` | **X1 `X1-G01`** | true | **선언 정본** | **dict**(`any_review_code_present`) | `emitted_codes`+`writer_effect` | +| 〃 / receipt_evidence_weak | `NOTICE_RECEIPT_EVIDENCE_WEAK_REVIEW` | **X1 `X1-G02`** | false | **선언 정본** | **dict**(`any_review_code_present`) | 〃 | +| LEGACY-GATE-03 | `LIEN_ASSERTED_AFTER_EXTINCTION_NOTICE_REVIEW` | **E-10 `E10-G06`** | false | 교집합 | **str** | `review_code`+`on_true` | + +**5개 중 3개(`E10-G05`·`E10-G06`·`E11-G01`)가 str `condition`이다** — §4.4 가시화 갈래로 간다. 따라서 이번 회차의 **발화 등가 대조 대상은 `X1-G01`·`X1-G02` 2개**이고, 나머지 3개는 `GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW` **가시화**를 확인한다. + +**등가 주장의 정확한 범위** — 이 회차가 발화 등가를 주장할 수 있는 것은 **LEGACY-GATE-02의 두 갈래(`X1-G01`·`X1-G02`)뿐**이고, 그것도 **합성 코드 주입 하에서만**이다(§7.2a — 두 게이트는 `any_review_code_present` 부류라 골든-S2 봉투가 비어 실입력으로는 발화하지 않는다). LEGACY-GATE-01·03은 술어 명세 전환(§9-4) 전까지 발화하지 않는다. + +**그리고 골든 등가가 두 행에서 깨진다.** 골든-FL 실측: + +``` +F-008 bh16 ['NO_EVIDENCE_REFS'] +F-011 bh19 ['LEGAL_CAUSE_STATE_UNKNOWN_REVIEW'] +F-014 bh21 ['LEGAL_CAUSE_STATE_UNKNOWN_REVIEW'] +``` + +`LEGAL_CAUSE_STATE_UNKNOWN_REVIEW`는 registry상 `E11-G01`(str `condition`)의 발화 코드로 선언돼 있으나, **골든의 실제 발화는 그 선언 조건이 아니라 구본 778–779행의 `if events and not causes` 갈래에서 나왔다**(발화 2행 bh19·bh21의 `legal_cause_assertions`가 0건이라 '쌍'이 성립하지 않는다). 이번 회차는 str을 가시화만 하므로 **F-011·F-014의 `skeleton_validation_codes`에서 사라진다.** `skeleton_validation_codes`는 구 27필드의 하나이고 §7.2a가 등가 판정 기준으로 지정한 필드이므로, 이는 **필수 회귀 G-r의 확정 차분**이다 → `EXPECTED_DELTA-03`로 착수 전 등록한다(§7.1). 등록하지 않으면 G-r이 원인 불명으로 실패한다. + +**차단 표지 신설 범위** — `X1-G01`의 `blocks_final_drafting: true`는 동결 계약 `LEGACY-GATE-02.receipt_missing`의 `final_drafting_block: true`와 같은 의미다. 그러나 **registry 121 중 `blocks_final_drafting: true`가 60건**이고 동결 계약이 차단을 선언한 것은 그 하나뿐이므로, registry 상 blocks=true는 60건이지만 **이번 회차 발화 가능 상한은 21건**이다(가시화 대상 boundary 6 · str 32 제외 · `X1-G01` 차감 — §7.1). `EXPECTED_DELTA-02`는 단건이 아니라 **그 범위 전체**로 등록한다. + +**차단 의미의 명문화** — `blocks_final_drafting: true`는 **Stage 1 기록을 막지 않는다.** Fact Ledger는 기록되고 `final_writer_status`는 `READY_WITH_REVIEW`가 되며, 차단 표지는 Stage 2 최종 기안 단계로 전달된다(사이드카 `writes_fact_ledger: true`가 확증). + +### 1.7 [구조] 고정 필드 목록 이중 정의 · 모듈·역할 상수표 + +`FINAL_FIELDS` 27필드가 구본 **96행(F0)과 1394행(F2)** 두 곳에 리터럴로 있다. → 정책 자산 한 곳이 유일 정본이 된다(정적 검사 11이 강제). + +구본 104–127행의 `STRUCTURE_TYPE_TO_MODULE`(10키)·`STRUCTURE_TYPE_TO_ROLE`(10키)은 동결 계약 `structure_projection_contract.json`(`FROZEN_WITH_REVIEW`)이 보존한다 — `part3_canonical_type_count: 11` ↔ `part4_module_key_count: 10`, **`actio_remedy_or_cap` 1건 누락**, 런타임 효과는 `dict.get` 기본값 `legal_effect`/`legal_effect_roles`. + +| 축 | 구본 | registry | 침묵 흡수 | +|---|---|---|---| +| `role_projection` 값 | **5종** | **50종** | 45종 | +| `type_id` 키 | **10키** | **56종** | **46종** | + +**세 번째 상수표도 삭제 대상이다** — 구본 677행 `_legal_calculation_object`가 `{money_claim, secured_debt, land_use_gain, valuation, fraudulent_transfer}` **5-원소 집합**으로 `calculation_required`를 정한다. 이 집합은 (나) 필드 `legal_calculation_object`의 원천이자 **F1의 유일 생존 부류 `legal_calculation_required`의 1차 트리거**다. 5종 전부 registry `type_id`에 실재한다. **다만 "`calculation_bindings` 비공백 도메인의 `type_id` 집합"으로 대체하면 안 된다** — 실측상 그 도메인이 **25개(게이트 보유 전건)**이고 그 `type_id` 합집합이 **56종(registry 전건)**이라 `needs_calc`가 **항등식으로 퇴화**한다(골든은 40/59가 True). 항등식이 되면 구본 899–925행 트리거가 v.8 BO에서 `amount` 후보 2개(`bo.amount`·`core.Amount` 양쪽이 채워진다)로 성립해 **pack이 폭주**하고, `max_iterations: 1`·flash-lite·`PACK_ITEM_CHAR_LIMIT` 전제의 F1이 무너진다. + +**대체 규칙은 좁게 잡는다** — "해당 도메인 `calculation_bindings`에 등재된 계산 ID와 실제로 연결된 `structure_types` 레코드의 `type_id`". 대체가 어려우면 **동결 계약처럼 5원소를 정책 자산에 옮겨 적는다**(창작이 아니라 승계다). 어느 쪽이든 **대체 전후 `calculation_required` 분포를 26-B에서 실측해 `EXPECTED_DELTA`에 등록**한다. 정적 검사 11의 강제 범위에 넣는다. + +**처방** — 상수표 **셋**을 삭제하고 `domain_config.structure_types[].module`·`.role_projection`·`calculation_bindings`를 직접 읽는다. E-13이 registry에 `{"type_id":"actio_remedy_or_cap","priority_rank":9,"module":"actio","role_projection":"linked_actio_structures"}`로 선언돼 있어 **누락이 자동 해소된다** → `EXPECTED_DELTA-01`. registry `structure_types` 73레코드 전건이 `module`·`role_projection`을 선언한다(결손 0). + +### 1.8 [승계] 행 카디널리티와 `fact_id` + +골든 실측 — 래핑 없는 배열 · **59행** · 모든 행이 같은 **27키**(합집합=교집합, 순서 일치) · `fact_id` **`F-001`…`F-059`** · `source_bo_id` `bh1`·`bh10`·`bh11`…(사전순). + +``` +700 "fact_id": f"F-{idx:03d}", +1904 if fact_ids != [f"F-{i:03d}" for i in range(1, len(final_rows)+1)]: +1905 raise ValueError("fact_id sequence must be continuous and unique") +``` + +**`fact_id`는 BO_ID 파생이 아니라 1-기반 연속 서수이며 연속성이 경성 단언으로 강제된다.** 바꾸지 않는다 — 바꾸면 27필드 중 하나가 59행 전부에서 달라지고, pack 항목·판정 decision·LES 검토 승계가 전부 `fact_id`를 정체성 키로 인용하므로 F2가 hard fail로 처리한다(구본 1343행). + +**행의 원천은 BO다** — 구본 1123행 루프가 BO 1건 = 1행임을 확증한다. Part 3 v.8에서 LES 레코드 수는 registry 선언 합으로 바뀌었으므로 "LES 59 = Fact Ledger 59"는 더 이상 성립하지 않는다. + +### 1.9 [상류 이탈 봉인] `by_claim_form`은 만들어지지 않았다 + +상위 지시 158·294행은 Part 3 산출에 `by_claim_form`을 요구했으나 **Part 3 v.8은 만들지 않았다** — D-4 (라)로 `by_module`을 채택했다. Part 4는 최종 소비자이므로 여기서 봉인한다 — **`by_claim_form`을 기대하는 코드를 쓰지 않는다.** + +--- + +## 2. 설계 결정 (D-P4-1 ~ D-P4-7) + +### D-P4-1 — 신작. 산출 YAML은 구본을 참조하지 않는다 + +**제약 1의 정확한 의미** — 금지되는 것은 **런타임 의존**(산출 YAML이 `Claude_YAML` 경로·구 signal 이름·구 상수를 읽거나 인용)이며, 정적 검사 8이 "산출 YAML 안 `Claude_YAML` 문자열 0"으로 강제한다. **이 전략 문서가 구본 행번호를 인용하는 것은 근거 제시**이지 런타임 의존이 아니다. + +| 승계 대상 | 경로 | 근거 | +|---|---|---| +| 게이트 조건·발화·writer 효과 | **자산 경유**(배포하여 런타임이 읽음) | 조립본 `domains//gates.json` 25 | +| 구 게이트 3종 ↔ registry 역매핑 | **동결 문서 + 자산 선언** | `deterministic_gates_contract.json` · X1/E-10 사이드카 | +| 모듈·역할 투영과 누락 1건 | **동결 문서** | `structure_projection_contract.json` | +| 27필드 목록·자료형·순서·`fact_id` 서식 | **골든 fixture 실측** | `run_1/.../Fact_Ledger_base.json` | +| F1 설정값(모델·TTL·max_iterations) | **구본 문헌 인용**(런타임 의존 아님) | 구본 1241–1254행 | +| `FX-{n:03d}` · `PACK_ITEM_CHAR_LIMIT` | 〃 | 구본 989 · 95행 | +| 판정 파일 수용 status 3값 | 〃 | 구본 1666행 | + +뒤 세 줄은 값을 **새 정책 자산에 옮겨 적는 것**이며, 산출 YAML은 구본을 읽지 않는다. + +### D-P4-2 — task 3개. F1을 유지하되 판정 부류는 1종으로 줄어든다 + +구본 판정 부류 3종(128–137행)을 신설계에서 재평가한 **실측 결과**: + +| `exception_type` | review_code | v.8에서 발생 가능? | +|---|---|---| +| `legal_calculation_required` | `CALCULATION_BASIS_INCOMPLETE_REVIEW` | **가능** | +| `unresolved_asset_cluster_projection` | `ASSET_CLUSTER_PROJECTION_GAP_REVIEW` | **불가** — `asset_cluster_ids`·`by_asset_cluster_id`가 Part 3 v.8에서 소멸(§1.1). P2 v.8·P3 v.8·LES 스키마·컴파일러에 `asset_cluster` 0회 | +| `unresolved_structure_role_projection` | `STRUCTURE_ROLE_PROJECTION_GAP_REVIEW` | **불가** — §1.7 처방 적용 시 registry가 73/73 전건 선언(결손 0) | + +**F1은 유지한다.** 전략서 §5.2가 "무변경"으로 못박았고, 남은 1종(`legal_calculation_required`)이 계산 완전성 판정이라 LLM 판정 가치가 가장 큰 부류다. 다만 **pack이 상시 1부류만 담는다는 사실을 §7.2 G-o 기대에 반영하고**, 발생 불가 2종은 **코드에서 제거하지 않고** 정책 자산에 `unreachable_in_v8: true`로 표기해 되돌림 경로를 남긴다(Part 3가 L1 자리를 비워 둔 것과 같은 규율). + +이번 회차 F1 변경은 하나 — **허용 출력 필드에 `domain_effects` 하위 키 경로 표기 추가**. + +### D-P4-3 — 로직은 모듈 하나, 규칙 선언은 정책 하나 + +Part 3 문법 승계. **게이트 구현표의 거처는 정책 자산 하나다** — 정책이 `implemented_condition_keys[]`(어떤 `condition` 키를 평가할 수 있는가)와 차단 의미·방언 정규화 규칙을 선언하고, 모듈은 그것을 읽어 dispatch한다. **gate_id를 코드에 열거하지 않는다**(정적 검사 12). + +### D-P4-4 — 행 스키마 일반화: 구 4필드 → `domain_effects` + `calculation_requests` + +`money_claim_effect`·`commercial_successor_effect`·`actio_roles`·`linked_actio_structures`를 `domain_effects{도메인ID}`와 `calculation_requests[]`로 대체하고 **구 4필드는 한 버전 동안 별칭으로 병기**한다. + +**P0 판정 C** — "기존 키의 값은 등가로 두고 신규 키만 더한다. `required`에 올리지 않는다." 결과 행 **29키**. 구 27키 중 **BO 유래 9필드만 값 등가**를 요구한다(§7.1a). + +**Stage 2 호환** — `additionalProperties: false`는 **Part 4 자기 산출 검증에만** 적용된다. Stage 2는 필요한 키만 읽으므로 27→29는 파괴적이지 않다(`registry_component_ids`·`hash_kind` 선례). 신규 2키를 `required`에 올리지 않는 것이 조건이다. + +### D-P4-5 — 게이트: 사이드카를 배포하고 17축 방언은 정규화 리더가 읽는다 + +§1.5가 근거. 세부 §4.4. + +### D-P4-6 — Stage 1은 operand 완전성 검증까지 · `operand_state`는 **Part 2가 산출하고 Part 4는 승계한다** + +`ensemble_v2.md` §3.1·§6-7. **금액을 계산하지 않는다.** + +**권한 경계가 자산에 명문화돼 있다** — `platform/schemas/client_goal_domain_profiles.schema.json` `$comment` (7): "`operand_state_not_emitted` — 이 층은 `operand_state` 를 산출하지 않는다. `s4_calculation_input.schema.json` 의 `ready|partial|blocked|deferred` 를 유도할 **책임은 Part 2 에 있다.**" + +따라서 **Part 4는 `operand_state`를 재산정하지 않는다.** SG-11 레코드의 `details.operand_state`를 그대로 승계해 `calculation_requests[]`에 싣는다(`signals/adapters/s4_calculation_adapter.py` 22행이 `operand_state`를 항목 최상위에 normalize하고, `signals/emitters/emitter_runtime.py` 102–103행이 그것으로 item status를 유도하며 `details`에 원본을 보존한다). 실적재는 **전 트리 합계 80레코드 중 30건 · distinct 16레코드 중 6건 · 골든-S2 0레코드**다(§0.1 분모 규약). + +`deferred`는 registry `calculation_registry_bindings[].runtime_status` 선언값이지 Part 4가 정할 값이 아니다. + +승계된 `operand_state`가 `calculation_operand_state_in` 게이트 6건의 입력이 된다(§1.5b). **순서 계약** — F0-5(SG-11 조인·승계)가 F0-8(게이트 실행)보다 앞선다(§4.2). + +### D-P4-7 — 별칭 해소는 다중 타깃과 자기모순 1건을 함께 처리한다 + +`legacy_alias_targets`는 구 이름(B1~B5)을 registry ID **목록**으로 옮긴다(`B1_Money_Successor → ["E-02","E-03"]`). 같은 자산이 **자기모순 1건을 기록**해 두었다: + +```json +"legacy_declaration_mismatches": [ + {"domain_id": "E-09", "legacy_domain_id_declared": "B2_Secured_Registry", "alias_targets": ["E-10"]} +] +``` + +**불일치의 방향을 정확히 읽어야 한다.** 별칭표의 **키는 구 이름**(B1~B5)이고 값이 registry ID이므로 `E-09`는 애초에 입력이 아니다 — "E-09가 E-10으로 옮겨지는" 경로는 **존재하지 않는다**. 실제 불일치는 정반대다: + +``` +자기선언 → alias_targets 차집합 (배포 structure_types.json 전수, 방언 2키 정규화) + B1_Money_Successor ['E-02','E-03'] alias ['E-02','E-03'] 누락 [] + B2_Secured_Registry ['E-09','E-10'] alias ['E-10'] 누락 ['E-09'] + B3 ['E-04','E-11','X2'] alias(B3_Land_Valuation) 누락 ['X2'] + B4 ['E-13'] alias(B4_Actio) ['E-13'] 누락 [] + B5 ['E-15','X1','X3'] alias(B5_…) ['E-10','E-15'] 누락 ['X1','X3'] + → **누락 4건** (E-09·X2·X1·X3) +역방향 과잉: alias_targets["B5_…"] 의 E-10 은 B5 를 자기선언한 적이 없다(B2 선언) + +자산의 legacy_declaration_mismatches 기록 : 1건(E-09) ← **파생 목록이며 원본이 아니다** +자기선언 값의 방언 : 완전명 2종(B1_·B2_) · 약칭 3종(B3·B4·B5) +``` + +즉 **`alias_targets`가 자기선언 집합을 덮지 못한다.** 그 결과 `source_domain == "B2_Secured_Registry"`인 BO는 **E-09를 잃고 E-10으로만 귀속된다** — 골든 BO에서 **10건**(`bh9`~`bh18`)이 그 경우다. + +**F0-0b의 처방** — ① `structure_types[].legacy_source`·`legacy_source_domain`을 **전수 훑어**(약칭↔완전명 정규화 포함) 자기선언 집합을 구성하고 별칭표와 **양방향 차집합**을 계산한다. 자산의 `legacy_declaration_mismatches`는 **파생 목록**이므로 대조용으로만 쓰고 불일치 시 `LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW`를 별도 계수한다. 이어 ② `alias_targets`에 없는 자기선언 도메인을 ② **해소 결과에 추가할지 말지를 정책 자산이 선언**하며(추가하면 활성 집합이 넓어지고 넣지 않으면 그 도메인의 게이트·효과가 통째로 빠진다), ③ 어느 쪽이든 `LEGACY_ALIAS_SELF_MISMATCH_REVIEW`에 **누락 도메인 ID와 영향 BO 수**를 계수한다. 침묵 탈락을 금지하는 것이 핵심이다. + +**137종 원칙과의 관계** — 별칭표는 **사건 종류 이름이 아니라 구 도메인 코드**를 registry ID로 옮기는 마이그레이션 사상이며 자산이 용도를 한정한다. 사건 이름 런타임 조회는 `runtime_case_name_lookup_forbidden: true`(s5 계약)로 별도 금지되고 정적 검사 8이 확인한다. + +--- + +## 3. 확정 계약 + +### 3.1 행 스키마 (29키 = 구 27 + 신규 2) + +구 27키는 골든에서 **순서까지 실측**한 것이며 이름·자료형·순서를 바꾸지 않는다. + +``` +fact_id · source_bo_id · type · date · parties · object_spec · amount · action · +evidence_refs · credibility · legal_centrality · proof_strength · legal_effect_roles · +claim_chain_ref · linked_structures · must_not_drop_in_claim_types · must_consider · +state_context · money_claim_effect · commercial_successor_effect · actio_roles · +linked_actio_structures · downstream_module_candidates · skeleton_validation_codes · +derived_fact · derivation_basis · legal_calculation_object +``` + +`fact_id` = `F-{idx:03d}`, 1-기반 연속(§1.8). + +| 신규 키 | 형태 | 규칙 | +|---|---|---| +| `domain_effects` | `{도메인ID: {module, role_projection, structure_ids[], type_ids[], review_codes[]}}` | 키 집합 ⊆ SG-01 활성 도메인(F2 검사). **색인은 ID만, 본문 복제 금지** | +| `calculation_requests` | `[{calculation_id, request_id?, operand_state?, missing_operands[]?, law_version_refs[]?, provenance?}]` | `calculation_id` 필수. **복수 정상.** **원천 결손 대비** — SG-11 distinct 16레코드 중 `details.calculation_id` 보유는 **6건**뿐이고 스키마가 `details`를 무제약으로 두므로, 결손 시 `signal_id`로 대체하고 `CALCULATION_ID_SUBSTITUTED_REVIEW`로 계수한다. **버리지 않는다** — 계산 보존 등식이 전 레코드를 부착/미부착으로 계수하기 때문이다 | + +### 3.2 어휘 + +| 항목 | 확정 값 | 정의처 | +|---|---|---| +| `operand_state` | `ready`\|`partial`\|`blocked`\|`deferred` | 전략서 §0.3 | +| 입력 칸 `status` | `filled`\|`partial`\|`missing`\|`conflicted` | `signals/_common/evidence_slot_status.schema.json` | +| 최종 `status` | `READY`\|`READY_WITH_REVIEW`\|`BLOCKED`\|`FAILED` | Part 1·2·3 승계 | +| 판정 파일 수용 status | `READY`\|`READY_NO_EXCEPTIONS`\|`READY_WITH_BLOCKS` | 구본 1666행 | +| 도메인 ID 패턴 | `^(?:(?:EC\|E)-[A-Z0-9]{2,8}\|X[A-Z0-9]{1,8})$` | Part 3 승계 | +| 검토 코드 패턴 | `^[A-Z][A-Z0-9_]{2,127}$` | 〃 | +| gate_id 패턴 | `^[A-Z0-9]{1,4}-G[0-9]{2}$` | 사이드카 127건 전수 적합 | +| **발화코드 대조면** | **`gates[].review_codes`(게이트 단위)** — 이 면에서 초과 **2건**(`E18-G03`·`E20-G03`의 `DEFERRED_CALCULATION_TRACK_REVIEW`). **`domain_config.review_codes`(도메인 어휘) 면에서는 0건**(E-18·E-20이 그 코드를 도메인 어휘에 선언한다). `GATE_EMITTED_CODE_NOT_IN_REGISTRY`는 **게이트 단위 면**을 쓴다 — 도메인 면을 쓰면 §9-7·G-k4가 사라진다 | 두 면 전수 대조 | +| 거래 id 패턴 | `^S5TX-[a-f0-9]{20}$` | signal_manifest v2 | +| 예외 id | `FX-{n:03d}` | 구본 989행 | +| 사실 id | `F-{idx:03d}` | 골든 실측 | + +**전역 status 규칙** — blocked item · rejected patch · deterministic review · **LES 승계 검토** · gate review code 중 하나라도 있으면 `READY_WITH_REVIEW`, 아니면 `READY`(동결 계약 `global_status_rule` 승계). + +**"LES 승계 검토"의 정확한 범위** — `structure_index_compiler.py`는 검토 코드 상수를 **7개**(25–31행) 정의하나 `record["review_codes"]`에 실제로 실리는 것은 **2개**다 — `UNREGISTERED_EFFECT_TYPE_REVIEW`(25행)·`PART3_REGISTRY_SIDECAR_MISMATCH`(27행). 나머지 5개는 handoff·audit로 간다. + +**단, `review_required`가 따로 있다** — 레코드 필수 16키의 1급 필드이고, **E-00 특칙**(313–314행)이 `review_codes`를 비운 채 `review_required: True`만 세운다. 따라서 F2 판정은 **`review_codes` 2종 ∪ `review_required` 플래그**로 정의한다. 플래그를 빠뜨리면 G-t("E-00만 활성")에서 검토 신호가 샌다. + +**사이드카 `global_status_rule` 3방언은 채택하지 않는다** — 10파일이 서로 다른 3값을 갖고 동결 계약 값과도 다르다. 전역 규칙은 **동결 계약 값 하나**를 쓰고, 사이드카의 세 값은 정규화 리더가 읽되 **경고로만 기록**한다(§9-1). + +### 3.3 파일 계약 (루트 래핑 전수 명시) + +| 구분 | 경로 | 루트 래핑 | 생산 → 소비 | +|---|---|---|---| +| 진입 | `signals/signal_manifest.json` | 없음 | P2-S0 → F0 | +| 진입 | `routing/domain_activation_manifest.json` (**= SG-01** · `signal_id: "SG-01"` 보유) | `domain_activation_manifest` | P1-D0 → F0·F2 | +| 진입 | `BO.json` | 없음(배열) | P2-F0 → F0·F2 | +| 진입 | `legal_effect_structures.json` | 없음 | P3-L2 → F0·F2 | +| 진입 | SG-04·06·07·10·11 (`signals/` 아래) | 없음 (`schema_version`·`signal_id`·`status`·`records`) | P2-S0 → F0 | +| **진입** | **`signals/domain_signals/.json`** | **`domain_signal_envelope`** | **P2-S0 → F0** (게이트 입력 · §1.4) | +| **진입** | **`quality_gates/stage1_part2_review_handoff.json`** | 없음 | **P2-R0/F0 → F0** (게이트 입력 · §1.4) | +| 진입 | `evidence_indexed.json` | 없음 | `Task_B1_quality_gate_evidence_indexed` → F0 | +| 중간 | `stage1_tmp/fact_ledger/fact_ledger_candidate_bundle.json` | `fact_ledger_candidate_bundle` | F0 → F2 | +| 중간 | `stage1_tmp/fact_ledger/fact_exception_pack.json` | `fact_exception_pack` | F0 → F1·F2 | +| 중간 | `stage1_tmp/fact_ledger/fact_adjudication.json` | `fact_exception_adjudication` | F1 → F2 | +| **최종** | **`Fact_Ledger_base.json`** | **없음(배열)** | F2 → Stage 2 | +| 최종 | `stage1_tmp/fact_ledger/fact_ledger_writer_report.json` | 없음 | F2 | +| 최종 | `quality_gates/stage1_part4_review_handoff.json` | `stage1_part4_review_handoff` | F0(OPEN) · F2(FINALIZED) | +| 최종 | `validation_assets/routing/part4_receipt.json` · `stage1_part4_receipt.v1` | 없음 | **F0 단독 기록**(§4.7) | + +**receipt 기록자는 F0 하나다.** F2는 receipt를 쓰지 않고 writer report에 자기 실측을 적는다. 이로써 F0 산출 4종 · F2 산출 3종 · 공유 1종(handoff) = **고유 6파일**(G-n 기대). + +**루트 래핑을 표로 박는 이유** — Part 1·2 분석서 660행: "`domain_screening.json`이 래핑돼 있어 `.get("candidates")`가 늘 `None`이었고 **계산 통과가 조용한 무효**였다." 이 표를 정책 자산에도 복제해 코드가 리터럴을 갖지 않게 한다. + +--- + +## 4. 소작업 3종 + +### 4.1 P4-F0 `Task_C_FL_F0_fact_ledger_reducer` [신설·code] + +**명명** — Part 2 `Task_C_BO_*` · Part 3 `Task_C_LE_*`를 잇는 `Task_C_FL_*`. 전략서 §1 DAG는 `Task_F0_fact_ledger_reducer`로 적었으나 Part 3에서 같은 방식으로 접두를 붙인 선례가 있다(전략서 `structure_seed_reducer` → 구현 `Task_C_LE_L0_structure_seed_reducer`). **표기 확장이지 계약 변경이 아니다.** 인계면·`stage_chain`에는 **확장 이름**을 기입한다(`Stage_1.Part_4` 약칭 제거 — Part 3 규율). + +실행기: `mcp: code-executor` · `tool_name: run_code` · `parameters` 5키 · `language: python` · `requirements: "httpx"` · `network: "agent-network"` · **`timeout: 300`**. + +**timeout 근거** — Part 3 L0 = 240(반입 4 · 사이드카 50). F0는 반입 5 + 사이드카 25 + 봉투 ×expected_runnable + SG 5종 + 게이트 합집합으로 더 무겁고, Part 2 A0·S0가 같은 부하 등급에서 300을 쓴다. **300.** + +**실행 뿌리 3인자** — `--asset-root Default_Agent/` · `--execution-root /tmp/s1` · `--logical-root /tmp/s1`을 **argv로만** 넘긴다. 절대경로 금지. `/tmp`는 macOS에서 `/private/tmp`의 심볼릭 링크이므로 `realpath`·`resolve()`로 해소하면 금지 정규식에 걸린다(`path_contract.json`의 `forbidden_regex`에 `/private/tmp/` 포함 — **다만 이 계약 파일은 현재 조립본에만 있고 배포 트리에 없다**, §9-9). + +**⓪ 배포 게이트** `assert_deployment()`가 `main()` 첫 문장. + +**`PART4_REQUIRED_ASSETS` = 13 리터럴** — 스키마 4(후보 bundle · 최종 base · 예외 pack · LES) · 정책 1 · 별칭표 1 · signal registry 1 · s5 계약 1 · 미러 5. 중복 제거 후 **실측 경로 수를 영수증에 적는다**(상수 PASS 금지). 실패 `PART4_ASSET_DEPLOYMENT_INCOMPLETE`. + +**게이트 목록에 넣지 않는 것** — SG 스키마 5종 · 봉투 스키마 · **사이드카 25**는 `expected_runnable` 것만 읽으므로 게이트 리터럴이 아니라 **읽는 시점의 `_verify_asset`**(매니페스트 해시 대조)이 지킨다(Part 3가 사이드카 50에 쓴 규율). + +**① 모듈 반입** R-1~R-5 — `.txt` 미러를 **`read_raw`**로 읽고(해시는 반드시 원문 문자열에서 — `unwrap_text` 재직렬화가 한 번만 끼어도 조용히 어긋나 `PART4_SIGNAL_SEAL_FAILED`) `runtime_manifest.json` sha256 대조 후 `/tmp/s1/_rt`에 `.py` 기록, `sys.path.insert`, `contextlib.redirect_stdout` 격리. 5종: `runtime_common` · `schema_subset_validator` · `registry_loader` · `structure_index_compiler` · **`fact_ledger_compiler`(신설)**. 실패 `MODULE_MIRROR_UNREGISTERED` / `MODULE_MIRROR_HASH_MISMATCH`. + +**② 정책 반입** `_verify_asset`. 판본 불일치 `PART4_POLICY_INVALID`. + +**③ 의미 검사 둘** — 입력 쪽: 후보 bundle 스키마가 `domain_effects`·`calculation_requests`를 아는가 → `PART4_LEDGER_SCHEMA_STALE`. 출력 쪽: 컴파일러 산출에 `domain_effects`·`gate_firings`가 실려 나오는가 → `PART4_COMPILER_STALE`(`TypeError`는 `signature_mismatch`). + +**④ 진입 봉인 셋** +- `signal_manifest` `transaction_id` 형식 + `files[]` 해시 전수 → `PART4_SIGNAL_SEAL_FAILED`. **`files[].path`에 `signals/` 접두사가 없으므로 붙여서 대조** +- SG-01 `registry_index_sha256` ↔ 색인 원문 + 도메인별 `config_sha256` 사슬 → `PART4_REGISTRY_INDEX_SEAL_FAILED` +- **LES 거래 승계 대조** — 두 갈래: 키가 있고 값이 다르면 `PART4_LES_TRANSACTION_MISMATCH` **경성 실패**, 키가 없으면 `PART4_LES_TRANSACTION_UNSEALED_REVIEW` **검토**. 이 키는 LES 스키마 `required` 8종에도, 인계면 `required_keys` 5종에도 **없다** — 계약상 적법한 산출이 키 없이 올 수 있다. §8 4b에서 상류 스키마를 보강하되, 보강 후에도 검토 갈래를 제거하지 않는다(fail-open) + +**⑤ 입력 적재** — SG 5종 · SG-01 · `BO.json`(**`extensions.domain_payload` 압축 유지**) · **`signals/domain_signals/.json` 봉투 ×expected_runnable**(부재는 계수 — 인계면 note 승계) · **`quality_gates/stage1_part2_review_handoff.json`** · `legal_effect_structures.json` · `evidence_indexed.json` · registry(`_registry_index` + `domain_config`) · **사이드카 `gates.json` ×expected_runnable**(`_verify_asset`) · 별칭표(+`legacy_declaration_mismatches`). **구 signal 3종 뿌리 이름은 읽지 않는다.** + +**⑥ `compile_fact_ledger_candidates` 한 호출** — §4.2. + +**⑦ 기록** — 후보 bundle · exception pack · review handoff(OPEN) · **receipt**. 자기 산출을 닫힌 스키마로 검증한 뒤에만 기록한다. + +**결론을 내리지 않는다 — 후보와 근거만 남긴다.** + +### 4.2 F0 모듈 내부 + +| 단계 | 내용 | +|---|---| +| F0-0 | **사이드카 정규화 리더**(17축 · §1.5a) — 127규칙을 하나의 내부 표현으로. 이 리더가 레코드 생성보다 **앞선다**(Part 3 L0-2c 순서 승계 — 교차 검증은 재료 소비보다 앞) | +| F0-0b | **별칭 해소 표 구성** — `legacy_alias_targets`로 구 이름(B1~B5)을 registry ID 목록으로 옮기되, `legacy_declaration_mismatches`에 오른 도메인(오늘 E-09)은 해소 대상에서 제외하고 `LEGACY_ALIAS_SELF_MISMATCH_REVIEW`로 가시화한다(D-P4-7) | +| F0-0c | **BO 압축·정규화** — 구본 `_compact_bo`(369–374행) 등가 계층. `BOType`→`bo_type` · `Action`→`action` 등 대문자 키를 소문자로 옮기고 `extensions.domain_payload`에서 `DOMAIN_PAYLOAD_KEEP_KEYS` 6종만 남긴다(비공백만 — 394행). **(가) 9필드 전부의 전제다** | +| F0-1 | **행 생성** — BO 1건 = 1행. `fact_id` = `F-{idx:03d}` 1-기반 연속 | +| F0-2 | **(가) 9필드 채움** — BO 18키·`core_field_base`에서 결정론 투영(§7.1a (가) 합성 규칙 9개 준수). (나) 15필드는 F0-3·F0-4(단 **`object_spec`은 v.8 우변이 미결이라 `OBJECT_SPEC_SOURCE_UNRESOLVED_REVIEW`로 가시화** — D-P4-d), (다) 2필드는 F0-6b, (라) 1필드는 F0-8(게이트 행 투영)·F0-8b(스켈레톤 3검사). **`must_consider`는 F0-3/F0-4가 1차로 정하고 F0-8이 OR로 갱신하는 공동 기록 필드다** | +| F0-3 | **LES 조인** — `by_bo_id[BO_ID] → structure_ids[] → structure_records[]`. `domain_id`·`module`·`role_projection`·`type_id`를 모아 `domain_effects{domain_id}` 구성. **registry가 말하지 않는 칸은 만들지 않고 review로 올린다** | +| F0-4 | **구 4필드 별칭 파생** — 정책 선언대로 역투영 | +| F0-5 | **SG-11 조인 + `operand_state` 승계** — `calculation_requests[]`. 조인 키 **`signal_id`**(`details.request_id`는 보장되지 않는 키 — Part 3 승계). **`details.operand_state`를 재산정 없이 그대로 싣는다**(D-P4-6). dangling ref도 **버리지 않고 `calculation_id` 없는 stub 항목으로 적재**한다(버리면 등식 ④가 정상 실행에서 발화한다) · `CALCULATION_REF_DANGLING_REVIEW` 계수 | +| F0-6 | **SG-10 조인** — `credibility`·`proof_strength` 산정 **근거 참조** 기록(§4.6). 값 자체는 F0-6b가 정한다 | +| F0-6b | **증거력 산정** — `evidence_indexed.json`으로 `evidence_authority`를 구성하고 §7.1a (다) 규칙 4단계로 두 필드를 산출한다. 증거 우주 이탈은 `EVIDENCE_REF_NOT_IN_EVIDENCE_UNIVERSE`로 가시화 | +| F0-7 | **SG-04 조인** — `calculation_requests[].law_version_refs[]`. **버전 확정 금지** | +| F0-7b | **게이트 입력 정규화** — 판정에 쓸 네 재료를 한 표로 모은다: ① 승계된 `operand_state`(F0-5) ② 봉투 `review_items`의 코드. **P2 handoff를 함께 쓸 때는 세 가지를 지킨다** — (i) 코드 키는 `review_code`가 아니라 **`source_review_code`**이고(`issue_type`은 enum 밖 값을 `review_required`로 뭉갠다), (ii) **이 혼입 주장은 Part 2 소스코드 독해이지 실측이 아니다** — 골든 P2 handoff 18항목은 `source_review_code` **키 자체가 없다**(9키 레거시 형식). 실입력이 생기는 것은 예행 pass2 이후이며, 그전까지 G-k10·k11·k13은 **합성 입력 전제**다. 소스 기준으로 그 칸에는 **생산자가 셋**이라(P2-R0 워커 1293행 · P2-R0 투영 1543행 · **P2-F0 2659행**. 1529행은 투영 pass-through 전달 경로다) registry 코드·`unresolved_type` 토큰·**BO 필드명**(투영 유래)·워커 status 합성값 `NO_SUPPORT`·`None`이 섞이므로 `^[A-Z][A-Z0-9_]{2,127}$`를 만족할 때만 코드로 승격하고 나머지는 `P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW`로 계수하며, (iii) 항목의 `source_domain`을 **F0-0b가 만든 별칭 해소표로 registry 도메인 ID로 옮긴 뒤**(골든 handoff는 전건 `B1_Money_Successor` 같은 레거시 별칭이라 해소 없이는 100% 미귀속) **해당 도메인 게이트에만 공급**한다(전역 병합하면 도메인을 넘나드는 코드가 교차 오발화한다). **P2-F0 유래 항목의 `source_domain`은 `candidate_ref` 접두사이지 registry 도메인 ID가 아니므로**, 도메인 ID 패턴에 맞지 않으면 `P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW`로 계수하고 어느 게이트에도 공급하지 않는다. 봉투 유래와 handoff 유래는 **영수증에서 분리 계수**한다 ③ 봉투 `evidence_slot_status` ④ BO payload의 `boundary_routes`/`track_boundary_candidates`/`boundary_domain_candidates`. **각 재료의 비공백 건수를 계수해 영수증에 적는다**(§7.5-4) | +| F0-8 | **게이트 합집합 실행** — §4.4. **판정 입력**은 F0-7b 표만 읽는다(게이트 선언 G와 사이드카 규칙 R은 registry·사이드카에서 직접) | +| F0-8b | **스켈레톤 3검사** — `EMPTY_DERIVED_FACT`(F0-2 이후) · `NO_EVIDENCE_REFS`(F0-2 이후) · `NO_LINKED_LEGAL_EFFECT_STRUCTURE`(F0-3 이후). 게이트 발화와 합쳐 `skeleton_validation_codes`((라) 1필드)를 확정한다 | +| F0-9 | **예외 pack 조립** — 판정 부류(D-P4-2)만 pack에. **`legal_calculation_required` 트리거는 구본 866·899–925행 술어를 승계한다** — `if` 갈래(후보 다중성)는 pack에 `conflict_flags: ["CALCULATION_BASIS_CONFLICT"]`로 싣고, 배타적인 `elif` 갈래(`amount` 또는 `object_spec` 공백)는 pack에 싣지 않고 `CALCULATION_BASIS_INCOMPLETE_REVIEW` 검토로 남긴다. `PACK_ITEM_CHAR_LIMIT` 유지. 상한 초과는 자동 차단이 아니라 `exception_id` 유지한 채 review. **정책이 `unreachable_in_v8: true`로 표기한 부류가 pack에 실리면 `PART4_UNREACHABLE_EXCEPTION_TYPE` 경성 실패** | +| F0-10 | **보존 등식 계수 + 부착 실패 계수** | + +**보존 등식 다섯 (F0 계수 → F2 원본 대조)** + +``` +① 행 : len(rows) == len(BO.json) PART4_ROW_CONSERVATION_BROKEN +② 부착커버리지: 부착 BO 수 + 미부착 BO 수 == len(BO.json) PART4_BO_ATTACHMENT_COVERAGE_BROKEN +③ 효과 : Σ_bo |domain_effects(bo) 키| + == Σ_bo |by_bo_id[bo] 레코드의 distinct domain_id| PART4_EFFECT_CONSERVATION_BROKEN +④ 계산(행) : Σ_bo |calculation_requests(bo)| + == Σ_bo |by_bo_id[bo] 레코드의 distinct calculation_request_refs| + PART4_CALC_CONSERVATION_BROKEN +⑤ 계산(원천) : 부착 distinct SG-11 + 미부착 distinct == SG-11 레코드 총수 PART4_CALC_SOURCE_CONSERVATION_BROKEN +``` + +**④를 ⑤와 나눈 이유 — 차원이 다르다.** 배포 컴파일러는 매칭된 SG-11 `signal_id`를 **그 도메인의 모든 LES 레코드에 복사**하고(`structure_index_compiler.py` 435–439행) 그 레코드가 다시 여러 BO에 부착되므로, **행 단위 항목 수 ≠ 원천 레코드 수**다. 한 도메인에 BO가 둘만 있어도 옛 등식은 `2 + 0 == 1`이 되어 **정상 실행에서 발화**한다(실측). §3.1이 "복수 정상", G-m이 "한 사실 두 계산·병합 안 함"을 명시하므로 다중성은 설계 의도다. + +**골든 경로에서는 ①~⑤가 전부 성립한다** — `59==59` · `0+59==59` · `0==0` · `0==0` · `0+0==0`. 즉 **④의 결함은 골든으로 검출되지 않는다** — G-w가 v.8 경로(26-B·pass4)에서 돌아야 한다. + +**`PART4_COVERAGE_BROKEN`·`PART4_BO_CONSERVATION_BROKEN`은 위 다섯과 다른 검사다.** 전략서 §5.3이 "자구 그대로 둔다"고 못박은 구본의 두 검사이며 이름을 빼앗지 않는다. + +| 코드 | 무엇을 보는가 | 구본 자구 | +|---|---|---| +| `PART4_COVERAGE_BROKEN` | **판정 커버리지** — `expected == decided ∪ blocked` · `decided ∩ blocked == ∅` · 중복 decision·미지 ID·예외 0건의 임의 decision은 hard fail | 1684–1758행 | +| `PART4_BO_CONSERVATION_BROKEN` | **후보 ↔ 최종 BO 집합 동일성** — `sorted(candidate source_bo_id) == sorted(final source_bo_id)` + `F-###` 연속·유일 | 1898–1902행 | +| `PART4_BO_ATTACHMENT_COVERAGE_BROKEN` | **신설** — 위 ② LES 부착 커버리지 | — | + +**`by_bo_id == {}` 는 정상 입력이다.** 그때 효과 등식은 `0 == 0`, 커버리지 등식은 `0 + 59 == 59`로 성립한다. 두 등식을 분리하지 않으면 골든 경로(부착 0/59 — `EXPECTED_DELTA-04`)에서 `59 == 0`이 되어 **필드 비교에 닿기도 전에 경성 실패**한다. 차원이 다른 두 수를 한 등식에 넣지 않는다. + +**등식 위에 부착 실패 침묵 금지를 따로 건다** — "보존 등식은 손실을 세지 흡수를 세지 않는다"(Part 1·2 분석서 109행). 어느 LES에도 닿지 않은 BO는 `FACT_ROW_UNATTACHED_REVIEW`, 어느 행에도 닿지 않은 LES 레코드는 `STRUCTURE_UNATTACHED_REVIEW`. + +### 4.3 P4-F1 `Task_C_FL_F1_fact_exception_adjudicator` [개정·LLM] + +설정(구본 승계) — `llm_provider: google` · `llm_model: gemini-3.1-flash-lite` · `llm_reasoning: low` · `llm_verbosity: low` · `max_iterations: 1` · `cache_control{mode: auto, ttl: 20m}` · `preflight: true` · `preflight_files: [stage1_tmp/fact_ledger/fact_exception_pack.json]` · `use_tools: [localdocs]`. + +프롬프트는 Part 1·2·3과 같은 3층 구조(`COMMON_CACHE_PREFIX_STAGE_1` → 역할 프리픽스 → static block + dynamic tail). + +**규율** — pack 하나만 읽는다(추가 read 금지). 최종 산출·후보 bundle·writer report를 쓰지 않는다. 출력은 `fact_adjudication.json` 하나. `has_exceptions == false`면 `READY_NO_EXCEPTIONS` 객체를 쓰고 즉시 종료. + +`reasoning: low`가 맞다 — 판정 대상이 pack에 내장돼 있고 부류가 좁다. + +### 4.4 게이트 실행 설계 + +``` +활성 도메인 D ← SG-01 (expected_runnable_domain_ids) +선언 게이트 G ← ⋃_{d∈D} domain_config[d].gates[] # registry가 무엇을 켜는가 (121 중 활성분) +사이드카 규칙 R ← ⋃_{d∈D} 정규화리더17축(gates.json[d]) # 판정식이 무엇인가 (127 중 활성분) + +for g in G (gate_id 오름차순 — 결정론): + r = R[g.gate_id] + if r 없음: GATE_SPEC_NOT_DEPLOYED_REVIEW + elif r.condition 이 dict: + k = r.condition 의 단일 키 + if k ∈ 정책.implemented_condition_keys: 평가 → 발화 시 코드 부착 + else: GATE_CONDITION_KEY_UNSUPPORTED_REVIEW + elif r.condition 이 str: GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW (술어 명세 인용) + 차단 여부 = 정규화된 차단 플래그(3경로) — registry g.blocks_final_drafting 과 대조 + 불일치 시 GATE_BLOCK_FLAG_MISMATCH_REVIEW + 발화 코드 = 정규화된 발화코드(3키) ∪ g.review_codes + ── 행 투영 (3분기) ── + 게이트 판정은 **도메인 스코프**다(봉투 review_items·evidence_slot_status 는 도메인당 한 벌). + 그러나 출력은 **행 단위**다. 발화한 게이트가 속한 도메인 d 의 투영 대상 행 R(d) + = { row | d ∈ row.domain_effects.keys() } 로 정의하고 세 갈래를 각각 적용한다. + + ① 발화 코드 (127/127) → R(d) 의 모든 행 skeleton_validation_codes 에 부착 + **순서 규칙** — 스켈레톤 3코드를 구본 고정 순서 + (EMPTY_DERIVED_FACT → NO_EVIDENCE_REFS → NO_LINKED_…)로 먼저, + 그 뒤에 게이트 코드를 gate_id 오름차순으로 append, + 중복은 _dedupe 로 **첫 출현 위치 보존**. G-n(바이트 동일)·G-r 이 순서에 의존한다 + ② row_effect.must_consider (48건) → R(d) 의 모든 행 must_consider 를 **OR 연산**으로 갱신 + (덮어쓰기 아님 — 다른 게이트·BO 유래 True 를 지우지 않는다) + ③ on_true.preserve_seed (42건) → 행 필드가 아니라 **writer report 의 게이트별 발화 목록**에 + 기록한다(27+2 키에 대응 필드가 없다) + R(d) 가 공집합이면 세 갈래 모두 행에 닿지 않고 writer report 에만 남는다(침묵 아님 — 계수) + registry 미선언 발화코드가 있으면 GATE_EMITTED_CODE_NOT_IN_REGISTRY (E18-G03·E20-G03 실측 2건) + +for r in R \ G: GATE_RULE_NOT_IN_REGISTRY # 고아 6건 +``` + +**gate_id 충돌 없음** — 사이드카는 도메인별 파일이지만 gate_id가 도메인 접두를 갖는다(`E10-G05`·`X1-G01`). 127건 전수에서 중복 0을 확인했다. 단일 사전 조회가 안전하다. + +**구현 범위 (registry 121 기준)** + +| 부류 | 건수 | 정의처 배포 | 처리 | blocks=true | registry 위반 | +|---|---|---|---|---|---| +| dict · `any_review_code_present` | 13 | O | **구현** · 발화 0 계수 | 10 | — | +| dict · `required_slots_missing_or_conflicting` | 6 | O | **구현** · 발화 0 계수 | 6 | — | +| dict · `calculation_operand_state_in` | 6 | O | **구현** · 골든 발화 0 | **6/6** | **E18-G03·E20-G03** — `DEFERRED_CALCULATION_TRACK_REVIEW`가 registry 미선언(§9-7) | +| **소계 구현** | **25** | | 판정식 창작 0 · **D-P4-c=(가) 가정** | 22 | 2 | +| dict · `boundary_or_overlap_detected` | 6 | **X**(형태·의미 둘 다 조립본 전용) | `GATE_CONDITION_KEY_UNSUPPORTED_REVIEW` 가시화 | 6 | — | +| str(술어 명세) | 90 | — | `GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW` 가시화 | 32 | — | +| **소계 가시화** | **96** | | | 38 | | +| **registry 합계** | **121** | | | 60 | | +| 사이드카 고아(`*_attempted` 2키) | 6 | — | `GATE_RULE_NOT_IN_REGISTRY` → §9-2 | — | — | +| **사이드카 합계** | **127** | | | | | + +**`calculation_operand_state_in` 6건은 세 사실이 겹치는 자리다** — ① 이번 회차 유일하게 실입력 도달이 확인된 부류(`s4_states` distinct 6건) ② **6/6 전부 `blocks_final_drafting: true`** ③ 그중 2건이 §9-7의 registry 계약 위반 짝이다. 발화하는 순간 차단 표지와 `GATE_EMITTED_CODE_NOT_IN_REGISTRY`가 함께 뜬다. 게다가 이 6건이 사는 도메인(E-14·17·18·19·20·21)은 **예행 pass4 활성 집합(X1·X2·X3) 밖**이므로 pass4가 한 번도 밟지 않는다 → `SUPP-calc6` 별도 픽스처가 필요하다(§7.4). + +**구현 25건은 발화 0이어도 구현한다.** 구현해 두고 발화 0을 **계수·가시화**하는 것이 침묵 스킵과 다른 점이다 — 상류 보강(§9-3·5·6·13)이 끝나면 코드 수정 없이 발화가 시작되고, 그 전까지는 영수증이 "구현 25 · 골든 발화 0 · 입력 미도달"을 사실로 기록한다. + +**`E14-G04`는 `E14-G02`와 발화 코드가 겹친다**(`E14_PROCEDURE_ONLY_REVIEW`·`TRACK_BOUNDARY_REVIEW`). 고아 검토 항목은 **gate_id를 반드시 인용**해 G02 발화와 구분한다. + +**`unknown_or_missing_policy`** — 9도메인 48규칙(E-01·04·05·06·11·12·13·16·X2)이 `fail_open_to_review`로 선언하나 **실행 대상 dict 31건 중 이를 선언한 것은 0건**이다. 따라서 정책 자산이 **전역 기본값 `fail_open_to_review`**를 선언하고, 사이드카 선언이 있으면 그것이 우선한다. 입력이 없으면 실패가 아니라 검토다(ensemble_v2 fail-open). + +**법령 버전 게이트 한정** — 전략서 §5.1: "법령 버전 관련 게이트는 **검토 표지 발화까지만** 구현한다." + +### 4.5 SG-04 — 참조만 싣고 판단하지 않는다 + +`ensemble_v2.md` §6-5: "법정 수치의 프롬프트 하드코딩 금지 — SG-04에서 주입." 참조는 `calculation_requests[].law_version_refs[]`로만 나른다(29키 행에 별도 필드를 만들지 않는다). + +### 4.6 증거력 산정 근거 + +전략서 §5.1 — "증거력 산정 근거를 SG-10 레코드 참조로 기록한다." + +`credibility`·`proof_strength`는 구 27필드이므로 **값의 어휘를 바꾸지 않는다.** 값은 `evidence_indexed.json` 기반 산정이 정하고(F0-6b · §7.1a (다) 규칙), `low`는 **증거 행 0건일 때의 값**이지 기본값이 아니다. 이 절이 더하는 것은 *어느 SG-10 레코드에서 왔는가*의 참조뿐이며, 참조는 **writer report와 handoff 항목**으로만 나른다(27키 불변 유지). **`skeleton_validation_codes`에 싣지 않는다** — 그 칸은 (라) 부류이고 값이 검토 코드 어휘(`^[A-Z][A-Z0-9_]{2,127}$`)로 제약되며, SG-10 레코드 id를 넣으면 `EXPECTED_DELTA-03`의 확정 범위(골든 2행)를 넘어 G-r의 (라) 판정이 무너진다. + +**증거 승격 금지** — `ensemble_v2.md` §4.1 hard rule. Part 4는 승격 판단의 주체가 아니다. + +### 4.7 P4-F2 `Task_C_FL_F2_final_fact_ledger_gate_and_writer` [신설·code] + +실행기: code-executor · 5키 · **`timeout: 240`**(Part 3 L2·Part 2 F0와 같은 부하 등급 — 반입 2종·판정 없음·원본 계수만). **입력은 전부 파일 계약**(메모리 전달 없음). + +| 검사 | 실패 코드 | +|---|---| +| 닫힌 스키마 검증 = registry effect projection 참조 | `PART4_LEDGER_SCHEMA_INVALID` | +| **`domain_effects` 키 ⊆ SG-01 활성 도메인 — 위반 시 즉시 실패** | `PART4_DOMAIN_EFFECT_NOT_ACTIVE` | +| 보존 등식 **다섯** — **바깥 수를 원본에서 직접 계수** | §4.2 ①~⑤ | +| ② LES 부착 커버리지 | `PART4_BO_ATTACHMENT_COVERAGE_BROKEN` | +| ④ 계산(행 차원) | `PART4_CALC_CONSERVATION_BROKEN` | +| ⑤ 계산(원천 차원) | `PART4_CALC_SOURCE_CONSERVATION_BROKEN` | +| 정확 커버리지 등식 | `PART4_COVERAGE_BROKEN` | +| 패치 거부 | `PART4_PATCH_REJECTED` | +| BO 보존 검증 | `PART4_BO_CONSERVATION_BROKEN` | +| `fact_id` 연속성 단언 | `PART4_FACT_ID_SEQUENCE_BROKEN` | +| 판정 파일 조건부 — `exception_count > 0`인데 부재면 실패 | `PART4_ADJUDICATION_MISSING` | + +전략서 §5.3 — "정확 커버리지 등식, 패치 거부, 3단 상태, BO 보존 검증은 **자구 그대로 둔다**." + +**writer report 추가 3종** — ① 게이트별 발화 목록 ② 도메인 효과 커버리지 ③ 계산 준비도 집계. **F2는 receipt를 쓰지 않는다**(§3.3). + +**기록 후 재읽기 해시 대조** → review handoff `FINALIZED` 갱신(F0과 공동 기록자). + +--- + +## 5. DAG + +``` + ┌────────┐ + │ IN │ + └───┬────┘ + ▼ + P4-F0 Task_C_FL_F0_fact_ledger_reducer code · timeout 300 + ⓪ 배포 게이트(13 리터럴) ① 모듈 5종 반입 ② 정책 반입 + ③ 의미 검사 둘 ④ 진입 봉인 셋(signal · registry · LES 거래) + ⑤ 입력 적재(BO payload · 봉투 · P2 handoff · 사이드카 25) + ⑥ compile_fact_ledger_candidates ⑦ 기록 + └─▶ candidate bundle · exception pack · handoff(OPEN) · receipt + │ + ▼ + P4-F1 Task_C_FL_F1_fact_exception_adjudicator LLM · flash-lite / reasoning low + pack 하나만 읽음 · 판정만 · fact_adjudication.json 하나만 씀 + has_exceptions == false → READY_NO_EXCEPTIONS 즉시 종료 + │ + ▼ + P4-F2 Task_C_FL_F2_final_fact_ledger_gate_and_writer code · timeout 240 + 닫힌 스키마 · domain_effects ⊆ SG-01 활성(위반 즉시 실패) + 보존 등식 다섯(바깥 수 원본 계수) · 커버리지 · 패치 거부 · BO 보존 · fact_id 연속성 + └─▶ Fact_Ledger_base.json · writer report · handoff(FINALIZED) ──▶ Stage 2 + │ + ┌───▼────┐ + │ OUT │ + └────────┘ +``` + +```yaml +task_procedure: + IN: + nexts: ["Task_C_FL_F0_fact_ledger_reducer"] + wait_until: [] + Task_C_FL_F0_fact_ledger_reducer: + nexts: ["Task_C_FL_F1_fact_exception_adjudicator"] + wait_until: ["IN"] + Task_C_FL_F1_fact_exception_adjudicator: + nexts: ["Task_C_FL_F2_final_fact_ledger_gate_and_writer"] + wait_until: ["Task_C_FL_F0_fact_ledger_reducer"] + Task_C_FL_F2_final_fact_ledger_gate_and_writer: + nexts: ["OUT"] + wait_until: ["Task_C_FL_F1_fact_exception_adjudicator"] + OUT: + nexts: [] + wait_until: ["Task_C_FL_F2_final_fact_ledger_gate_and_writer"] + +prevs: [] +nexts: [] +``` + +F1은 예외 0이어도 `READY_NO_EXCEPTIONS`를 쓰고 종료하므로 **그래프상 무조건 노드**다. + +--- + +## 6. 자산 — 정확한 배포 위치 + +배포 원본 `v.7/extension_research/Default_Agent/` — 현재 **git 추적 209파일 · `runtime_manifest.json` 432항(작성 후 433 관측) · 등재-실물 교집합 155 · 해시 불일치 0 · 실물-미등재 54**. 절대값은 착수 직전에 다시 센다(§6.4). + +### 6.1 신설 자산 6종 + +| 배포 경로 (`Default_Agent/` 기준) | 형태 | 쓰는 task | 역할 | +|---|---|---|---| +| `stage1_runtime/fact_ledger_compiler.py` | py | — (정본) | 조립·판정 로직 전부. 순수 함수 — I/O·시각·난수 없음. **17축 사이드카 정규화 리더** · 행 생성 · LES/SG 조인 · 게이트 평가 · 별칭 파생 · 검토 수집 | +| `stage1_runtime/fact_ledger_compiler.txt` | 미러 | F0 | 바이트 동일 미러(localdocs가 `.py`를 서빙하지 않음) | +| `stage1_runtime/fact_ledger_policy.v1.json` | json | F0 · F2 | `stage1_fact_ledger_policy.v1` — **27필드 목록·자료형·순서(유일 정본)** · `fact_id` 서식 · 별칭 파생 · **`implemented_condition_keys[]`** · 방언 정규화 규칙(17축) · 차단 의미 · `unknown_or_missing_policy` 전역 기본값 · 조인 키 · 등식 다섯 · **루트 래핑 표** · LES 승계 검토 2코드 · 검토/경성 코드 목록 · `deprecation`(구 4필드 v.9 제거) · F1 판정 부류 `unreachable_in_v8` 표기 | +| `platform/schemas/fact_ledger_candidate_bundle.schema.json` | json | F0 | 후보 bundle 닫힌 스키마 | +| `platform/schemas/fact_ledger_base.schema.json` | json | F2 | 최종 산출 닫힌 스키마 | +| `platform/schemas/fact_exception_pack.schema.json` | json | F0 · F1(계약) | 예외 pack 닫힌 스키마 | + +**스키마 명명 규칙**(Part 3 승계) — **파일명에 판본을 넣지 않는다**(빌더 `$ref` 폐포·형식 검증이 `*.schema.json`만 훑음). 열거를 하드코딩하지 않고 패턴만 쓴다. + +### 6.2 복사 배포 — 게이트 사이드카 ×25 (81,430 B) + +`domains//gates.json` — EC-00 제외 25개 도메인. **조립본의 동명 파일을 바이트 동일 복사만 한다(내용 수정 0 — 방언 통일은 registry 소유자 몫).** Part 3 §6.2 절차 그대로. + +복사 전 원천 정합 — 대응 `domain_config.json` 26/26이 조립본과 바이트 동일한지 먼저 확인한다. + +### 6.3 기존 자산 중 Part 4가 새로 소비하는 것 + +| 배포 경로 | 쓰는 task | 쓰임 | +|---|---|---| +| `runtime_manifest.json` | F0 · F2 | 반입 자산 sha256 선언표 | +| `domains/_registry_index.json` | F0 · F2 | 도메인 순서 · `config_sha256`. **SG-01 봉인이 지킨다** | +| `domains//domain_config.json` ×expected_runnable | F0 · F2 | `structure_types[].module`·`.role_projection`(§1.7) · `gates[]`(§4.4) · `calculation_bindings`(**계산 ID 배열일 뿐 피연산자 정의가 아니다** — 26/26 실측) | +| `routing/extension_payload_key_declarations.v1.json` | F0 | `legacy_alias_targets` + **`legacy_declaration_mismatches`**(D-P4-7) | +| `platform/schemas/legal_effect_structures.schema.json` | F0 | **LES 능력 검사만**(스키마가 `signal_manifest_transaction_id`를 아는가) — **LES 문서를 전건 스키마 검증하지 않는다.** 그래서 §8 4b의 `required` 승격 후에도 키 부재가 스키마 위반이 아니라 §4.1④의 검토 갈래(G-e2)로 남는다 | +| `signals/schemas/*.schema.json` ×5 + `domain_signal_envelope.schema.v2.json` | F0 | 형태 검증(`_verify_asset`) | +| `signals/signal_registry.v2.json` · `contracts/signals/s5_execution_contract.v2.json` | F0 (게이트 선검사) | 하류 소비 자산 배포 완전성 | + +**모듈 미러 5종** — `runtime_common`(4,496 B) · `schema_subset_validator`(7,442 B) · `registry_loader`(6,404 B) · `structure_index_compiler`(27,309 B) · `fact_ledger_compiler`(신설). + +### 6.4 매니페스트 갱신 — **N → N+31** + +신설 6종 + 사이드카 25 = **31행**. `runtime_artifact_count`를 **착수 직전 실측값 N → N+31**로 갱신한다. 이 문서 작성 시점 실측은 432였으나 이후 작업 트리에서 1항이 늘어 **433**이 관측됐다 — **절대 리터럴을 쓰지 말고 착수 직전에 다시 센다**(§8-7 중단 규칙이 전수 재계산 불일치에서 즉시 정지한다). + +**사이드카가 등재 대상인 근거** — 매니페스트의 `domains/` 항목 51개 = `structure_types.json` 25 + `module_role_projection.json` 25 + `common_worker_contract.md` 1. **Part 3가 사이드카 50을 전부 등재했다.** 같은 관례를 따른다. + +절차 — **직접 행 추가**(빌더 재실행은 파괴적) · **앵커 기반 행 치환**(`json.load`→`json.dump`는 무관한 400여 행을 diff에 올린다) · **sha256은 실물에서 재계산** · 갱신 후 **전수 재계산** · indent=2 왕복 보존. + +### 6.5 인계면 선언표 — 45 → 55 (신설 10 · 갱신 **12**) + +**정본은 `선행구축/Default_Agent_Stage_1/handoffs/stage1_part_interface.v1.json` 하나다** — 현재 `handoffs` **45행** · `handoff_count` **45**. **둘 다 갱신한다.** + +**주의 — 같은 이름의 사본이 하나 더 있다.** `ver_8_yaml_candidates/Default_Agent_Stage_1/handoffs/`의 사본은 **34행**이고 바이트가 다르며, §1.4의 근거인 **행 39(봉투)가 아예 없다**(SG-07·SG-11·LES 행도 없다). 이 회차는 정본만 고치고, 사본 동기화는 §9-11에 이월한다. + +**신설 10줄** · **갱신 12줄**(대상 경로 12 · 작업 13 — LES 행은 소비자 기입과 `required_keys` 추가 두 작업) + +| # | 경로 | 비고 | +|---|---|---| +| 1–3 | SG-04 `signals/governing_law_version_signals.json` · SG-06 `…/timeline_notice_condition_signals.json` · SG-10 `…/evidence_proof_conflict_signals.json` | **행 자체가 없다**. `produced_by_module: signals/compiler/common.py` · `required_keys: ["schema_version","signal_id","status","records"]` 형식을 SG-07/SG-11 행에서 따온다 | +| 4–6 | 중간 3종 — candidate bundle · exception pack · adjudication | 검사기가 비-`Default_Agent/` 경로의 미선언 read/write를 `U1_UNDECLARED_READ`/`U2_UNDECLARED_WRITE`로 센다. **선언하지 않으면 finding 0이 불가능하다.** Part 2·3도 중간 파일을 전부 선언했다 | +| 7–10 | 최종 4종 — `Fact_Ledger_base.json`(소비자 `Stage_2`) · writer report · review handoff(`also_written_by: F2`) · receipt | | + +**갱신 12줄(작업 13)** + +| 유형 | 대상 | 작업 | +|---|---|---| +| **소비자 기입** | `legal_effect_structures.json`(빈 배열) | Part 3가 "소비 task 이름은 Part 4 착수 시 기입"으로 남긴 자리 | +| **소비자 추가** | `signals/domain_signals/.json`(0-based index 39 = **40번째 행**) · `quality_gates/stage1_part2_review_handoff.json`(index 26 = **27번째 행**) | §1.4 게이트 입력 — **행이 이미 있으므로 소비자만 추가** | +| **소비자 추가** | `routing/domain_activation_manifest.json` · `evidence_indexed.json` · SG-07 · SG-11 | 이 4행에는 `Stage_1.Part_4` **약칭이 없다** — 치환이 아니라 추가 | +| **약칭 치환** | `BO.json` · `signals/signal_manifest.json` | `Stage_1.Part_4` → `Task_C_FL_F0_…`·`Task_C_FL_F2_…` | +| **소비자 제거** | 구 이름 3행(`actio_case_signals.json` · `case_liability_signals.json` · `legal_effect_signals.json`) | `Stage_1.Part_4` 제거 → **C-7 완전 폐쇄** | +| **required_keys 추가** | LES 행 | `signal_manifest_transaction_id`(§4.1④ 상류 보강과 한 벌) | + +**경로 중복 주의** — 검사기는 `rows = {row["path"]: row}`로 경로 키 사전을 만들며 **`handoffs`만 읽는다**(`entry_inputs`는 읽지 않는다). 같은 경로가 `handoffs` 안에 두 번 있으면 뒤엣것이 이긴다. 신설 10줄이 기존 경로와 충돌하지 않는지 먼저 확인한다. + +`stage1_stage_chain.v1.json` Part 4 구간 — 현재 `{"yaml": "Stage_1_Part_4_Claude_v3.yml", "requires": ["BO.json","signals/signal_manifest.json"], "produces": []}` → yaml을 `stage_1_part_4_v.8.yml`로, requires에 `legal_effect_structures.json`·`routing/domain_activation_manifest.json`·`evidence_indexed.json`·`signals/domain_signals/.json` 추가, produces에 최종 4종 기입. + +검사기 `_check_stage1_interface.py`를 **네 통합본 전수**로 돌려 finding 0을 확인한다. + +--- + +## 7. 검증 계획 + +### 7.1 착수 전 등록 — 골든 등가는 **필드 부류별로** 판정한다 + +**"27필드 전건 값 등가"는 성립하지 않는다.** §1.1이 Part 3 v.8에서 소멸했다고 판정한 LES 레코드 필드(`issue_cluster_ids`·`asset_cluster_ids`·`evidence_indexes`·`linked_claim_group_ids`·`linked_liability_group_ids`·`linked_actio_signal_ids`·`legal_effect_roles`)와 §1.2가 폐지한 구 signal 3종이, **골든-FL 27필드 중 7개의 값을 만들고 있다.** 골든-FL 실측: + +``` +linked_structures 59/59 {"structure_ids":["LES-001"],"issue_cluster_ids":["IC-001"],…} +claim_chain_ref 59/59 {"claim_group_ids":[],"liability_group_ids":["B1:005"],"signal_count":…} +state_context 59/59 {"bo_type":"event","issue_cluster_ids":["IC-001"],"asset_cluster_ids":…} +legal_effect_roles 59/59 ["claim_chain_ref"] 등 +downstream_module_candidates 59/59 · linked_actio_structures 59/59 · actio_roles 6/59 +``` + +`claim_chain_ref.liability_group_ids`의 `"B1:005"`는 **구 도메인 이름**이고, `issue_cluster_ids`·`asset_cluster_ids`는 v.8 LES에 존재하지 않는다. 즉 이 7필드는 **재현 대상이 아니라 재설계 대상**이다. + +#### 7.1a 27필드 4부류 판정 — **골든 BO ↔ 골든 FL 전건 실대조로 귀속했다** + +귀속 근거는 구본 `_build_row`(**687행 정의** · row dict 697–731 · 파생 732–741 · return 743)의 각 필드 우변과, 골든-FL 59행 × 골든 BO 59건의 실대조다. + +| 부류 | 필드 | 근거 | 판정 기준 | +|---|---|---|---| +| **(가) BO 유래 — 값 등가 요구** · **9** | `fact_id` · `source_bo_id` · `type` · `date` · `parties` · `amount` · `action` · `evidence_refs` · `derived_fact` | 실대조: `FL.type == BO.BOType` **59/59** · `FL.action == BO.Action` **59/59** · `FL.evidence_refs == BO.source_evidence_indexes` **59/59** · `FL.amount` = `BO.amount`의 키정렬 JSON 문자열 **59/59** · `FL.derived_fact == FL.action` **59/59** · `date` 전건 `null`(`core.BehaviorTime` 0/59) · `parties` 전건 `[]` | **59행 전건 등가.** 어긋나면 실패 | + +**(가) 9필드의 합성 규칙 — 정책 자산에 싣고 정적 검사 11이 강제한다.** 골든이 (가)로 나온 것은 구본 우변이 우연히 BO 값만 실었기 때문이므로, v.8이 다르게 합성하면 정상 구현에서도 G-r이 깨진다. + +- `evidence_refs` = BO `source_evidence_indexes` **단독**. 구본 1125행은 여기에 LES `evidence_index_ids`를 합집합하는데, 골든에서는 LES 기여가 BO의 부분집합이라 59/59가 일치했을 뿐이다. **v.8 LES는 `evidence_refs`를 필수 16키로 가지므로 합집합하면 깨진다** — LES 증거는 `domain_effects` 쪽에만 싣는다 +- `type` = BO `BOType` 단독. **LES `types[0]` 폴백 금지** +- `parties` = BO `core_field_base` 단독 — `Performer`→`role: actor` · `Subject`→`role: counterparty` · 값은 `_text(party, 160)` · `source: "BO.core_field_base"`. **구 signal `case_liability` 경로(구본 626–629행)는 폐지됐다**(§1.2) +- `amount` = `_text(_first_value(bo.amount, core.Amount), 240) or None` — **키 오름차순 JSON(`ensure_ascii=False`) → `\s+`를 단일 공백으로 정규화 → 240자 절단 → 빈 문자열이면 `None`**. 세 부수 동작을 빠뜨리면 240자 초과 행이 어긋난다 +- `date` = `_first_value(core.BehaviorTime, bo.date)`. **골든은 `BehaviorTime` 0/59라 전건 `null`이지만 v.8 BO는 그 칸을 채운다**(Part 2 v.8 1263행) — 규칙 없이 구현하면 v.8 경로에서 골든과 달라진다. `bo.date` 2차 폴백은 BO 18키에 없는 키라 항상 `None`이다 +- `action` = `_first_value(bo.action, core.Action)` — **정규화 계층(`_compact_bo`) 통과 후의 소문자 키**를 읽는다. 정규화 없이 원본 `Action`을 소문자 키로 찾으면 전건 `None`이 된다 +- `fact_id` = `F-{idx:03d}`(BO 사전순 1-기반) · `source_bo_id` = BO_ID 그대로 +- `derived_fact` = `action` 복제. 구본 `_derived_fact`는 `date`·`parties`·`object_spec`이 채워지면 4항 서술로 갈라지는데, 골든은 `date` 전건 null이라 그 분기가 죽어 `action`과 같아졌다. **v.8은 구본의 4항 서술 분기(`date and actor and obj and action`)를 삭제하고 `derived_fact = action` 단독으로 확정한다** — `obj`(=`object_spec`)가 (나) 필드라 참조를 끊으면 조건이 영구 거짓이 되어 분기가 사문이 된다. 이 삭제를 정책 자산 `deprecation` 절에 등재한다 +| **(나) LES·구 signal 유래 — 재산출 대조** · **15** | `object_spec` · `legal_centrality` · `legal_effect_roles` · `claim_chain_ref` · `linked_structures` · `must_not_drop_in_claim_types` · `must_consider` · `state_context` · `money_claim_effect` · `commercial_successor_effect` · `actio_roles` · `linked_actio_structures` · `downstream_module_candidates` · `derivation_basis` · `legal_calculation_object` | 우변이 `linked`(LES 압축본) · `types`(LES `candidate_structure_type`) · `signals`(구 signal 3종) 중 하나 이상. 실측: `core.Object` **0/59**이라 `object_spec`은 `linked.asset_cluster_ids` 폴백 — **그런데 그 폴백 대상이 Part 3 v.8에서 소멸했다**(§1.1). 대체 우변이 정해지지 않아 v.8에서는 `_text([],160)` = 리터럴 `"[]"`가 59행 전건에 들어간다. **결정 전까지 `OBJECT_SPEC_SOURCE_UNRESOLVED_REVIEW`로 가시화한다**(§10 D-P4-d) · `derivation_basis`는 `structure_ids=LES-001` 문자열을 담는다(v.8 형식은 `LES-E-02-money_claim`) · `must_not_drop`은 `_modules(types, signals)`이고 59/59가 구 signal 어휘 `claim_liability`를 담는다 · `downstream_module_candidates`와 **값이 59/59 동일** | 등가를 요구하지 않는다. **v.8 LES에서 결정론으로 재산출한 값과 일치**하는지를 본다. 실측 diff는 `EXPECTED_DELTA-04` | +| **(다) 증거 유래 — 값 등가 요구** · **2** | `credibility` · `proof_strength` | `_score_evidence`가 `evidence_indexed.json`의 `evidence_authority`를 읽는다(구본 492·570·1112행). 골든-FL에는 그 파일이 없으나 **`S_0_GPT_v2/fixtures/legacy_part1/run_1/evidence_indexed.json`이 골든 BO가 참조하는 증거 30건을 전건 덮는다**(covered 30/30 · 재현 실측 **59/59**) | **59행 전건 등가.** 그 픽스처를 `SUPP-golden59`에 포함한다. **산출 규칙은 아래에 명문화한다** | + +**(다) 2필드의 산출 규칙 — (가)와 같은 급으로 정책 자산에 싣는다.** 골든 분포가 `credibility {high 39·medium 19·low 1}` · `proof_strength {documentary 29·medium 19·high 10·low 1}`이므로 "기본값 `low`"만 구현하면 **58/59가 깨진다**. + +1. **`evidence_authority` 사전 구성 — 행은 발췌 문자열이 아니라 6키 dict다**(구본 496–505행). 이 구성을 틀리면 재현이 **54/59**로 떨어진다(실측). + + | 키 | 우변 | + |---|---| + | `evidence_index` | `_evidence_id(record)` | + | **`title`** | `_first_key(record, ["title","Title","name","doc_title"])` — **마커가 여기에만 있는 행이 5건이다**(예: `영업양도 계약서`·`각서 및 금전소비대차계약서`의 `계약`) | + | `excerpt` | `_excerpt_from_indexed`(`key_facts` → `key_dates`/`key_amounts` → fallback) | + | `priority` | `_first_key(record, ["priority","Priority","weight"])` | + | `authentication` | `_first_key(record, ["authentication","auth","source_authentication"])` | + | `source` | 고정 문자열 `"evidence_indexed.json"` | + + `evidence_indexed.json`은 `_flatten_records`(깊이 3)로 펼쳐 id 키 6종으로 색인한다. **우주 밖 참조는 버리지 않고 `{"evidence_index": eid, "missing": True}` stub을 만든다**(구본 1136행) — 그 stub도 **비어 있지 않은 증거 행으로 계수된다** +2. **텍스트 합성 — `_text`가 dict를 통째로 직렬화한다.** `_text(row, 300)` = `json.dumps(row, ensure_ascii=False, sort_keys=True)` → `\s+`를 단일 공백으로 정규화 → **300자 절단**. 즉 대조 대상은 **6키 전부의 값**이며 발췌만이 아니다. 행마다 절단한 뒤 `text = " ".join(...)`으로 합쳐 **1회 대조**한다(합친 뒤 절단이 아니다). + + **경고 — `sort_keys` 정렬에서 `title`이 최후미다**(`authentication`·`evidence_index`·`excerpt`·`priority`·`source`·`title`). 300자 절단이 먼저 잘라내는 것이 곧 마커 보유 필드이므로, `excerpt`가 길어지면 `title`의 마커가 사라져 `documentary` 판정이 바뀐다. 현 픽스처 최대 252자로 여유 48자다 — **절단 여유를 0c-1에서 실측해 영수증에 적는다** +3. **판정 — 5갈래 · 순서 그대로**(구본 570–581행). 순서가 바뀌면 골든 재현이 깨진다. + + | # | 조건 | `credibility` / `proof_strength` | + |---|---|---| + | 1 | 증거 행 **0건** | `low` / `low` | + | 2 | 마커 10종 중 **하나라도** `text`에 있음 — `등기`·`계약`·`판결`·`공문`·`금융`·`송금`·`계좌`·`registry`·`contract`·`judgment` | **`high` / `documentary`** | + | 3 | 증거 행 **2건 이상** | `high` / `high` | + | 4 | `text`에 `meeting` 또는 `회의` 또는 `면담` | `medium` / `meeting_only_core` | + | 5 | **그 외 전부** | **`medium` / `medium`** | + + 골든 분포가 `documentary 29 · medium 19 · high 10 · low 1`인 것은 갈래 2가 **첫 검사**이고 갈래 5가 **실재**하기 때문이다. 갈래 5를 빠뜨리거나 2·3의 순서를 바꾸면 **59행 중 30행이 어긋난다**(문서 초안이 실제로 그랬고 재현 29/59로 실측됐다). + +4. **증거 우주 이탈 계수** — 참조가 authority에 없으면 구본 1129행처럼 `EVIDENCE_REF_NOT_IN_EVIDENCE_UNIVERSE`를 남긴다. **침묵 금지 대상이다**(§7.5-5). 다만 stub이 증거 행으로 계수되므로 **값이 `low`/`low`가 되는 것은 참조가 전부 우주 밖일 때가 아니라 `evidence_refs` 자체가 빈 경우뿐이다.** + +| **(라) 스켈레톤·게이트 유래 — 확정 차분** · **1**(+겸용 1) | `skeleton_validation_codes` · **`must_consider`(나)∩(라) 겸용** | 행 스켈레톤 3검사 + **행 투영된** 게이트 발화(§4.4) | `EXPECTED_DELTA-03` | + +**9 + 15 + 2 + 1 = 27. 무분류 필드는 없다.** 단 **`must_consider`는 (나)∩(라) 겸용**이다 — LES 부착에서 1차로 정해지고(F0-3·F0-4) 게이트 행 투영 ②가 **OR로 갱신**한다(F0-8). 최종 기록자가 둘이므로 G-r2의 (나) 기대는 "LES 재산출값 **∨ 게이트 투영값**"이다. 실측상 `row_effect.must_consider`가 **48/48 전부 `true`**라 OR는 순서 무관·단조이며 결정론이다. + +**`must_not_drop_in_claim_types`가 (라)가 아닌 이유** — 사이드카 127규칙 전수에서 이 필드는 **0회** 등장한다. 게이트가 쓰는 유일한 행 필드는 `row_effect.must_consider`(48건)다. 값은 `_modules(types, signals)`이므로 LES + 구 signal 유래이고, 골든에서 `downstream_module_candidates`와 **59/59 값이 같다**. 같은 값을 가진 두 필드를 다른 부류에 넣지 않는다. + +**(가)에 `type`·`action`이 들어간 근거** — 구본 `_build_row`가 받는 `bo`는 원본이 아니라 **`_compact_bo`(369–374행)가 정규화한 압축본**이고, 그 함수가 `BOType`→`bo_type`·`Action`→`action`으로 키를 옮긴다. 실대조에서 **59/59가 BO 대문자 키 값과 일치**하므로 원천은 BO다. **v.8도 같은 정규화 계층을 두어야 한다** — 정규화 없이 `bo.get("bo_type")`을 읽으면 전건 `None`이 된다. + +#### 7.1b 등가 예외 등록 — **5건** + +| id | 내용 | 범위 | +|---|---|---| +| `EXPECTED_DELTA-01` | `actio_remedy_or_cap` module·role 매핑 해소 → `actio`/`linked_actio_structures` | E-13 활성 시 | +| `EXPECTED_DELTA-02` | **최종기안 차단 표지 신설** — registry `blocks_final_drafting: true` 60건의 condition 분해는 any_review 10 · required_slots 6 · calc_operand 6 · boundary 6 · str 32. 이번 회차 발화 가능 상한 = **구현 부류 ∩ blocks=true − `X1-G01` = 10+6+6−1 = 21건**. 가시화 대상(boundary 6 · str 32)은 차분이 아니다. **골든-S2 기준 실발화 0**(입력이 비었고, 설령 발화해도 골든 경로는 `domain_effects` 공집합이라 **행 투영 대상이 0**이다) | 활성 도메인 · 입력 도달 시 | +| `EXPECTED_DELTA-03` | **`LEGAL_CAUSE_STATE_UNKNOWN_REVIEW` 소실** — 골든-FL F-011·F-014에서 사라진다. **원인은 "str이라 가시화만"이 아니다** — 골든 발화가 registry 미선언 갈래(구본 778–779행 `if events and not causes`)에서 나왔고 두 행의 `legal_cause_assertions`가 0건이라 `E11-G01`의 선언 술어("양립 불가 **쌍**")로는 **재현 자체가 불가능**하다 | **골든-FL 2행 · 확정** | +| `EXPECTED_DELTA-05` | **`NO_LINKED_LEGAL_EFFECT_STRUCTURE` 59행 전건 추가** — 골든 경로는 부착 0/59라 F0-8b의 셋째 스켈레톤 검사가 **전 행에서 발화**한다. 골든-FL은 그 코드가 0회이므로 (라) 필드의 실제 차분은 "2행 제거"가 아니라 **"2행 제거 + 59행 추가"**다 | 골든 경로 전 행 | +| `EXPECTED_DELTA-04` | **(나) 부류 15필드는 골든 경로에서 전건 "부착 0 하의 결정론 기본값"** (공집합이 아니다 — 비교 연산자를 틀리지 않도록) — 골든 BO의 `Legal_Keywords`가 한국어 법률용어라 registry `type_id`와 교집합 0이고, `structure_index_compiler` 468–476행이 그 교집합으로만 BO를 부착하므로 **부착 0/59 · `by_bo_id` 공집합**이다(0c-1 직접 구동으로 실측). **기본값 실측** — `must_consider = False`(골든 True 59/59) · `legal_centrality = "medium"`(골든 `critical` 59/59) · `state_context = {"bo_type": …, "issue_cluster_ids": [], "asset_cluster_ids": []}` · `money_claim_effect = {"enabled": False, "source_bo_id": None}` · `derivation_basis`는 `structure_ids=…` 절만 소멸 · `linked_structures`·`claim_chain_ref`·`actio_roles` 등은 빈 구조. **15필드 각각의 기본값 표를 0c-1에서 실측해 등록한다.** + +따라서 (나)는 **골든으로 판정하지 않는다**. 6b는 "부착 0 하의 기본값"을 확인해 등록하고, (나)의 실질 검증은 **G-r2**(v.8 실산출 경로)로 옮긴다 | 골든 경로 전 행 | + +**§8 0b는 `-01`·`-02`·`-03`·`-05` **4건을 착수 전에** 등록하고, `-04`는 0c-1 직후 완결 등록한다.** 등록하지 않으면 필수 회귀 G-r이 원인 불명으로 실패한다. + +### 7.2 회귀 시험 — **58종 · 필수 15종**(굵게) + +명명은 Part 2 `M-*` · Part 3 `L-*`를 잇는다. **변조는 전부 사본 트리에서**(배포 원본 무변). 고정 스테이징 경로(`/tmp/s1`·`/tmp/s1_r0`·`/tmp/s1_p4`)는 **먼저 지운다**. + +| id | 시나리오 | 기대 | +|---|---|---| +| G-a | 필수 자산 1건 결손 | `PART4_ASSET_DEPLOYMENT_INCOMPLETE` | +| **G-b** | bundle 스키마+매니페스트 함께 옛 판본 | `PART4_LEDGER_SCHEMA_STALE` | +| **G-c** | 서명 같고 `domain_effects` 투영만 잃은 컴파일러 | `PART4_COMPILER_STALE` | +| G-d | `signal_manifest` `file_sha256` 변조 | `PART4_SIGNAL_SEAL_FAILED` | +| **G-e** | LES `signal_manifest_transaction_id` **값 변조** | `PART4_LES_TRANSACTION_MISMATCH` | +| G-e2 | LES에서 그 **키 제거** | `PART4_LES_TRANSACTION_UNSEALED_REVIEW`(경성 실패 아님) | +| G-f | 색인 도메인 순서 변경(SG-01 봉인 그대로) | `PART4_REGISTRY_INDEX_SEAL_FAILED` | +| **G-g** | `domain_effects`에 비활성 도메인 주입 | F2 **즉시 실패** `PART4_DOMAIN_EFFECT_NOT_ACTIVE` | +| G-h | 어느 LES에도 닿지 않는 BO | `FACT_ROW_UNATTACHED_REVIEW` · 행 등식 유지 | +| G-i | 어느 행에도 닿지 않는 LES 레코드 | `STRUCTURE_UNATTACHED_REVIEW` · 효과 등식 유지 | +| **G-j** | **구 게이트 등가 — 합성 fixture 4종** | §7.2a — 발화 대조는 `X1-G01`·`X1-G02` **2건**, 나머지 3건은 가시화 확인 | +| **G-j2** | **17축 정규화 리더 — 무변조 사이드카** | 127/127 판정 · **미판정 0** · blocks 불일치 0 · `on_true` 42건 발화코드 판독 확인 | +| G-k | 사이드카 1개 미배포 | `GATE_SPEC_NOT_DEPLOYED_REVIEW` · 침묵 스킵 0 | +| G-k2 | str `condition` 게이트 활성 | `GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW` + 술어 명세 인용 | +| G-k3 | 고아 gate_id 도메인 활성 | `GATE_RULE_NOT_IN_REGISTRY` (6건) | +| G-k4 | E18-G03·E20-G03 활성 | `GATE_EMITTED_CODE_NOT_IN_REGISTRY` (`DEFERRED_CALCULATION_TRACK_REVIEW`) | +| **G-k5** | **구현 3부류 각 양성** | `calc_operand`는 `SUPP-calc6` **실입력**, `any_review_code_present`·`required_slots_missing_or_conflicting`은 `SUPP-dict4` **합성 주입**으로 각 1건 이상 발화 | +| G-k12 | boundary 입력(`boundary_routes`)을 채워도 | **발화 0** · `GATE_CONDITION_KEY_UNSUPPORTED_REVIEW` 가시화(이번 회차 미구현 확인) | +| G-k7 | 입력 미도달 상태 그대로 실행 | dict 25건 **발화 0** + 영수증에 "구현 25 · 발화 0 · 입력 미도달" 계수 기록 · 침묵 스킵 0 | +| G-k8 | 정책 `implemented_condition_keys`에서 키 1개 제거 | `GATE_CONDITION_KEY_UNSUPPORTED_REVIEW` | +| G-k9 | 사이드카 차단 플래그 ↔ registry `blocks_final_drafting` 불일치 주입 | `GATE_BLOCK_FLAG_MISMATCH_REVIEW` | +| G-o2 | pack에 `unreachable_in_v8` 부류(asset_cluster·structure_role) 출현 | **실패** `PART4_UNREACHABLE_EXCEPTION_TYPE` (D-P4-2) | +| G-k6 | 봉투 부재 도메인 | `unknown_or_missing_policy` 기본값으로 **검토**(실패 아님) · 계수 기록 | +| G-l | SG-11 dangling | `CALCULATION_REF_DANGLING_REVIEW` · **등식 ④·⑤ 유지**(stub 적재로 좌우변 보존) | +| G-m | 한 사실 두 계산 동시 | `calculation_requests` 2건 · 병합 안 함 | +| **G-n** | 같은 입력 2회 | **고유 6파일 바이트 동일**(F0 4 + F2 3 − 공유 handoff 1) | +| G-o | 예외 0 | F1 `READY_NO_EXCEPTIONS` · F2 통과 | +| **G-o3** | **F1 양성 경로** — 트리거 술어(구본 **866행** `_projection_conflicts_for_row`(트리거 본문 899–925행): `calculation_required == true` **AND** `amount` 후보 2개 이상 **또는** `object_spec` 공백+후보 2개 이상)를 만족하는 행 합성 | **pack 적재** + `conflict_flags: ["CALCULATION_BASIS_CONFLICT"]` → F1 판정 → F2 수용. **구본 899–925행이 `if/elif` 배타**이므로 pack 적재 갈래에서는 `CALCULATION_BASIS_INCOMPLETE_REVIEW`가 **뜨지 않는다** | +| G-o4 | `elif` 갈래 — `amount` 또는 `object_spec` 공백(후보 다중성 없음) | pack **미적재** · `CALCULATION_BASIS_INCOMPLETE_REVIEW` 검토 기록 | +| **G-p** | 예외 >0 인데 판정 파일 부재 | `PART4_ADJUDICATION_MISSING` | +| G-q | 판정이 허용 필드 밖 수정 | `PART4_PATCH_REJECTED` | +| **G-r** | 골든 등가 — `SUPP-golden59` 조립 입력으로 실행 | **(가) 9필드 + (다) 2필드 = 11필드 59행 전건 등가** · (라) 1필드는 **`-03`(2행 제거) ∪ `-05`(59행 추가)**와 일치 · **(나) 15필드는 판정 대상 아님**(`-04`가 공집합으로 확정 — 골든 BO가 v.8 조인 규격이 아니다). 행수 59 · `F-001`~`F-059` 연속 | +| **G-r2** | **(나) 15필드 재산출 검증** — `SUPP-synth26`(26도메인 활성 · v.8 규격 합성 BO)로 실행 | `by_bo_id` 부착 > 0 · `domain_effects`가 registry `module`·`role_projection`으로 채워짐 · 구 4필드 별칭이 그로부터 결정론 파생 | +| G-s | 137종 일반성 | 사건 이름 라우팅 0 · 구 signal 이름 0 · `case_kind_domain_matrix` 0 · `Claude_YAML` 0 · **조립본 경로 0** | +| **G-t** | E-00(잔여)만 활성 / 활성 0 — **활성 0은 수제 LES가 전제다**(`compile_structure_seeds` 193–194행이 `PART3_EXPECTED_RUNNABLE_EMPTY`로 경성 실패하므로 정상 상류로는 만들 수 없다) | 등식 통과 · **실패 아님**(fail-open) · **E-00 레코드의 `review_required: True`가 `final_writer_status == READY_WITH_REVIEW`로 이어짐**(§3.2 — `review_codes`는 빈 채로 온다) | +| **G-u** | 행 1건 삭제 | `PART4_ROW_CONSERVATION_BROKEN` | +| G-u2 | BO 1건 누락 | `PART4_BO_CONSERVATION_BROKEN` | +| G-u3 | `fact_id` 불연속 주입 | `PART4_FACT_ID_SEQUENCE_BROKEN` | +| G-v | `domain_effects` 키 1개 제거 | `PART4_EFFECT_CONSERVATION_BROKEN` | +| G-v2 | 판정 커버리지 깨기(`expected ≠ decided ∪ blocked`) | `PART4_COVERAGE_BROKEN`(구본 1684–1758 자구) | +| G-w | SG-11 레코드 추가(원천 차원) | `PART4_CALC_SOURCE_CONSERVATION_BROKEN` | +| G-w3 | 행의 `calculation_requests` 1건 삭제(행 차원) | `PART4_CALC_CONSERVATION_BROKEN` | +| G-x | 29키 밖 키 주입 | `PART4_LEDGER_SCHEMA_INVALID` | +| G-y | 정책 판본 변조(매니페스트 동반) | `PART4_POLICY_INVALID` | +| **G-z** | 미러 해시 변조(매니페스트 **미동반**) | `MODULE_MIRROR_HASH_MISMATCH` — 해시 게이트 자체의 발화 증명 | +| G-s3 | `object_spec` 우변 미결 상태로 실행 | `OBJECT_SPEC_SOURCE_UNRESOLVED_REVIEW` 계수 · 59행 전건 · 침묵 0 | +| G-z2 | 자기선언 ↔ `alias_targets` 양방향 차집합 | **누락 4건**(E-09·X2·X1·X3)에 각각 `LEGACY_ALIAS_SELF_MISMATCH_REVIEW` 계수 + **역방향 과잉 1건**(B5→E-10) · 약칭 3종 정규화 확인 · 자산 파생 목록(1건)과 불일치 시 `LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW` · 침묵 탈락 0 | +| G-k10 | P2 handoff `source_review_code`에 BO 필드명·`None` 혼입 | `P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW` 계수 · 코드 승격 0 | +| G-k11 | 같은 코드가 두 도메인에 걸린 handoff 항목 | `source_domain` 귀속 후 **해당 도메인 게이트에만** 공급 · 교차 오발화 0 | +| G-k13 | P2-F0 유래 handoff 항목(`source_domain` = `candidate_ref` 접두사) | `P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW` 계수 · 어느 게이트에도 미공급 | +| G-w2 | v.8 경로(26-B)에서 한 도메인에 BO 2건 · SG-11 1건 | 등식 ④ 성립(행 차원) · ⑤ 성립(원천 차원) · 옛 혼합 등식이면 실패했을 자리 | +| G-v3 | 후보 BO 집합 ≠ 최종 행 BO 집합 | `PART4_BO_CONSERVATION_BROKEN`(구본 1898–1902 자구) | +| G-v4 | LES 부착 커버리지 깨기 | `PART4_BO_ATTACHMENT_COVERAGE_BROKEN` | +| G-s2 | 파생 사실 공백 / 증거 참조 공백 / LES 미부착 행 각 1건 | `EMPTY_DERIVED_FACT` · `NO_EVIDENCE_REFS` · `NO_LINKED_LEGAL_EFFECT_STRUCTURE` 각 발화(F0-8b) | +| G-j3 | 도메인 발화의 **3분기 투영**(§4.4) | ① 코드가 R(d) 전 행에 합집합 부착 · ② `row_effect.must_consider` 48건이 **OR 갱신**(기존 True 미소실) · ③ `on_true.preserve_seed` 42건은 writer report에만 · R(d) 공집합이면 셋 다 행 미도달 + 계수 | +| G-l2 | SG-11 레코드에 `details.calculation_id` 없음(원천 10/16) | `signal_id`로 대체 · `CALCULATION_ID_SUBSTITUTED_REVIEW` 계수 · 계산 등식 유지 · 버림 0 | +| G-r3 | 참조 증거가 `evidence_authority`에 없는 행 | `EVIDENCE_REF_NOT_IN_EVIDENCE_UNIVERSE` 계수 · 침묵 0. **값은 `low`/`low`가 아니다** — stub이 증거 행으로 계수되므로 §7.1a (다) 5갈래가 그대로 적용된다(1건이면 `medium`/`medium`, 2건 이상이면 `high`/`high`). `low`/`low`는 `evidence_refs` 자체가 빌 때뿐 | +| G-z3 | 미러 매니페스트 미등재 | `MODULE_MIRROR_UNREGISTERED` | + +**계수** — 행 58 · 필수(굵게) 15 · id 중복 0. (id는 `G-a`~`G-z3`이며 알파벳 연속이 아니라 계열 확장 표기다 — 결번 개념이 없다.) + +**음성 짝** — 보존 등식·스키마 = G-u·G-u2·G-u3·G-v·G-v2·G-w·G-x · 자산 게이트 = G-y·G-z·G-z3 · 게이트 가시화 = G-k·G-k2·G-k3·G-k4·G-k6·G-k7·G-k8·G-k9·G-k10·G-k11·G-k12·G-k13 · 증거 = G-r3 · 판정 = G-o2·G-p·G-q. **양성만 재면 게이트가 죽었는지 모른다.** + +#### 7.2a G-j — 합성 fixture가 필요한 이유 + +골든 게이트 발화 분포 실측: + +``` +skeleton_validation_codes = {LEGAL_CAUSE_STATE_UNKNOWN_REVIEW: 2, NO_EVIDENCE_REFS: 1} +must_consider True = 59 / 59 +``` + +(`NO_EVIDENCE_REFS`는 게이트 코드가 아니다 — 사이드카·registry 어느 쪽에도 발화원이 없고 구본의 행 스켈레톤 검사 산물이다. 게이트 발화는 `LEGAL_CAUSE_STATE_UNKNOWN_REVIEW` 2건뿐이다.) + +골든이 실증하는 승계 게이트는 **`E11-G01` 하나뿐**이고 나머지 4개 코드는 **0회 발화**한다. 또 `must_consider`가 59/59라 **변별력이 없다** — 등가 판정은 `must_consider`가 아니라 **`skeleton_validation_codes` 집합**으로 잡는다. + +**두 층의 fixture가 필요하다.** X1-G01·X1-G02는 사실을 재판정하지 않고 **이미 발행된 검토 코드를 집계**하므로(§1.4), BO 사실만 만들어서는 발화하지 않는다. + +| 층 | 내용 | 대조 대상 | +|---|---|---| +| **코드 층**(이번 회차) | 봉투 `review_items`에 X1-G01·G02의 조건 코드(`X1_REQUIRED_ELEMENT_GAP_REVIEW`·`X1_ARRIVAL_UNPROVEN_REVIEW`·`NOTICE_RECEIPT_UNVERIFIED`·`BLOCK_FINAL_DRAFTING_NOTICE_RECEIPT_MISSING` / `X1_LIFECYCLE_DATE_CONFLICT`·`X1_RECIPIENT_OR_CONTENT_CONFLICT`·`NOTICE_RECEIPT_EVIDENCE_WEAK_REVIEW`)를 **직접 주입** | 발화 코드 · 차단 표지 · **`writer_effect.status_when_triggered`**를 동결 계약 조건절과 대조. **`row_effect`는 대조 대상이 아니다** — `X1-G01`·`X1-G02`는 축 12 기준 row 효과가 없는 37건에 속한다 | +| **사실 층**(이월) | ①' **골든 실발화 갈래** — `possession_state.events` 있고 `legal_cause_assertions` 0건(bh19·bh21 형) · ① 같은 `asset_ref`·중첩 기간의 양립 불가 점유원인 쌍 ② `requires_receipt_for_effect=true`인데 도달·수령일 모두 없음 ③ 수령일은 있으나 `receipt_evidence_refs` 비고 `recipient_admission_text` 없음 ④ `lien_state.extinction_notice_refs`의 도달일보다 점유 개시일이 늦음 | **BO 사실 → 검토 코드 발행은 Part 2 X1 워커(LLM)의 몫**이다. 이 사슬은 pass4 범위 밖이며 §9-3과 함께 열린다. 지금 만들어 두는 이유는 술어 명세 전환 회차(§9-4)가 그대로 쓸 회귀 자산이기 때문이다 | + +**따라서 이번 회차의 발화 등가는 "합성 코드 주입 하에서 `X1-G01`·`X1-G02` 2건"으로 한정된다.** 이것은 **평가기 정확성**의 증명이지 상류 소비 정합의 증명이 아니다(§7.5-2b). + +①④가 대상으로 삼는 `E10-G05`·`E10-G06`·`E11-G01`은 str `condition`이므로 발화가 아니라 `GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW` **가시화와 술어 명세 인용**을 확인한다. + +코드 층은 `SUPP-gate4`, 사실 층은 `SUPP-gate4-facts`(이월 자산)로 각각 영구 기록한다. + +### 7.3 정적 검사 12종 (공통 10 + Part 4 고유 2) + +1. YAML 구문 · 2. `task_name` ↔ 그래프 노드 1:1 · 3. **선언 1벌 계수** · 4. DAG 정합 · 5. code-executor `parameters` 5키 · 6. **embedded python AST**(실패 0 · 미정의 이름 0) · 7. 개별 조각 ↔ 통합본 블록 대조 · 8. 금지 참조(**산출 YAML 안** `Claude_YAML` 0 · 구 signal 3종 0 · `case_kind_domain_matrix` 0 · 조립본 경로 0 · 사건명 런타임 조회 0) · 9. 인계면 검사기 4-yaml 전수 · 10. 매니페스트 등재-실물 전수 해시 + +**Part 4 고유** — 11. **27필드 + 합성 규칙 + `type_id` 리터럴 단일 정의 검사** — 정책 자산 밖에 27필드 리터럴 0 · (가) 9필드 합성 규칙과 (다) 증거 산정 규칙(마커 10종·300자 절단·판정 순서)이 정책 자산에만 있고 모듈에 하드코딩 0 · **모듈·YAML 안 registry `type_id` 리터럴 0** — 구본은 `_legal_calculation_object` 5원소 외에도 720·721·726행에 `"money_claim" in types`·`"commercial_successor" in types`·`{"fraudulent_transfer","preserved_claim"}`을 갖는다(넷 다 registry `type_id`에 실재). 이 검사가 없으면 §1.7의 상수표 삭제가 반쪽이 된다 · 12. **gate_id 하드코딩 부재 검사**(모듈·YAML에 gate_id 리터럴 0 — 사이드카·registry에서만 온다) + +**검사 3·6이 특히 중요하다.** 조각 병합에서 `tasks:`/`task_procedure:`가 여러 벌로 나와도 **YAML 구문은 통과한다**(같은 층 중복 키는 뒤엣것이 이김). 중복 키를 잡는 것은 파서가 아니라 선언 계수 검사다. 구문 통과·문자열 부재는 실행 가능의 증거가 아니다 — Part 2 R0에서 정의 없는 이름 5개가 사용처 8곳에 남아 AST 검사가 잡은 실측 사례가 있다. + +### 7.4 예행 — 4패스 + 보충 실측 6종(+이월 1종) + +Part 3의 3패스 뒤에 **pass4(F0·F1·F2)**를 잇는다. 하네스 규율 — task의 `_call`을 갈아 끼우지 않고 **`httpx.Client.post` 자리에서 JSON-RPC를 받는다.** + +| 패스 | 내용 | +|---|---| +| pass1 | Part 1 code task 5종 + P2-A0 | +| (사이) | seed 대역 | +| pass2 | P2 R0 · F0 · S0 | +| pass3 | P3 L0 · L2 | +| **pass4** | **P4 F0 · F1(대역) · F2** | + +**예행 한계와 보완** — 대역 스크리너가 registry 단서만 쓰므로 pass4 활성 도메인이 X1·X2·X3 셋에 그치고, §1.6의 5개 게이트 중 `X1-G01`·`X1-G02`만 활성 경로에 오른다. + +**`SUPP-golden59`의 활성 집합은 골든-S2 SG-01이 정한다 — 11도메인(E-02·03·04·09·10·11·13·15·X1·X2·X3) · 게이트 48개.** X1이 포함되므로 §1.6의 `X1-G01`·`X1-G02`가 G-r 경로에서도 순회된다. `E10-G05`·`E10-G06`·`E11-G01`도 활성이나 str `condition`이라 가시화 갈래다(§1.6). + +- **`SUPP-synth26`** — **두 조각이다.** + - **26-A(SG-01만)** — 26 도메인 전부를 `expected_runnable`로 둔 합성 SG-01. 기대: registry 121 전건 순회 · **구현 25 평가 · 가시화 96(str 90 + boundary 6) · 고아 6 검토**. **활성만 열 뿐 입력을 채우지 않으므로 실발화는 0이다** + - **26-B(v.8 규격 BO + 재조립 LES)** — `Legal_Keywords`에 **registry `type_id`**를 채운 최소 BO n건을 만들고(v.8 Part 2 1262행 규격) `compile_structure_seeds` → L2 재조립을 돌려 **부착 > 0인 LES**를 얻는다. **G-r2의 입력이다.** 골든 BO로는 만들 수 없다(교집합 0 — §0.1) + + **대안** — 예행 pass4가 P2-F0의 실 v.8 BO와 P3-L2의 실 LES를 이미 만든다. 26-B 대신 **pass4 산출을 G-r2 입력으로 써도 된다**(자료 제작 0). 활성 도메인이 X1·X2·X3로 좁은 것이 유일한 한계다 +- **`SUPP-dict4`** — **2부류**에 합성 입력을 주입해 평가기 발화를 확인. **두 도메인 집합을 켠 SG-01이 필요하다**(한 매니페스트에 함께 켤 수 있다) — `any_review_code_present` 13건은 E-00·E-15·X1·X3에, `required_slots_missing_or_conflicting` 6건은 E-14·17·18·19·20·21에 산다. 활성되지 않으면 게이트 집합 `G`에 들어오지도 않는다. `calculation_operand_state_in`은 `SUPP-calc6`이 실입력으로 덮고, `boundary`는 이번 회차 가시화 대상이라 제외한다. **G-k5의 3부류 = 이 2부류 + `SUPP-calc6` 1부류** +- **`SUPP-golden59`** — 골든-FL의 BO 59건 위에 만든 **G-r 전용 입력 조립본**. 골든-FL에는 `signal_manifest`·SG 5종·봉투·`domain_activation_manifest`·`evidence_indexed`가 없고 LES도 레거시 형태이므로(§0.1) 이 조립 없이는 F0가 진입 봉인에서 선다. + + **골든-S2가 신호 전 세트를 실물로 갖고 있다** — 손합성하지 않는다. `ls 선행구축/S_5_GPT_v2/e2e/run_1/golden_s2/` 실측: `domain_activation_manifest.json`(SG-01 · `expected_runnable` **11도메인** · `registry_index_sha256` 실적재) · `signal_manifest.json`(**봉인된** `transaction_id` · `files[]` 27) · **SG 13종 전부** · `domain_signals/` 11 · `compatibility_views/`. 2-run 바이트 동일. + + | 조각 | 만드는 법 | + |---|---| + | SG-01 · `signal_manifest` · SG 5종 · 봉투 | **골든-S2 실물을 그대로 쓴다.** 봉인·해시 사슬이 이미 정합하므로 §4.1④ 진입 봉인 셋이 그대로 통과한다 | + | BO · Fact Ledger 기준선 | **골든-FL 실물**(`canonical/BO.json` 59건 · `fact_ledger/Fact_Ledger_base.json`) | + | P2 review handoff | **골든-FL 실물**(`gates/` 아래) | + | `evidence_indexed.json` | 골든-FL·골든-S2 둘 다 없다. **`S_0_GPT_v2/fixtures/legacy_part1/run_1/evidence_indexed.json`을 쓴다** — 골든 BO가 참조하는 증거 30건을 전건 덮고(covered 30/30) (다) 2필드가 **59/59 재현**됨을 실측했다 | + | v.8 LES | **유일한 합성분.** `structure_index_compiler`를 **골든 BO + registry + 골든-S2 SG-01(11도메인)**로 직접 구동한 뒤 **L2 재조립**(§8 0c-0)을 얹는다. 인자 12개 전건 공급 — `activation_manifest`·`registry_index`·`domain_configs`·**`domain_sidecars`**(=`{structure_types, module_role_projection}` 쌍 · `gates.json`을 넣으면 `admission_policy: absent`로 어긋난다)·`policy`(`structure_index_policy.v1.json`)·`alias_declarations`·`sg13/sg05/sg11`(골든-S2 실물)·`domain_envelopes`(골든-S2 실물)·`bo_records`·`slice_documents`(부재는 `audit.input_gaps` 계수) | + + **활성 집합은 SG-01 실물이 정한다 — 11도메인 · 게이트 48개.** 별칭 해소 결과(7 또는 8)는 **활성 집합을 정하지 않는다**; 그것은 F0-0b가 BO를 도메인에 귀속시킬 때만 쓰이고, 자기선언 불일치는 계수 대상이다(D-P4-7). **X1·X2·X3·E-09가 모두 활성이므로 pass4와 G-r이 서로 다른 게이트를 밟는다는 앞선 서술은 성립하지 않는다.** + + **이 조립본 위의 등가는 "평가기 정확성"이지 "상류 정합"이 아니다** — §7.5-2b와 같은 구분을 적용한다. 합성 조각(SG·봉투·SG-01)은 값을 만들지 않으므로 (나) 부류 diff는 **v.8 LES 재산출분만** 반영한다. +- **`SUPP-calc6`** — **도메인 재라벨 합성본이다.** `details.operand_state` 실적재 6건은 **`domain_ids`가 전부 `E-05`**인데 `calculation_operand_state_in` 게이트는 **E-14·17·18·19·20·21**에 산다 — 교집합 0. F0-7b(iii)의 도메인 귀속 규칙을 지키면 닿지 않고, 어기면 문서가 금지한 교차 오발화가 된다. 따라서 네 조각을 함께 만든다 — ① 레코드의 `domain_ids`·`details.domain_ids`·`details.source_bo_ids` 재라벨 ② 그 도메인을 켠 SG-01 ③ **E-14~21 봉투의 `calculation_refs`에 그 `signal_id`를 넣는다**(조인 경로는 `봉투.calculation_refs ↔ SG-11.signal_id` → LES `calculation_request_refs` → BO 부착 → 행 — 컴파일러 420–439·454–478행) ④ **`Legal_Keywords`에 그 도메인 `type_id`를 넣은 BO**(부착 없으면 행에 실리지 않는다). `calculation_id`는 대상 도메인 `calculation_bindings` 안 값으로 맞춘다 +- **`SUPP-gate4`** — §7.2a 합성 fixture 4종 +- **`SUPP-reader13`** — 17축 정규화 리더가 127/127을 판정하고 미판정 0임을 배포 직후 재측정(§8 2단계 산출을 영구 기록) + +**F1의 실제 LLM 판정 품질은 대역으로 닫히지 않으며 미측정으로 남긴다.** + +### 7.5 완수 판정 (제약 3) + +1. **의도한 산출물이 올바른 방식으로 생성** — 래핑 없는 배열 · 모든 행 29키 · `fact_id` `F-001`부터 연속 · 닫힌 스키마 통과 +2a. **upstream 소비 정합 (이번 회차 판정 대상)** — F0가 Part 3 v.8의 `structure_records`·3색인을 실제로 읽어 `domain_effects`를 채운다. LES 거래 대조 통과 또는 검토 기록. SG-11 `details.operand_state` 승계 확인(**`SUPP-calc6` 실입력 하에서** — 골든-S2 SG-11은 0레코드다) +2b. **게이트 실발화 (미측정으로 명시)** — 봉투 유래 dict 게이트가 실입력으로 발화하는 것은 상류 보강(§9-3·6) 완료 후로 미룬다. 이번 회차는 `SUPP-dict4`·`SUPP-gate4` **합성 주입으로 평가기만** 검증한다. **합성 주입 통과를 "게이트가 실제로 산다"로 기록하지 않는다** — 라운드 3·4가 같은 자리에서 두 번 넘어졌다 +3. **downstream handoff 정합** — Stage 2가 읽을 색인·보고서가 선언 포맷대로. 인계면 55행 등재 + `handoff_count` 갱신 + 검사기 finding 0 +4. **검증이 실제로 수행됐음이 증거로 남는다** — 영수증에 `schema_injection` 실측값·게이트 실측 경로·봉인 셋 결과·**17축 방언 지도와 127/127 판정**을 적는다. **상수 PASS를 적지 않는다.** + + 그리고 **"선언 존재"와 "실측 비공백"을 따로 적는다** — 게이트 입력 4종(`operand_state`·`review_items`·`evidence_slot_status`·`boundary_routes` 계열) 각각에 대해 *스키마가 요구하는가*와 *이번 실행에서 몇 건이 실제로 채워졌는가*를 별도 칸으로 기록한다. 라운드 3에서 "스키마 필수인데 실물 전부 공백"을 놓쳐 게이트 25건을 실행으로 오분류한 자리다 +5. **침묵 0** — 부착 실패 · 게이트 미배포/미구현/고아/코드 초과 · dangling · 별칭 자기모순이 전부 코드로 가시화되고 계수된다 + +--- + +## 8. 실행 순서 — **21단계** + +| 단계 | 내용 | YAML | +|---|---|---| +| **0a** | **boundary 선언 대체가능성 정적 대조 → D-P4-c 결정** — **`source_refs`가 배포 선언에 0회**(실측 확정)라는 단일 사실로 판정한다 — "근거 있는 중첩"을 강제할 수단이 배포본에 없으므로 게이트 `condition` 값을 읽지 않고도 (가)가 확정된다. 골든 실행이 필요 없다(골든 BO의 boundary 키는 0건이라 실행으로는 근거가 나오지 않는다). **0보다 앞에 둔다 — D-P4-c가 `EXPECTED_DELTA-02` 상한(21 vs 27)의 결정 함수이기 때문이다.** **0a의 판정 기준은 단일 사실로 닫힌다** — `source_refs`가 **배포 선언에 0회**(실측 확정)이므로, "근거 있는 중첩"을 강제할 수단이 배포본에 없다는 것이 게이트 `condition` 값을 읽지 않고도 확정된다. 따라서 **0a는 (가)를 결론으로 낼 수 있고 2단계를 역행 참조하지 않는다.** (나)를 검토하려면 `condition` 값이 필요하므로 그 경우에만 2단계 뒤로 미룬다 | — | +| 0 | **결선 확정** — §10 미결 **4건**. D-P4-c는 0a에서 이미 결정됐고, D-P4-a·b는 여기서 확정한다 | — | +| 0b | **등가 예외 등록** — `-01`·`-02`·`-03`·**`-05`** 4건을 착수 전에 등록한다. **`-04`는 0c-1 직후 "골든 경로 공집합"으로 완결 등록한다**(컴파일러 없이 확정된다 — 부착 0/59는 `structure_index_compiler` 구동만으로 나온다) | — | +| **0c-0** | **L2 재조립부 작성** — `structure_index_compiler`가 내는 것은 seed bundle(`stage1_structure_seed_bundle.v1`)이고 최종 LES가 아니다. 색인 3종(`by_domain_id`·`by_module`·`by_bo_id`)·`quality_gate`·최상위 required 8키 + 선택 4키 치환은 **P3-L2 YAML 로직에만 있으므로** 그 조립부를 순수 함수로 재작성한다. **0c-1과 8단계가 둘 다 이것을 전제한다** | — | +| 0c-1 | **`SUPP-golden59` 입력 조립 + 규칙 재현 확인** — 조립 후 **(가) 9필드·(다) 2필드 규칙만 보고 다시 구현해 골든 59/59가 재현되는지** 확인한다(규율 11). `EXPECTED_DELTA-04`(골든 기본값)도 여기서 완결 등록한다 — §7.4 우회 레시피(L2 재조립 포함). **여기서는 입력만 만든다** — (나) diff는 컴파일러가 있어야 재므로 6b로 미룬다 | — | +| 1 | **사이드카 25 복사 배포** + 원천 정합(조립본 `domain_config` 26/26 바이트 동일) | — | +| 2 | **17축 정규화 리더 먼저 작성** → 127규칙·registry 121 커버·고아 6·**미판정 0**·발화코드 초과 2를 **리더로 재측정**. 이 리더로 잰 값만 근거로 삼는다 (`SUPP-reader13` 기록) | — | +| 3 | 5 gate_id의 `condition` 형·차단 경로 확정(리더 산출) → §4.4 구현 범위 확정 | — | +| 4a | `fact_ledger_policy.v1.json` 작성 | — | +| 4b | **상류 스키마 보강** — `legal_effect_structures.schema.json` `required`에 `signal_manifest_transaction_id` 추가(+매니페스트 해시 갱신) | — | +| 5 | 닫힌 스키마 3종 작성 | — | +| 6 | `fact_ledger_compiler.py` + `.txt` 미러 + 순수성 확인(2회 호출 직렬화 동일) | — | + +| 7 | 매니페스트 **N→N+31** 갱신(N은 착수 직전 실측) + 전수 재계산 | — | +| 8 | **회귀 데이터 제작** — `SUPP-gate4`(코드 층 4종) · `SUPP-dict4`(2부류) · `SUPP-calc6`(재라벨 4조각) · **`SUPP-synth26` 26-A(합성 SG-01) + 26-B(v.8 규격 BO + 재조립 LES)** · **G-o3/G-o4 트리거 합성 행**. 네 픽스처 모두 **대상 도메인 `type_id`를 실은 BO + 재조립 LES**를 포함해야 발화가 행에 닿는다(§4.4 투영). 사실 층 `SUPP-gate4-facts`는 §9-3 해소 회차로 이월 | — | +| 9 | 조각 후보본 3종 작성 | — | +| 10 | 통합본 조립 → **정적 검사 12종** → 개별↔통합 블록 대조 | `stage_1_part_4_v.8.yml` | +| 11 | 예행 pass4 + `SUPP-synth26` + `SUPP-gate4` | — | +| 11b | **(나) 재산출 기준선** — 8단계가 만든 `SUPP-synth26` **26-B**(또는 11단계 pass4 산출)에 컴파일러를 돌려 (나) 15필드의 **부착 > 0 기준선**을 만든다. **8·11 뒤에 둔다**(앞에 두면 역행). `EXPECTED_DELTA-04`(골든 기본값)는 0c-1에서 이미 확정되므로 다시 재지 않는다 | — | +| 12 | 회귀 G-a~G-z3 (**58종**) | — | +| 13 | 인계면 45→55 + `handoff_count` 갱신 + `stage_chain` 갱신 → 검사기 4-yaml finding 0 | — | +| 14 | 분석서 작성 + 자산 배포 기록 갱신 + MEMORY.md | — | + +**중단 규칙** — 2단계 리더 재측정이 §1.5 수치와 다르면 이 문서를 먼저 고친다. 7단계 전수 재계산 불일치 시 즉시 정지. 10단계 검사 3(선언 계수) 또는 6(AST) 실패 시 조립을 다시 한다. 12단계 **필수 15종** 중 하나라도 기대와 다르면 원인을 닫기 전에 진행하지 않는다. + +**순서의 이유** — 1·2단계가 앞에 오는 것은 "**방언이 갈린 자료에서는 정규화 리더를 먼저 만들고 그 리더로 잰 값만 근거로 삼는다**"는 MEMORY 교훈 때문이다. 이 문서의 §1.5 수치도 임시 스크립트로 잰 것이므로 2단계에서 재측정한다. + +--- + +## 9. 이월 (이 회차 범위 밖) + +| # | 항목 | 소유 | 이번 회차 영향 | +|---|---|---|---| +| **9-1** | 사이드카 방언 통일 **17축**(§1.5a) | registry 소유자 | 정규화 리더로 흡수 | +| **9-2** | 고아 gate_id 6건(`E14-G04`·`E17-G04`·`E18-G04`·`E19-G04`·`E20-G04`·`E21-G04`, `*_attempted` 2키) — registry 등재 또는 사이드카 제거 | registry 소유자 | 검토 가시화 | +| **9-3** | **registry 도메인 검토코드 어휘가 워커에 전달되지 않는다** — slice `domain_declarations`(`domain_slice_compiler.py` 277행)에 `review_codes`가 없다. 전달되는 통로는 `part1_review_guard.review_item_ids`(Part 1 인스턴스 코드, 497행) 하나뿐. **두 통로 중 어느 쪽을 열지 결정 필요** | Part 2 | `any_review_code_present` 13 미도달 | +| **9-4** | str `condition` **90건**의 술어 명세 → 실행 가능 형태 전환. `E10-G05`·`E10-G06`·`E11-G01`이 포함되므로 **발화 등가의 나머지가 여기 달려 있다. 단 `EXPECTED_DELTA-03`은 선언 술어를 구현해도 복원되지 않는다** — registry 소유자가 구본 778–779 갈래를 술어로 새로 선언해야 비로소 복원 가능하다 | registry 소유자 + 후속 회차 | 가시화 90 | +| **9-5** | **boundary 정의처 미배포** — 형태 `domains//domain_extension.schema.json`(전체 25개 중 boundary_* 보유 **9**) · 의미 `domains//Stage2_handoff_contract.json`(`conditional_boundary_handoffs[].activation = "source-backed overlap only"`). 배포하면 6건이 구현으로 승격 | 조립본 동기화 + registry 소유자 | `boundary_or_overlap_detected` 6 가시화 | +| **9-6** | **봉투 `evidence_slot_status`가 `missing`·`conflicted`를 산출한 적이 없다** — 112파일 distinct 실측 `filled` 11 · `partial` 8 · **`missing` 0 · `conflicted` 0**. 즉 막힌 것은 "채움"이 아니라 **그 두 상태의 판정 경로 존재 여부**다. 슬롯 항목이 `review_code`를 직접 나르므로(실적재 `E14_ORDER_SERVICE_FINALITY_REVIEW`·`E17_LAW_VERSION_REVIEW`) 판정 경로만 생기면 registry 어휘 없이 동작한다 — 9-3보다 우선순위가 높다 | Part 2 | `required_slots_missing_or_conflicting` 6 미도달 | +| **9-7** | 발화코드 ⊄ registry `review_codes` **2건**(`E18-G03`·`E20-G03`의 `DEFERRED_CALCULATION_TRACK_REVIEW`) — 이번 회차 구현 부류에 속하므로 발화 시 즉시 드러난다 | registry 소유자 | `GATE_EMITTED_CODE_NOT_IN_REGISTRY` | +| **9-8** | `legacy_declaration_mismatches` **파생 목록이 원본과 어긋난다** — 실제 누락 **4건**(E-09·X2·X1·X3) + 역방향 과잉 1건 + 자기선언 값의 약칭 3종 | registry 소유자 | `LEGACY_ALIAS_SELF_MISMATCH_REVIEW` | +| **9-9** | **조립본 전용 자산 — 참조 문서 3종**: `platform/contracts/path_contract.json` · `compatibility/legacy_contracts/part4/deterministic_gates_contract.json` · `structure_projection_contract.json`. 배포하거나 참조를 조립본 경로로 명시 | 조립본 동기화 회차 | 문서 참조만 | +| **9-10** | **조립본 전용 자산 — 구현 입력 정의처**: `domains//domain_extension.schema.json` **25개 · boundary_* 키 보유 10 · `boundary_routes` 보유 6 · `source_refs` 보유 18** · `Stage2_handoff_contract.json` ×13 · `calculation_registry_bindings` 계열. **9-9와 등급이 다르다 — 이것이 없으면 구현이 창작이 된다** | 조립본 동기화 회차 | boundary 6 미구현의 직접 원인 | +| **9-11** | 신설 자산·사이드카의 조립본 반영 + 빌더 등록(**파일 반영과 등록은 한 벌** — 등록만 선행하면 `MISSING_SELECTED_SOURCE` 즉사) | 조립본 동기화 회차 | — | +| **9-12** | 매니페스트 432항 중 **277항이 배포 트리에 실물 없음**(`computations/` 205 전건 포함) — 설계상 정상(상위 장부)이나 `calculation_bindings`의 피연산자 정의가 여기 있어 계산 게이트의 심화 판정이 막혀 있다 | 설계 판단 | — | +| **9-13** | **골든-S2 SG-11이 0레코드**(`status: EMPTY`) — `calculation_operand_state_in` 6건이 골든 경로에서 한 번도 구동되지 않는다. `s4_states` 계열로만 실입력 확인 가능 | Part 2 예행 자산 | `SUPP-calc6` 필요 | +| **9-14** | **행동 검증(판정의 법률적 타당성)은 미측정으로 남는다** — 실 LLM 실주행과 법률 검토가 필요하며 대역으로 닫히지 않는다 | — | — | + +## 10. 착수 전 결선 필요 4건 + +| # | 사안 | 선택지 | 권고 | +|---|---|---|---| +| **D-P4-a** | 구 4필드 별칭의 존속 기간 | (가) 한 버전(v.8)만 (나) 무기한 | **(가)** — 전략서 §0.2 "구 필드는 한 버전 동안 별칭으로 병기". v.9 제거 예정을 정책 자산 `deprecation` 절에 명기 | +| **D-P4-b** | str `condition` 90건의 이번 회차 처리 | (가) 검토 가시화만 (나) 일부라도 구현 | **(가)** — 술어 명세를 코드로 옮기는 것은 판정식 창작이다. **"골든이 검증하니 3건은 예외로 구현하자"는 갈래를 검토했으나 실측으로 폐기했다** — 골든의 유일 발화 2행(bh19·bh21)은 `possession_state.events` 1건 · `legal_cause_assertions` **0건**이라 `E11-G01`의 선언 condition("양립 불가능한 점유원인 **쌍**이 존재")이 성립할 수 없다. 골든 발화는 registry가 선언하지 않은 구본 778–779행 갈래(`if events and not causes`)에서 나온 것이므로, 그것을 옮기면 **registry·동결 계약 어디에도 없는 술어를 창작**하는 것이 된다. 따라서 `EXPECTED_DELTA-03`은 §9-4 완료 전까지 **확정 차분**이다 | +| **D-P4-c** | `boundary_or_overlap_detected` 6건을 배포된 선언만으로 구현할 것인가 | (가) 가시화(현행 초안) (나) **배포된 `extension_payload_key_declarations.v1.json`의 키 선언 + 게이트 `condition`의 도메인 목록**만으로 구현. **주의 — `source_refs`는 배포본에 없는 개념이다**(배포 선언 0회 · 조립본 `domain_extension.schema.json` 18/25에만 존재). (나)를 택하면 "근거 있는 중첩"을 강제할 수단 없이 키 존재만으로 판정하게 된다 | **§8 0a 정적 대조로 결정** — 배포 선언 ∪ 게이트 조건이 미배포 스키마의 항목 enum·`source_refs` 필수를 대체하는지 **파일 대조**로 판정한다. 대체 가능하면 (나), 아니면 (가). **현행 본문은 (가)를 가정해 쓰였다 — 아래 파급표 참조** | + +| **D-P4-d** | `object_spec`의 v.8 우변 | (가) `core.Object` 단독(폴백 없음 — 골든 0/59라 전건 `null`) (나) LES `evidence_refs`·`routes` 중 하나로 폴백 (다) 리터럴 `"[]"` 유지 | **§8 0c-1에서 결정** — 어느 쪽이든 `EXPECTED_DELTA`에 등록한다. 결정 전에는 `OBJECT_SPEC_SOURCE_UNRESOLVED_REVIEW`로 가시화 | + +**D-P4-c 파급표 — 본문은 (가)를 가정해 쓰였다.** (나)를 택하면 아래가 동시에 뒤집히므로, 0a 결과가 (나)면 이 표의 자리들을 함께 고친다. + +| 항목 | (가) 가시화 — **현행 본문** | (나) 구현 시 | +|---|---|---| +| §0 표 · §1.5b · §4.4 구현/가시화 | 25 / 96 | **31 / 90** | +| `EXPECTED_DELTA-02` 상한(§1.6 · §7.1b · §8-0a 세 곳) | 10+6+6−1 = **21** | 10+6+6+6−1 = **27** | +| §7.2 G-k12 기대 | 발화 0 · 가시화 확인 | **발화 검증으로 반전** | +| §9-5 · §9-10 | 이월 | **이월 유지** — (나)는 미배포 정의처를 배포하지 않고 배포 선언만으로 구현하는 갈래이므로, "정의처 없이 구현했다"는 사실이 오히려 이월 근거를 강화한다. §9-10은 boundary 외 자산도 담으므로 통째 취소하지 않는다 | +| §7.4 `SUPP-dict4` · §8-8 | 2부류 | **3부류**(boundary 추가) | +| §7.2 **G-k5**(필수) 기대 | 구현 **3부류** 각 양성 | **4부류** 각 양성 | +| §7.4 `SUPP-synth26` 기대 | 구현 25 평가 · 가시화 96 | **구현 31 · 가시화 90** | +| §6.1 정책 `implemented_condition_keys[]` | 3키 | **4키**(`boundary_or_overlap_detected` 추가) | +| §4.2 F0-7b 재료 ④ | 계수 전용 | **판정 입력** | + +나머지 dict 부류(any_review 13 · required_slots 6 · calc_operand 6)의 구현 범위는 §1.5b 실측으로 결정됐으므로 결선 사안이 아니다. + +--- + +## 11. 근거 + +| 주장 | 근거 | +|---|---| +| 구본 LES 계약 불일치·침묵/경성 두 겹(§1.1) | 구본 399–448행 파싱 ↔ `legal_effect_structures.schema.json` `required`/`properties` | +| `downstream_read_sets.part4_F0`(§1.2) | `signals/compiler/signal_compiler.py` 166–170행(part4_F0 = 168행) + `stage_1_update_strategy.md` 163행 | +| SG-07 상시 공백 | 인계면 SG-07 행 `optional: true` · note 원문 + Part 3 분석서 §3·§8.2 | +| 게이트 사실 원천 = BO payload(§1.3) | 구본 768·772·809·811·825·830·138–140행 + 골든 BO payload 키 분포 | +| BO 18키 닫힘 · `extensions`는 `domain_payload` 단일 | Part 2 v.8 1954–1959 · 2411행 | +| **봉투가 `review_items`·`evidence_slot_status` 필수 13키 보유**(§1.4) | `signals/schemas/domain_signal_envelope.schema.v2.json` 파싱 | +| **인계면 행 39(봉투) · 행 26(P2 handoff) 실재** | `handoffs/stage1_part_interface.v1.json` 전수 | +| LES 검토 코드 2개 | `stage1_runtime/structure_index_compiler.py` 25·27행 | +| **사이드카 25 · 127규칙 · registry 121 전건 커버 · dangling 0 · 고아 6 · 81,430 B** | `선행구축/Default_Agent_Stage_1/domains/*/gates.json` 전수 | +| **17축 방언 지도**(발화코드 37/48/42 · 효과 컨테이너 85/48/42 · 차단 경로 37/48/42 · dict 키 6종 · 파일 status 키 16/9 · `global_status_rule` 3값 · 발화 status 키 37/48/42 · 평가 키 37/48/42 · row 효과 48/42 · `writes_fact_ledger` 85 · `unknown_or_missing_policy` 48) | 〃 전수 집계 | +| **5 gate_id의 `condition` 형 — str 3(E10-G05·E10-G06·E11-G01) · dict 2(X1-G01·X1-G02)** | 사이드카 직접 조회 | +| **게이트 입력 도달성** — SG-11 `details.operand_state` 전 트리 80/30 · distinct 16/6 · **골든-S2 0레코드** · 봉투 `review_items`/`evidence_slot_status` 골든-S2 11도메인 전건 공백(전체 112파일 중 비공백 28/40은 s3_adapter·fixture 계열) · `boundary_routes` 계열 골든 BO payload 0건 | 골든·예행 산출 전수 파싱 | +| **`operand_state` 책임은 Part 2** | `platform/schemas/client_goal_domain_profiles.schema.json` `$comment`(7) 명문 + `signals/adapters/s4_calculation_adapter.py` 22행 · `signals/emitters/emitter_runtime.py` 102행 | +| **`review_codes`가 slice `domain_declarations`에 없다** | `stage1_runtime/domain_slice_compiler.py` 277행 이하 | +| **`boundary_*` 전용 페이로드 키 9도메인 선언**(배포됨) · **`source_refs`는 배포 선언에 0회** | `routing/extension_payload_key_declarations.v1.json` | +| **`"source-backed overlap only"` 정본** | `선행구축/Default_Agent_Stage_1/domains//Stage2_handoff_contract.json` `conditional_boundary_handoffs[].activation`(E-18·19·20 등) — **배포 트리에 없음** | +| **boundary 입력 형태 정의처** | `선행구축/Default_Agent_Stage_1/domains//domain_extension.schema.json` ×6 — **배포 트리에 없음**(배포본 도메인 폴더는 4파일) | +| **골든-S2 SG-11 = 0레코드** · 전 트리 80/30은 5개 run 중복 합계 · distinct 16/6(`s4_states` 4·`positive` 1·`blocked` 1) | `선행구축/**/calculation_requirements.json` 40파일 전수 파싱 | +| **골든-FL의 `LEGAL_CAUSE_STATE_UNKNOWN_REVIEW` 2회**(F-011·F-014) · `NO_EVIDENCE_REFS` 1회(F-008) | 골든-FL 행별 `skeleton_validation_codes` 전수 | +| **`part1_review_guard.review_item_ids` 통로 실재** | `stage1_runtime/domain_slice_compiler.py` 447–450 · 497행 | +| **P2 handoff `source_review_code` 생산자 3곳** | Part 2 v.8 1293행(P2-R0 워커) · 1543행(P2-R0 투영 — `note_item.get("field")` = BO 필드명) · **2659행(P2-F0)** — 그 항목의 `source_domain`은 **2656행** `candidate_ref` 접두사 | +| **골든 BO ↔ 골든 FL 27필드 전건 실대조** | `FL.type == BO.BOType` 59/59 · `FL.action == BO.Action` 59/59 · `FL.evidence_refs == BO.source_evidence_indexes` 59/59 · `FL.derived_fact == FL.action` 59/59 · `core.Object` 0/59 · `core.BehaviorTime` 0/59 · `bo.amount` 59/59 | +| **인계면 사본 2벌** | `선행구축/…` 45행 ↔ `ver_8_yaml_candidates/…` 34행 · 바이트 상이 · 후자에 봉투·SG-07·SG-11·LES 행 부재 | +| **blocks=true 60의 condition 키 분해** | any_review 10 · required_slots 6 · boundary 6 · calc_operand 6 · str 32 | +| **blocks=true 60건의 condition 형 — dict 28 · str 32** | registry × 사이드카 교차 | +| **LES `review_codes` 실적재 2 · 상수 7 · E-00 `review_required` 특칙** | `structure_index_compiler.py` 25–31 · 313–314 · 391–405행 | +| **P2 handoff 코드 키는 `source_review_code`** | Part 2 v.8 1286–1293행 + `bo_surface_projection_policy.v1.json` `additive_keys` | +| **registry∩dict 31 · registry∩str 90 · 고아 6 전부 dict** | 〃 + registry 교차 | +| **발화코드 ⊄ registry 2건** | 3방언 정규화 후 registry `review_codes` 차집합 | +| `blocks_final_drafting: true` 60건 | `domains/*/domain_config.json` 전수 | +| 역매핑 5개 — 선언 정본 | X1 `legacy_equivalence_projections`(2건) · E-10 `legacy_gate_projection`(도메인 수준). 비어 있지 않은 것은 이 둘뿐 | +| 역매핑 5개 — 교집합 | `deterministic_gates_contract.json` `emitted_codes` ∩ registry `review_codes` | +| `LEGACY-GATE-01`에 `final_drafting_block` 키 부재 | 동결 계약 `writer_effect` | +| 상수표 10키 · 누락 1건 · `dict.get` 폴백 | `structure_projection_contract.json` | +| 어휘 5 vs 50 · type_id 10 vs 56 · 침묵 46 · `structure_types` 73 결손 0 | `domains/*/domain_config.json` 전수 + 구본 104–127행 | +| 골든 59행 × 27필드(순서 포함) · `F-001`~`F-059` · 게이트 발화 2건 · `must_consider` 59/59 | `선행구축/S_2_GPT_v2/fixtures/legacy_equivalence/run_1/fact_ledger/Fact_Ledger_base.json` | +| `fact_id` 서식·연속성 경성 단언 · 행 1:1 | 구본 700 · 1904–1905 · 1123행 | +| 예외 부류 3종 · F1 설정 · `FX-` · `PACK_ITEM_CHAR_LIMIT` · 판정 status 3값 | 구본 128–137 · 1241–1254 · 989 · 95 · 1666행 | +| `asset_cluster` 소멸(D-P4-2) | P2 v.8 · P3 v.8 · LES 스키마 · `structure_index_compiler.py` 전수 0회 | +| `evidence_slot_status` 4값 | `signals/_common/evidence_slot_status.schema.json` | +| `legacy_declaration_mismatches` E-09 | `routing/extension_payload_key_declarations.v1.json` | +| `by_claim_form` 미생성(§1.9) | 전략서 158·294행 ↔ Part 3 분석서 §1.2 D-4(라) ↔ 스키마 `structure_index.required` | +| **인계면 45행 · `handoff_count` 45 · SG-04/06/10 행 부재 · 구 3행 Part 4 잔존 · 4행 약칭 부재** | 선언표 전수 | +| **검사기가 `handoffs`만 읽고 경로 키 사전을 만든다 · U1/U2** | `_check_stage1_interface.py` 41–44(상수) · **209행**(`rows = {row["path"]: row …}`) · 235(U1) · 239(U2) | +| **매니페스트 사이드카 등재 관례**(`domains/` 51항 = 25+25+1) | `runtime_manifest.json` 파싱 → N+31 산술(N은 착수 직전 실측 · 작성 시 432, 이후 433 관측) | +| 배포 트리 209 · 매니페스트 432 · 교집합 155 · 불일치 0 | `stage_1_2_3_assets_distribution_location.md` §3 + 전수 재계산 | +| timeout 선례(P3 L0 240 · L2 240 · P2 A0/S0 300 · F0 240) | v.8 YAML 전수 | +| Part 3 신작 판단·L1 부재·모듈/정책 분리·사이드카 복사 배포·L0-2c 순서 | Part 3 분석서 §1.1·§1.3·§1.4·§6.2·§10 | +| 상류 함정(래핑·봉인 재직렬화·중복 키·보존 등식 한계·AST) | Part 1·2 분석서 109·197·473·545·588·660행 | +| 실행 뿌리 3인자·경로 정규화 금지 | 전략서 §0.3 50행 + `path_contract.json`(조립본) | +| SKILL 규칙(5키·미러·clientInfo·`read_raw`) | `v.7/SKILL.md` §3.3·§5·§5.2·§11 | +| code-executor 제약 | `test_code_executor.ipynb` cell 2·11–12 | +| 137종 절대 규칙 · signal 13 · 계산 17 · 침묵 금지 4코드 | `ensemble_v2.md` §2.2·§3.1·§4.2·§6 | +| 방언 정규화 리더 교훈 · 매니페스트 갱신 관례 · 배타 표현 경계 | `v.7/MEMORY.md` ⑯·㉖·㉗·㉘ | + +--- + +## 12. 이 문서의 검증 이력 + +적대적 sub-agent **12라운드**를 거쳤다. 열두 라운드 모두 FAIL 판정을 받아 그때마다 재작성했다. + +**라운드 1 — BLOCKER 3 · MAJOR 9 · MINOR 14 · 놓친 기회 6** +1. 게이트 사이드카 25개가 조립본에 **실재**하는데 "하나도 존재하지 않는다"고 단정 → §1.5·§4.4·§6.2 재작성. **작업량이 오히려 줄었다**(창작 0). +2. `fact_id`를 `FL-{BO_ID}`로 바꾸려 했다. 실제는 `F-{idx:03d}`이고 연속성이 경성 단언 → §1.8·§3.1 정정. +3. 게이트 데이터 원천을 SG-06/SG-07로 오지정. 실제는 BO payload이고 SG-07은 상시 공백 → §1.3 신설. +MAJOR·MINOR·놓친 기회는 §1.6(차단 예외)·§6.5(인계면 신설/갱신 분리)·§7.2a(합성 fixture)·D-P4-7(별칭 자기모순)·§1.9(`by_claim_form` 봉인) 등으로 전건 반영. + +**라운드 2 — BLOCKER 3 · MAJOR 9 · MINOR 12** +1. **dict `condition` 37건이 `any_review_code_present` 하나가 아니었다** — 실제 **6종 키**(any 13 · required_slots 6 · boundary 6 · calc_operand 6 · `*_attempted` 6)이고 registry∩dict는 **31**이다. "평가기 하나" 주장 철회 → §1.5b 신설 · §4.4 구현범위 재작성(창작 0은 유지되나 근거가 달라졌다). +2. **dict 게이트의 판정 입력이 Part 4 입력 계약에 없었다** — `review_items`·`evidence_slot_status`는 `domain_signals` 봉투에 있고 BO 18키에는 없다. 라운드 1 BLOCKER 3이 원천만 바뀌어 재발할 뻔했다 → §1.4 신설 · §3.3·§4.1⑤ 등재. **봉투 행이 인계면에 이미 있어 소비자 한 줄 추가로 해결된다**(라운드 2 놓친 기회 2 채택). +3. **방언 지도가 3축을 빠뜨렸다** — 발화코드 키 3종·효과 컨테이너 3종·차단 경로 3종. `writer_effect`만 보는 리더는 `on_true` 계열 **42규칙**(**`E10-G05`·`E10-G06` 포함**)을 못 읽어 "blocks 불일치 0"이 **미측정 42건 위의 공허한 0**이 된다 → §1.5a 9축으로 확장 · G-j2 신설. + +MAJOR 9 — 인계면 56→**55**(신설 10: 중간 3행 누락 보완) · 약칭 치환 6행 중 4행은 **추가**임 · F1 판정 부류 3종 중 2종 **발생 불가**(D-P4-2 실측 표) · §4.4 합계 133→127(고아 이중 계상) · 필수 9종/10개 재발 → **38종·필수 13종**으로 통일(작성 후 자체 계수로 재확인) · `PART4_REQUIRED_ASSETS` 12→**13** · 음성 시험 4종 추가(G-u2·G-u3·G-v2·G-z3) · 발화코드 초과 2건 · receipt 생산자 **F0 단독**으로 일원화. + +MINOR 12 — `unknown_or_missing_policy` 선언 분포(dict 31 중 0건) · `path_contract.json` 미배포 · `handoff_count` 갱신 · F2 timeout **240** · G-s 5항 · X1 `input_fields` 경로 한 단 어긋남 · 봉투 행이 6줄 중 하나 · 근거표 3행 추가 · §8에 0b·8단계 신설 · 인계면 note 원문 · `global_status_rule` 3방언 · 구본 138–140행 — 전건 반영. + +**라운드 3 — BLOCKER 4 · MAJOR 7 · MINOR 14.** 검증자 총평: "**이 문서의 계량 정확도는 매우 높다. 실패는 계량이 아니라 해석과 도달성에서 났다.**" 라운드 2가 제시한 모든 수치(dict 6종·31/90·127/121/6·9축 5·6·7·38종/필수13·리터럴 13·receipt F0 단독)는 **전건 재측정 일치**했다. 무너진 것은 그 위의 해석이다. + +1. **`E10-G05`·`E10-G06`도 str `condition`이었다.** §1.6 표의 "축 3" 자리에 축 5·6 값(`review_code`+`on_true`)을 적어 dict로 오분류했다 — **9축을 만들어 놓고 축을 섞어 읽었다.** 5개 중 str이 3개이므로 **발화 등가 대조 대상은 `X1-G01`·`X1-G02` 2개**로 줄었다 → §1.6 표 2열 분리 · 등가 범위 명문화 · §7.2a fixture ①④의 목적 재정의. +2. **봉투 `review_items`·`evidence_slot_status`가 골든-S2 11도메인 전건 공백이었다.** "스키마 필수"와 "실물이 채워짐"을 또 구분하지 않았다. 게다가 slice `domain_declarations`에 `review_codes`가 없어 워커에 코드 어휘가 전달되지 않는다 → §1.5b에 **도달성 열** 신설 · §9-3·9-6. +3. **`boundary_or_overlap_detected`를 도메인 활성 교집합으로 읽은 것이 창작이었다.** registry가 `boundary_routes`·`track_boundary_candidates`·`boundary_domain_candidates`를 전용 입력 키로 선언해 두었고, 생성기가 활성 조건을 `"source-backed overlap only"`로 적었다. 교집합 해석은 복합 사건에서 차단 표지를 대량 오발한다 → §1.5b 정정 · §9-5. +4. **`operand_state`는 Part 4 자기 산출이 아니라 Part 2 책임이었다.** 배포 자산이 `operand_state_not_emitted`로 명문화했고 SG-11 `details`가 이미 나른다(골든 30/80) → D-P4-6 전면 정정 · F0-5 승계 명시 · F0-5→F0-8 순서 계약. + +MAJOR 7 — `EXPECTED_DELTA-02` 59→**27**(str 32는 발화 없음) · 방언 9축→**17축** · 인계면 갱신 9→**12줄** · G-k5 데이터 계획(`SUPP-dict4`) · P2 handoff 코드 키가 `source_review_code`임 · LES 검토에 `review_required` 플래그 누락 · **F0-7b(게이트 입력 정규화) 단계 신설**. + +MINOR 14 — §0 표 게이트 입력·실행 줄 · `GATE_CONDITION_KEY_UNSUPPORTED_REVIEW` 무시험(G-k8) · 차단 불일치 검토 코드(G-k9) · 인계면 0-기반 index 표기 · `_check_stage1_interface.py` 경로 사전은 **209행** · §12 "3종"→4종 · 축 6 합이 배타 아님 · `calculation_bindings`는 ID 배열 · 매니페스트 277항 미배포 · 이월 범위 확대 · G-n의 `fact_adjudication.json` · 미러 쌍 관례 · 조립본 전용 계약 3종 · 고아 6이 두 키로 갈림(+`E14-G04`가 `E14-G02`와 코드 중복) — 전건 반영. + +**라운드 4 — BLOCKER 4 · MAJOR 8 · MINOR 15.** 검증자 총평: "**원문 대조형 계량은 사실상 완벽하다**(구본 행번호 16곳, 배포 자산 20여 건, 사이드카 17축 전건, 매니페스트·인계면 전건이 오차 0). 실패는 셋으로 좁혀진다 — ① **픽스처 이름의 혼동** ② **자기 문서 내부의 상충을 대조하지 않음** ③ **한 생산자·한 통로만 재고 '없다/이것뿐'이라 단정**." + +1. **"골든 SG-11 80레코드 중 30건"의 80/30이 골든이 아니었다.** 5개 run 트리에 중복 배치된 e2e 시나리오의 **합계**였고, **골든-S2의 SG-11은 `records: []` · `status: EMPTY`**다. 같은 표에서 봉투는 골든 한정으로, SG-11은 전 트리 합계로 **분모를 바꿔 썼다** → **§0.1 픽스처 사전 신설** · 도달성 표를 "골든 / 최량 픽스처" 두 칸으로 분리. +2. **골든-FL 등가가 두 행에서 깨진다.** `LEGAL_CAUSE_STATE_UNKNOWN_REVIEW`가 F-011·F-014에서 **실제로 2회 발화**하는데 그 코드의 게이트 `E11-G01`은 str이라 이번 회차가 가시화만 한다 → 필수 회귀 G-r의 확정 차분이므로 **`EXPECTED_DELTA-03` 신설**. §1.6의 "세 코드 0회 발화" 문장은 거짓이었다. +3. **G-j fixture가 게이트 조건형과 맞지 않았다.** X1-G01·G02는 코드 집계인데 fixture는 BO 사실을 만든다. 그리고 §1.5b는 같은 게이트를 "미도달·발화 0"으로, §1.6은 "발화 등가 대조 대상"으로 판정해 **한 문서가 상충**했다 → §7.2a를 **코드 층(이번 회차) / 사실 층(이월)** 두 층으로 분리. +4. **§7.5-2 완수 판정이 달성 불가능했다** — 봉투가 비었고 그 해소는 Part 2 이월인데 완수 조건은 "봉투를 읽어 실제로 발화한다"였다 → **2a(판정 대상) / 2b(미측정 명시)**로 분리하고 "합성 주입 통과를 실발화로 기록하지 않는다"를 명문화. + +MAJOR 8 — `EXPECTED_DELTA-02` 27→**21**(가시화분 제외, 골든에선 0) · 이월 참조 "3·4"→**3·5·6·13** · `part1_review_guard` 통로 실재(과잉 일반화 정정) · `source_review_code` 생산자 2곳·이름공간 3종·도메인 귀속 · `PART4_UNREACHABLE_EXCEPTION_TYPE` 무사양(F0-9에 결박) · `"source-backed overlap only"` 출처 오귀속(`Stage2_handoff_contract.json`) · **boundary 정의처 미배포 → 구현에서 가시화로 강등**(구현 31→**25**) · calc_operand 6의 네 사실(도달 유일·전건 차단·registry 위반 2·pass4 밖) 연결. + +MINOR 15 — 검사기 행번호 209 · §6.5 갱신 12줄 · 이월 번호 §9-n 안정화 · 축 열거 · §12 4종 · `SUPP-reader13` 개명 · F0-8 입력 한정 · F0-0b 신설 · `GATE_BLOCK_FLAG_MISMATCH_REVIEW` 결박 · 매니페스트 등재 관례 범위 등 전건 반영. + +**라운드 5 — BLOCKER 4 · MAJOR 9 · MINOR 12.** 검증자 총평은 라운드 4와 같았다 — 원문 대조형 계량은 오차 0(독립 검증 40건 중 계량 불일치 3건), 실패는 해석·정합에서 났다. + +1. **골든 등가 "27필드 전건"이 성립 불가였다.** 골든-FL 실측에서 `linked_structures`에 `issue_cluster_ids`, `claim_chain_ref`에 `liability_group_ids: ["B1:005"]`(**구 도메인 이름**), `state_context`에 `asset_cluster_ids`가 실려 있다. 전부 §1.1이 "소멸"로, §1.2가 "폐지"로 판정한 것들이다 — 즉 **7필드 × 59행이 재현 대상이 아니라 재설계 대상**이었다. 문서는 그 두 판정을 내려놓고 등가 요구는 그대로 뒀다 → **§7.1을 필드 3부류 판정표로 전면 재작성** · `EXPECTED_DELTA-04` 신설 · G-r 기대 재정의. +2. **골든-FL 픽스처로는 G-r을 돌릴 수 없다.** 파일이 5종뿐이고 `signal_manifest`·SG 5종·봉투·활성 매니페스트가 **하나도 없으며** LES도 레거시 형태다. 두 골든이 상호 배타인데 §7.1이 골든-S2에서 잰 값을 골든-FL 회귀의 기대로 결론했다 → **§0.1에 "주지 않는 것" 열 신설** · `SUPP-golden59` 조립본과 §8 0c단계 신설. +3. **§0.1 골든-S2 "55파일"이 자기 규약 위반이었다.** 적은 경로로는 44이고 55는 경로 밖 트리까지 포함해야 나오며, 55 자체가 **11도메인 × 5트리 중복**이다. 라운드 4가 SG-11 80/30에서 잡은 오류가 그 수정으로 만든 표 안에 남았다 → distinct 11로 전건 치환. +4. **"등가 예외 2건"이 5곳에 잔존**했는데 §7.1은 3건이었다. 규율 5(기계 대조)를 스스로 EXPECTED_DELTA 계수에 적용하지 않았다 → 부류별 판정으로 재작성하며 4건으로 통일. + +MAJOR 9 — §1.5b 도달성 "0/0"이 §11 "비공백 28/40"과 상충(측정 지표가 행마다 다름) · `required_slots` 0의 진짜 이유는 공백이 아니라 **`missing`·`conflicted`가 트리 전체에 0건** · **G-k5의 "3부류"가 §7.4의 "3부류"와 다른 집합** · §1.6 "59" ↔ §7.1 "21" · §6.5 소제목 9 ↔ 본문 12 · §1.5b 입력 정본이 slice를 들고 P2 handoff를 빠뜨림(Part 4는 slice를 읽지 않는다) · **자산 트리가 셋인데 둘로 셈**(`ver_8_yaml_candidates/Default_Agent_Stage_1`은 매니페스트 370항·`domains/` 부재인데 copy_map은 사이드카 25를 INCLUDED로 기록) · **F1 유일 생존 부류의 양성 시험 부재**(G-o3 신설) · **boundary 강등 논거 약함** — 키 선언·도메인 귀속·폐포는 배포돼 있고(`extension_payload_key_declarations.v1.json`) 미배포인 것은 항목 내부 형태뿐 → **§10 D-P4-c 결선 사안으로 승격**. + +MINOR 12 — 회귀 38→**46종**(G-k10·k11·k12·o2·o3·u2·u3·v2·z3 등 누적)·필수 14 · e2e 시나리오 4→8개 · 비공백 40의 5는 `positive` · `domain_extension.schema.json`은 25개 중 boundary 6 · `ensemble_v2` 도메인 수 표기(22 vs 실측 26) · 38→44 전환 미기록 · 역매핑 "선언 정본" 한정(동결 계약도 후보를 선언) · `NO_SUPPORT` 통로 · 실물-미등재 54 · G-o2 배치 · `POSSESSION_CAUSE_CONFLICT` 발화원 2개(도메인 한정 필요) · F0-7b ④는 계수 전용 · `NO_EVIDENCE_REFS`는 게이트 코드 아님 — 전건 반영. + +**라운드 6 — BLOCKER 4 · MAJOR 9 · MINOR 9.** 검증자 총평: "계량 오차는 3건뿐. **실패는 R5가 (나) 7필드를 고치면서 같은 검사를 (가)·(다)에 돌리지 않은 것**." 그리고 실행 가능성 판정 — "**1~7단계는 지금 그대로 착수 가능**하다. 막힌 것은 '만드는 법'이 아니라 '맞았는지 재는 법'이다." + +1. **3부류가 27필드를 덮지 않았다**(16+7+2=25). `money_claim_effect`·`commercial_successor_effect`가 무분류였다. +2. **`must_not_drop_in_claim_types`를 게이트 유래로 오분류했다.** 사이드카 127규칙에 **0회** 등장하고 값은 `_modules(types, signals)`이며 골든에서 `downstream_module_candidates`와 **59/59 값이 같은데** 두 필드를 정반대 부류에 넣었다. +3. **(가) 16필드가 BO만으로 재현되지 않았다.** `object_spec`(`core.Object` 0/59 → LES 폴백) · `derivation_basis`(`LES-001` 문자열) · `legal_calculation_object` · `credibility`/`proof_strength`(`evidence_indexed` 필요) 등. +4. **§8 0c 레시피가 실행 불가였다** — P2-S0은 BO가 아니라 `client_meeting.md`·`domain_seed_outputs/`를 읽는데 저장소에 둘 다 없다. + +→ **골든 BO ↔ 골든 FL 59행 전건 실대조로 §7.1a를 4부류로 재구성**했다(BO 유래 **9** · LES·구signal 유래 **15** · 증거 유래 **2** · 스켈레톤 **1** = 27, 무분류 0). 0c는 `structure_index_compiler` 직접 구동 + 최소 합성본의 **우회 레시피**로 교체하고, 그 위의 등가가 "평가기 정확성"임을 명시했다. + +MAJOR 9 — D-P4-c (나)의 `source_refs`가 배포본에 없는 개념 · §8-8에 `SUPP-dict4`·`SUPP-calc6` 누락 · **인계면 정본이 두 벌**(`선행구축` 45행 ↔ `ver_8_yaml_candidates` 34행, 봉투 행 부재) · `source_review_code` 생산자 **3곳**(P2-F0 2659행 누락, 그 `source_domain`은 `candidate_ref` 접두사) · 매니페스트 기준값 432→**433** 변동(절대 리터럴 폐기) · D-P4-b가 골든 있는 3건까지 뭉뚱그림 · §10 제목 2→3건 · §12 서두 "2라운드" · §7.5-2a에 골든 미달성 항목 잔존 — 전건 반영. + +MINOR 9 — `§7.2b` 참조 부재 · `golden_s2` 디렉터리 9개 중 봉투 보유 5 · "5배" 규칙이 2종에서 거짓 · `domain_extension.schema.json` boundary 관련 6→**9** · `emitter_runtime` 경로 · G-k5/`SUPP-dict4` 3부류 집합 · 음성 짝 범위 표기 · `SEAL_FAILED` 어휘 · 실물-미등재 63 — 반영. + +**라운드 7 — BLOCKER 4 · MAJOR 9 · MINOR 9.** 검증자 실행 가능성 판정: "**1~7단계는 여전히 착수 가능**하다. 막히는 곳은 **0단계 하나**다. 병목이 '무엇을 재는가'에서 **'언제 무엇으로 재는가'**로 한 칸 옮겨갔을 뿐." + +1. **0c가 자기 6단계 산출을 요구했다**(순환) — (나) 15필드는 Fact Ledger 행 값이라 `fact_ledger_compiler.py`(6단계)가 있어야 잰다. → **0c-1(입력 조립) / 6b(diff 실측)로 분리.** +2. **D-P4-b의 "골든 있는 3건 예외"가 반증됐다** — 골든 발화 2행(bh19·bh21)은 `legal_cause_assertions` **0건**이라 `E11-G01`의 선언 condition("양립 불가 **쌍**")이 성립할 수 없다. 골든 발화는 registry가 선언하지 않은 구본 778–779행 갈래에서 나온 것이므로 옮기면 창작이 된다. → **(나) 갈래 폐기 · `EXPECTED_DELTA-03`을 §9-4까지 확정 차분으로 고정.** +3. **D-P4-c가 0c에 결박됐으나 0c는 그 측정을 하지 않았다** — 골든 BO의 boundary 키가 **0건**이라 실행으로는 근거가 나오지 않는다. → **0c-2 정적 대조 단계 신설.** +4. **미결인 D-P4-c의 한쪽 답이 본문 7곳에 확정으로 박혀 있었다.** → **§10에 파급표 부착 · §0 표·§4.4에 "(가) 가정" 조건 명시.** + +MAJOR 9 — R4가 잡은 "골든 SG-11 80/30"이 D-P4-6·§11에 **잔존**(분모 규약 재위반) · §7.4 "3종"→**6종** · **(가) 9필드의 합성 규칙 부재**(`evidence_refs`를 LES와 합집합하면 정상 구현에서 G-r이 깨진다 — 골든은 LES 기여가 부분집합이라 우연히 일치했다) · `_compact_bo` 정규화 계층 누락 · `possession_state` **키 13 / 비공백 8** 혼용(규율 3 자기 위반) · §3.2가 G-t에 위임한 검사가 G-t 기대에 부재 · 0c 레시피가 모듈 **12인자**와 **L2 재조립 부재**를 누락 · **(다) 2필드가 지정 픽스처로 59/59 재현됨이 실측돼 "미측정"→"필수 판정" 승격** · 역매핑 타이브레이크 부재(`POSSESSION_CAUSE_CONFLICT` 발화원 2개). + +MINOR 9 — 인계면 0-based/1-based 혼용 · 매니페스트 절대값 잔존 · **`SUPP-golden59` 활성 집합이 pass4와 정반대**(E-02·03·04·10·11·13·15 · X1 부재) · 사실 층 fixture가 골든 실발화 갈래 미포함 · `domain_extension.schema.json` 수치 범위 · `s4_calculation_adapter` 22행 설명 · `_build_row` 687행 · `source_review_code` 4곳 — 전건 반영. + +**라운드 8 — BLOCKER 4 · MAJOR 8 · MINOR 9.** 검증자가 **§8 단계 의존 위상표**를 만들어 판정했다 — "**진성 순환은 사라졌다**(R7 처방의 성과). 남은 것은 **역행 2 · 결손 3**." + +1. **역행** — 0c-2가 0·0b **뒤**에 있는데 D-P4-c가 `EXPECTED_DELTA-02` 상한(21 vs 27)의 결정 함수였다. → **0c-2를 `0a`로 올려 0 앞에 두었다.** +2. **결손 — 골든 BO가 v.8 조인 규격이 아니다.** 0c-1 레시피를 실제로 구동해 보니 **BO 부착 0/59**였다. 골든 BO의 `Legal_Keywords`는 한국어 법률용어 26종(`가압류`·`도급`…)인데 v.8 Part 2는 그 칸에 registry `type_id`(1262행 `effect_type_ids`)를 싣고, `structure_index_compiler` 468–476행이 그 교집합으로만 부착한다 — **교집합 0**. → **(나) 15필드는 골든으로 판정하지 않는다**(`-04`를 "기본값 확정"으로 등록) · **G-r2**를 신설해 `SUPP-synth26`에서 검증. +3. **결손 — 0c-1이 LES 최상위 8키 중 3키, 봉인 3종 중 1종만 만들었다.** → 레시피에 8키 전건 + SG-01 `registry_index_sha256`·`config_sha256` 사슬 + `signal_manifest_transaction_id` 전파를 명시. +4. **결손 — (다)를 필수로 승격했으나 산출 규칙이 없었고 §4.6이 정반대("기본값 `low`")를 지시했다.** 골든 분포는 `high 39·medium 19·low 1`이라 그대로 구현하면 **58/59가 깨진다**. → **(다) 산출 규칙 4단계 명문화**(authority 구성 · 마커 10종 · 300자 절단 · 판정 순서) · **F0-6b 신설** · §4.6 정정. + +MAJOR 8 — §10 파급표에 4행 추가(G-k5·`SUPP-synth26`·§8-8·정책 키) · §9-5·§9-10을 "이월 취소"→**"이월 유지"** · `EXPECTED_DELTA-03` 원인 정정 + §9-4 "복원되지 않는다" · 검사 11에 합성 규칙 강제 추가 · **F0-2를 27필드→(가) 9필드로 한정** · `derived_fact` 4항 분기 **삭제 확정** · §0 표 (다) "전제조건부"→"값등가" · §6.3에 "LES는 능력 검사만" 명문화. + +MINOR 9 — boundary 스키마 9/10 출처 · 매니페스트 절대값 3곳 · `domain_seed_outputs` 검색 범위 · 음성 짝에 G-k13 · 6b 구동 방식 · `amount`·`evidence_refs` 규칙의 부수 동작 · 증거 우주 이탈 침묵 금지 · 구본 행번호 4곳 — 반영. + +**라운드 9 — BLOCKER 4 · MAJOR 10 · MINOR 11.** 검증자: "**독립 검증 41건 중 계량 불일치 0건.** 실패는 계량이 아니라 **계량을 실행 절차로 옮겨 적는 자리**에서 났다." + +1. **(다) 산출 규칙이 구본과 달랐다** — 4갈래로 썼는데 실제는 **5갈래**이고 순서도 틀렸다. `documentary`는 "그 외"가 아니라 **첫 검사**(마커 1개만 적중해도), `high`/`high`는 마커 적중 수가 아니라 **증거 행 수 ≥2**, 그리고 **`medium`/`medium` 최종 폴백이 통째로 빠졌다**(골든 19행). 문서 규칙으로 재현하면 **29/59**. → 5갈래 표로 코드 그대로 옮기고 순서 의존성을 명문화. +2. **효과 보존 등식이 `EXPECTED_DELTA-04` 공집합과 양립 불가였다** — 골든 경로에서 좌변 59 · 우변 0이라 **필드 비교 전에 경성 실패**한다. → 커버리지 등식(BO 수)과 효과 등식(도메인 수)을 **차원별로 분리**하고 `by_bo_id == {}`를 정상 입력으로 명시. +3. **G-r2의 입력을 만드는 단계가 없었다** — `SUPP-synth26`은 정의상 "SG-01 한 장 · 입력을 채우지 않는다"인데 G-r2는 "v.8 규격 합성 BO"를 요구했다. → **26-A(SG-01) / 26-B(v.8 BO + 재조립 LES)** 두 조각으로 분리하고 pass4 산출 대안을 병기. +4. **`SUPP-calc6`의 도메인이 게이트와 어긋났다** — `operand_state` 실적재 6건의 `domain_ids`가 전부 **E-05**인데 게이트는 **E-14~21**에 산다. 귀속 규칙을 지키면 발화 0, 어기면 금지된 교차 오발화. → **재라벨 합성본**으로 재정의하고 §1.5b 도달성 열을 **"도메인 귀속 후 도달"**로 바꿨다(전 부류 0으로 정정). + +MAJOR 10 — 파급표 도입문의 폐기 단계명 `0c-2` · **6b 목적 변경**(공집합은 0c-1만으로 확정되므로 G-r2 기준선 산출로) · **(가) 규칙 5/9 → 9/9**(`date`·`action`·`fact_id`·`source_bo_id` 추가) · `amount`에 240자 절단·공백 정규화·`core.Amount` 폴백·`or None` · **G-r3 기대 정정**(stub이 증거 행으로 계수되므로 `low`/`low`가 아니다) · §4.6에서 `skeleton_validation_codes` 삭제 · **`calculation_id` 결손 6/16 대비**(`signal_id` 대체 + 계수) · **0a 범위 축소**(`condition` 값 판독은 2단계 소관) · 12인자 공급처 · **F0-0c(정규화)·F0-8b(스켈레톤 3검사) 신설**. + +MINOR 11 — 구본 행번호 2곳 · 텍스트 join 1회 대조 · meeting 토큰 3종 · `parties` 키·160자 · 파급표 지목 범위 · 매니페스트 절대값 2곳 · `client_meeting.md` 검색 범위 등 — 반영. + +**라운드 10 — BLOCKER 4 · MAJOR 10 · MINOR 11.** 검증자: "독립 재측정 40여 건에서 계량 불일치 **2건뿐**. 세 BLOCKER가 전부 **'세는 대상의 단위'** 문제다." + +1. **(다) authority 행이 발췌 문자열이 아니라 6키 dict였다** — `_text`가 `json.dumps(sort_keys=True)`로 통째 직렬화하므로 대조 대상은 6키 전부의 값이다. 마커 `계약`이 **`title`에만 있는 행이 5건**이라 문서 규칙으로 재구현하면 **54/59**. → 6키 표 + `_text` 동작 + `sort_keys`에서 `title`이 최후미라 300자 절단이 마커를 먼저 지운다는 경고 추가. +2. **계산 등식이 행 항목 수와 원천 레코드 수를 한 식에 넣었다** — 컴파일러가 SG-11 `signal_id`를 도메인 내 전 레코드에 복사하므로(435–439행) **한 도메인에 BO 2건만 있어도 정상 실행에서 발화**한다. 골든은 SG-11 0레코드라 검출되지 않는다. → **④(행 차원)·⑤(원천 차원)로 분리**, G-w2 신설. +3. **게이트 발화가 어느 행에 붙는지 정의가 없었다** — 판정은 도메인 스코프인데 출력은 행 단위다. → §4.4에 **행 투영 규칙** 명문화("도메인 d가 `domain_effects` 키로 등장하는 모든 행에 부착, 없으면 writer report에만"). 골든 경로는 `domain_effects` 공집합이라 투영 대상 0이 되어 `-02`의 "실발화 0"과 자동 정합. +4. **`PART4_COVERAGE_BROKEN`·`PART4_BO_CONSERVATION_BROKEN`을 새 등식이 빼앗았다** — 구본에서 전자는 **판정 커버리지**(1684–1758), 후자는 **후보↔최종 BO 집합 동일성**(1898–1902)인 별개 검사이고 전략서 §5.3이 "자구 그대로"를 명령한다. → 세 검사를 코드로 분리(`PART4_BO_ATTACHMENT_COVERAGE_BROKEN` 신설) · G-v2/G-v3/G-v4 분리. + +MAJOR 10 — "등식 셋" 지시어 4곳 · **G-o3 트리거 술어**(구본 674–690행) · 0a 역행 · (가) `parties`·`date` 판정력 0 · `SUPP-dict4` **활성 SG-01 두 벌** · `SUPP-calc6` **조인 4조각**(재라벨 + SG-01 + 봉투 `calculation_refs` + 부착 BO) · F0-7b가 **F0-0b 별칭표 사용** · 레시피에 P2 handoff · G-k10/11/13 입력 · **F0-8b 전용 회귀**(G-s2 신설). + +MINOR 11 — LES 선택 4키 · `boundary_*` 10 · SG-01 파일 등식 · 절단 여유 실측 등 반영. + +**라운드 11 — BLOCKER 3 · MAJOR 10 · MINOR 11.** 검증자: "**A-1 (다) 재현 59/59 ✅ · A-2 (가) 재현 531/531 ✅.** R10 처방 자체는 옳았고 **전파 범위만 부족한** 유형." + +라운드 8~10의 핵심 BLOCKER였던 "규칙을 문서만 보고 재구현하면 골든이 재현되지 않는다"가 **해소됐다.** + +1. **`NO_LINKED_LEGAL_EFFECT_STRUCTURE`가 골든 경로에서 59행 전건 발화**하는데 등가 예외에 없었다 — 부착 0/59라 F0-8b 셋째 검사가 전 행에서 뜬다. (라) 실제 차분은 "2행 제거"가 아니라 **"2행 제거 + 59행 추가"** → **`EXPECTED_DELTA-05` 신설**. +2. **`legacy_declaration_mismatches`를 반대 방향으로 읽었다** — 별칭표 키는 구 이름이라 "E-09가 E-10으로 옮겨진다"는 경로가 **없다**. 실제는 `B2_Secured_Registry`를 자기선언한 `['E-09','E-10']` 중 `alias_targets`에 **E-09가 빠져** 골든 BO 10건이 E-09를 잃는 것이다. F0-0b의 "제외"는 no-op이었다 → 방향 정정 · 누락 도메인·영향 BO 수 계수 · G-z2 재정의 · 활성집합 7/8 확정을 0c-1로. +3. **행 투영이 발화 코드만 덮었다** — 같은 사이드카가 선언하는 **행 효과 90/127**(`row_effect.must_consider` 48 · `on_true.preserve_seed` 42)에 규칙이 없었다. `must_consider`는 27필드의 하나다 → **3분기 투영**(코드 합집합 / `must_consider` **OR 갱신** / `preserve_seed`는 writer report). + +MAJOR 10 — F2 검사표가 등식 3종만 강제(신설 3코드 행 추가) · 음성 짝 목록 **기계 생성**으로 교체 · **G-o3 기대가 구본 `if/elif` 배타와 충돌**(G-o4 분리) · §8-8에 **26-B 제작** · G-j 대조 대상에서 `row_effect` 제거 · **5-원소 상수표**(`_legal_calculation_object`)를 §1.7 삭제 범위에 추가 · `SUPP-dict4`/`gate4`에 부착 BO 조각 · `-04`를 "공집합"→**"부착 0 하의 결정론 기본값"** · dangling **stub 적재**(등식 ④ 보존) · 0a 판정을 `source_refs` 0회 단일 사실로 단순화(역행 해소). + +MINOR 11 — `title` 단위(authority 2 / FL 5) · `_projection_conflicts_for_row` 866행 · `source_domain` 2656행 · SG-01 한 벌로 충분 · G-t 활성 0의 전제 · 12인자 전건 공급 · `domain_sidecars` 형태 등 반영. + +**라운드 12 — BLOCKER 5 · MAJOR 11 · MINOR 10.** 검증자: "독립 검증 50건 중 **계량 불일치가 사실상 없었다**. B1~B5는 계량이 아니라 **① 이미 있는 자산을 안 열어 본 것 ② 신설 처방을 전파하지 않은 것 ③ 대체 규칙의 결과 집합을 안 재 본 것 ④ 파생 목록을 원본으로 착각한 것**." + +**A-1 (다) 59/59 · A-2 (가) 531/531 유지**되고, `EXPECTED_DELTA` 5건이 27필드 diff를 **필드 차원에서 정확히 덮음**이 실측으로 확인됐다(추가 `NO_LINKED_…` 59 · 제거 `LEGAL_CAUSE_…` 2). + +1. **골든-S2가 신호 전 세트를 실물로 갖고 있었다** — `ls` 한 번이면 나올 것을 "손합성하라"고 지시했다. SG-01(`expected_runnable` **11도메인**) · 봉인된 `signal_manifest`(`files[]` 27) · SG 13종 · 봉투 11이 2-run 바이트 동일로 실재한다. 그 결과 "X1 부재 · 활성 7 또는 8 · 게이트 32"라는 §7.4·§1.6의 핵심 논증이 **전부 반증**됐다(실측 11도메인 · 게이트 48). → 레시피에서 손합성 삭제, 유일 합성분은 v.8 LES 하나로 축소. +2. **`EXPECTED_DELTA-05`에 등록 단계가 없고 계수가 4건으로 남았다** — 59행 전건 차분인데 어느 단계도 등록하지 않는다. 라운드 6 지적("예외 2건이 5곳에 잔존")의 동일 재발 → 5건으로 통일 · 0b에 등록. +3. **5원소 상수표의 대체 규칙이 항등식으로 퇴화한다** — `calculation_bindings` 비공백 도메인이 **25개(게이트 보유 전건)**이고 그 `type_id` 합집합이 **56종(registry 전건)**이라 `needs_calc`가 항상 True가 되고, v.8 BO에서 `amount` 후보 2개가 성립해 **pack이 폭주**한다 → 대체를 좁히거나 5원소 승계, 분포를 26-B에서 실측 등록. +4. **`legacy_declaration_mismatches`는 파생 목록이고 원본은 4건이다** — 자기선언 전수(`structure_types[].legacy_source`)와 별칭표를 양방향 대조하면 누락 **4건**(E-09·X2·X1·X3) + 역방향 과잉 1건이고, 자기선언 값에 **약칭 3종**이 섞여 완전 일치 조회로는 B3/B4/B5가 아예 판정되지 않는다 → F0-0b를 전수 대조로, G-z2·§9-8 확장. +5. **행 투영 ②가 (나) 부류 판정과 충돌한다** — `must_consider`는 (나)인데 게이트가 OR로 갱신한다. 최종 기록자가 §4.2와 §4.4에서 다르다 → **(나)∩(라) 겸용**으로 표기하고 G-r2 기대를 "재산출값 ∨ 투영값"으로. + +MAJOR 11 — `GATE_EMITTED_CODE_NOT_IN_REGISTRY` 대조면(게이트 단위 2 / 도메인 단위 0) · **0c-0(L2 재조립부 작성) 단계 신설** · 6b를 **11b로 이동**(역행 해소) · **17축 확장**(`input_fields`·`input_contracts`·`silent_repair_forbidden`·`source_membership_required`) · `-04` "공집합"→"기본값" 5곳 · 활성집합 파급 · `type_id` 리터럴 4종 · 0a 첫 문장 · **행 투영 ① 순서 규칙**(스켈레톤 고정 → gate_id 오름차순 → dedupe) · `object_spec` v.8 우변 미정 · P2 handoff 채널 미실측(골든 18/18 전부 `None`). + +MINOR 10 — 행번호 3곳 · `Evidence` dict 기전 · module 축 10 vs 34 · 매니페스트 · §8 계수 · 검사기 미배포 · 이중 계수 · 항등 검사 유보 — 반영. + +### 검증 중단 기록 — 12라운드에서 멈춘 이유 + +**13라운드를 돌리지 않고 여기서 멈췄다.** 근거는 셋이다. + +1. **종료 조건이 원리적으로 도달 불가능했다.** 검증 프롬프트가 "지적 0건이라 쓰려면 최소 25가지 독립 검증을 수행하라"고 요구하므로, 적대적 에이전트에게는 매 라운드 무언가를 찾는 것이 임무가 된다. "100% 통과"를 종료 조건으로 삼은 것이 설계 착오였다. +2. **지적 건수가 라운드 8~12에서 평평했다**(BLOCKER 3~5 유지). 수렴이 아니라 **정체** 신호였고, 문서가 1,300행을 넘기면서 한 곳을 고칠 때마다 그 수정이 참조되는 다른 절에서 새 불일치가 생기는 국면에 들어섰다 — 라운드 11·12 검증자가 각각 "옳은 처방을 전파하지 않아서", "처방이 참조하는 원본을 다시 열어 보지 않아서"라고 부른 그 패턴이다. +3. **라운드 7이 이미 실행 가능성을 판정했다** — "1~7단계는 지금 그대로 착수 가능하고 막히는 것은 재는 절차뿐". 그 시점에 남은 항목을 미해결 목록으로 넘겼어야 했다. + +**따라서 §10(결선 4건)과 §9(이월 14건)가 이 문서의 미해결 목록이며, 그것이 검증 라운드를 더 도는 것보다 착수에 유용하다.** + +**열두 라운드의 교훈** + +라운드 1의 BLOCKER 둘이 **"없다"고 단정한 자리**에서 나왔다("사이드카가 없다", "원천이 SG다"). MEMORY.md ⑩·⑬·⑱이 세 회차 연속 기록한 "**'없다'는 주장은 범위를 명시해야 산다**"를 그대로 재현했다. + +라운드 2의 BLOCKER 셋은 성격이 달랐다 — **라운드 1의 수정이 만든 새 부정확**이다. 사이드카를 발견하고 기뻐한 나머지 (a) dict 조건의 키를 세지 않고 하나로 뭉뚱그렸고 (b) 그 조건이 무엇을 읽는지 확인하지 않았으며 (c) 방언을 6축까지만 세고 멈췄다. **"세지 않은 필드를 공통 필드로 승격하지 않는다"**는 교훈을 네 번째로 어긴 셈이다. + +라운드 3은 **성격이 또 달랐다**. 라운드 1은 "없다"고 단정해서, 라운드 2는 입력 계약을 안 세워서, 라운드 3은 **계약은 있는데 값이 비어 있어서** 넘어졌다. 세 번 모두 같은 게이트 발화 문제로 돌아왔고 원인만 한 칸씩 안쪽으로 옮겨 갔다 — 원천 오지정 → 계약 부재 → **실물 공백**. + +라운드 4는 **다른 픽스처의 값을 이 픽스처의 값이라 불러서**, 5는 **소멸시킨 것에 등가를 요구해서**, 6은 **그 교정을 일부 부류에만 돌려서**, 7은 **재는 절차의 선후를 세우지 않아서**, 8은 **측정한 것을 만드는 법을 적지 않아서**, 9는 **만드는 법을 적었으나 원본과 다르게 적어서**, 10은 **규칙을 적은 대상이 실제 계산의 피연산자와 달라서**, 11은 **옳은 처방을 문서 전체에 전파하지 않아서**, 12는 **처방이 참조하는 원본을 다시 열어 보지 않아서** 같은 자리로 돌아왔다. 여덟 라운드가 전부 "무엇이 실제로 발화·재현되는가"였고 원인만 한 칸씩 안쪽으로 옮겨 갔다 — 원천 오지정 → 계약 부재 → 실물 공백 → 픽스처 혼동 → 판정 귀결 미추적 → 교정의 부분 적용 → 측정 순서의 순환 → **측정과 생성의 분리**. + +라운드 8이 남긴 가장 값싼 도구는 **§8 단계 의존 위상표**다. "단계 → 요구 산출 → 공급 단계 → OK/역행/결손"을 한 줄씩 적으면 순환도 역행도 결손도 한 번에 드러난다. 착수 전에 그 표를 한 번 더 돌린다. + +라운드 5·6이 가르친 것이 가장 값싸다. **§1에서 "소멸"·"폐지"로 판정한 것이 §7의 기준선에 무엇을 남기는지 한 번만 따라가면**, 그리고 그 검사를 **전 필드에 빠짐없이 돌리면** 두 라운드의 BLOCKER가 한꺼번에 나온다. §7.1a를 골든 실대조로 재구성한 것이 그 답이다. + +그래서 이 문서는 다섯 가지를 규율로 세웠다. + +1. 모든 부재 주장에 **검색 범위**를 붙인다(라운드 1). +2. 모든 집계에 **방언 축과 분모**를 붙인다(라운드 2). +3. 모든 입력에 **"선언 존재"와 "실측 비공백"을 따로 적는다**(라운드 3 · §7.5-4 영수증 항목). +4. 모든 픽스처 인용에 **이름·경로·분모를 동반한다**(라운드 4 · **§0.1 픽스처 사전**). +5. 제출 전 **같은 수치·같은 이름의 모든 출현을 기계로 대조한다** — 라운드 4·5 지적 각 12·21건 중 6·7건이 "이 문서의 A절과 B절을 나란히 놓기"만으로 나왔다. 대조 대상에 **수치**뿐 아니라 **부류 이름이 가리키는 집합**(같은 "3부류"가 두 절에서 다른 집합을 가리켰다)과 **EXPECTED_DELTA 계수**를 포함한다. +6. **§1의 판정이 §7의 기준선에 무엇을 남기는지 표로 잇고, 그 검사를 전 항목에 빠짐없이 돌린다**(라운드 5·6 · §7.1a 필드 4부류표). +7. **모든 측정 단계에 "무엇이 있어야 잴 수 있는가"를 적는다**(라운드 7). 0c가 6단계 산출을 요구했던 것은 도구와 대상의 선후를 세우지 않았기 때문이다. §8의 각 단계는 이제 선행 산출을 명시한다. +8. **미결 결선의 한쪽 답을 본문에 확정으로 쓰지 않는다** — 쓸 수밖에 없으면 **가정 표기와 파급표**를 함께 둔다(라운드 7 · §10). 파급표는 **필수 시험까지** 훑는다. +9. **측정한 값에는 반드시 "만드는 법"을 함께 적는다**(라운드 8). (다) 2필드를 필수 등가로 승격하면서 산출 규칙을 적지 않아 58/59가 깨질 뻔했다. 기준선으로 삼은 값은 재현 절차가 문서에 있어야 기준선이다. +10. **조립 레시피는 한 번 실제로 돌려 본다**(라운드 8). 0c-1은 문면상 완결이었으나 구동해 보니 BO 부착 0/59였다 — 조인 키가 규격 밖이라는 사실은 실행으로만 드러났다. +11. **옮겨 적은 규칙은 원본으로 되돌려 재현을 재 본다**(라운드 9). (다) 5갈래를 4갈래로 옮겨 적어 29/59가 됐다. 규칙을 문서에 실었으면 **그 문서만 보고 다시 구현했을 때 기준선이 재현되는지**를 착수 전에 확인한다 — §8 0c-1에 그 확인을 넣었다. +12. **등식은 차원을 확인하고 세운다**(라운드 9). +13. **모든 규칙에 피연산자의 단위와 그 단위를 만드는 구조를 함께 적는다**(라운드 10). +14. **처방을 세웠으면 그 처방이 닿아야 할 자리를 전수로 훑는다**(라운드 11). +15. **파생 목록을 근거로 쓰기 전에 원본을 전수로 다시 만들어 대조하고, 픽스처를 합성하기 전에 그 디렉터리를 `ls` 한다**(라운드 12). `legacy_declaration_mismatches`(파생 1건 vs 원본 4건) · `calculation_bindings`(비공백 25 = 전건) · `golden_s2/`(합성 지시 vs 실물 존재) — 셋 다 **자산이 이미 답을 갖고 있는데 요약본이나 이웃 파일만 본 것**이다. +16. **대체 규칙을 세우면 대체 후 결과 집합의 크기를 잰다**(라운드 12). 5원소를 56원소로 바꾸는 것은 일반화가 아니라 필터의 소멸이다. 행 투영은 코드까지만, 등식은 §4.2까지만, (다)는 6키까지만 갔다 — 처방이 옳아도 §4.7·§7.2·§8에 내려오지 않으면 실행자에게는 없는 것과 같다. **새 규칙을 세울 때마다 "이 규칙을 참조해야 할 절"을 목록으로 만들고 그 목록을 소진한다.** (다)는 발췌가 아니라 6키 dict를 쟀고, 계산 등식은 레코드가 아니라 행 항목을 셌으며, 게이트는 도메인에서 판정하는데 결과는 행에 적는다 — 세 BLOCKER가 전부 단위 문제였다. "무엇을"만 적고 "어떤 단위로"를 빼면 재현이 깨진다. "Σ 도메인 키 + BO 수 == 도달 총수"는 개수의 종류가 섞인 식이었고, 공집합 입력에서 즉시 드러났다. + +이 문서는 회귀 종수·필수 개수·실패 코드 양방향·내부 참조·이월 번호·EXPECTED_DELTA 계수·부류 집합을 기계 대조로 확인한 뒤 제출했다. diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/ver_8_yaml_candidates/stage_1_part_4_v.8.yml b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/ver_8_yaml_candidates/stage_1_part_4_v.8.yml new file mode 100644 index 00000000..b417d22b --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/ver_8_yaml_candidates/stage_1_part_4_v.8.yml @@ -0,0 +1,1279 @@ +# ============================================================================= +# Liti-agent Stage 1 Part 4 v.8 — 사실원장(Fact Ledger) · 통합 실행본 +# +# 정본 근거 +# 전략 : stage_1_part_4_개정전략서.md (§2 설계 결정 · §4 소작업 · §6 자산 · §7 검증) +# 상위 계약 : stage_1_part_1_v.8.yml · stage_1_part_2_v.8.yml · stage_1_part_3_v.8.yml 실산출 +# 진입 계약 : signals/signal_manifest.json (downstream_read_sets.part4_F0 = +# SG-04 · SG-06 · SG-07 · SG-10 · SG-11) + SG-01 + BO.json +# + legal_effect_structures.json + 도메인 봉투 + P2 review handoff +# +# 구성 — task 3종 (완전 직렬 · fan-out 없음) +# [신설] Task_C_FL_F0_fact_ledger_reducer 행 생성 · 조인 · 게이트 · 예외 pack +# [개정] Task_C_FL_F1_fact_exception_adjudicator LLM 판정 (유일 생존 부류 1종) +# [신설] Task_C_FL_F2_final_fact_ledger_gate_and_writer 보존 등식 5 · 커버리지 · 최종 기록 +# +# 확정 계약 +# 행 BO 1건 = 1행 · fact_id = F-{idx:03d} 1-기반 연속(경성 단언) +# 행 스키마 구 27키(이름·순서 불변) + domain_effects · calculation_requests = 29키 +# 게이트 사이드카 25 배포본을 17축 정규화 리더로 읽는다 — 판정식 창작 0 +# 구현 25(dict 3부류) · 가시화 96(str 90 + boundary 6) · 고아 6 검토 +# operand_state Part 2 산출을 승계한다 — 재산정 금지(D-P4-6) +# 최종 산출 Fact_Ledger_base.json (래핑 없는 배열) +# 로직 거처 stage1_runtime/fact_ledger_compiler.py · 규칙은 fact_ledger_policy.v1.json +# +# 제약 1 — 구 Part 4 명세의 어떤 파일도 읽지 않는다. 구 signal 3종 뿌리 이름을 읽지 않는다. +# 137종 — 사건 이름·도메인 목록·유형 열거를 코드에 두지 않는다. +# 이 파일은 스테이지 하나다. 스테이지 선언 1벌 · task_procedure 1벌 · tasks 1벌. +# ============================================================================= +--- +Agent: + name: Liti-agent_Civil_Suit_Plaintiff_Stage_1_Part_4 + description: 민사소송 원고 송무 초지능 AI변호사 - Stage 1 Part 4 + version: v.2 + Stages: + - name: stage1_사실원장_생성 + description: 사실원장 생성, 게이트 실행, 최종 기록 + llm_provider: openai + llm_model: gpt-4o-2024-08-06 + tools: + mcpServers: + localdocs: + type: streamable-http + url: http://mcp-localdocs:8012/mcp + description: Get the content of local documents + code-executor: + type: streamable-http + url: https://code-executor.mcp.eroomai.com/mcp + description: Run scripts of programming languages + headers: + Authorization: Bearer rR8OXqWrVZA1gFEo8oWfBkw2XgpWoGrrspw5ObsxTCM= + tasks: + - task_name: Task_C_FL_F0_fact_ledger_reducer + mcp: code-executor + tool_name: run_code + parameters: + language: python + requirements: "httpx" + network: "agent-network" + timeout: 300 + code: |- + #!/usr/bin/env python3 + # Task_C_FL_F0_fact_ledger_reducer (v1) + # 0) 배포 게이트 -> 1) 모듈 5종 반입 -> 2) 정책 반입 -> 3) 의미 검사 둘 -> + # 4) 진입 봉인 셋 -> 5) 입력 적재 -> 6) compile_fact_ledger_candidates -> + # 7) bundle · exception pack · review handoff(OPEN) · receipt 기록 + import contextlib + import hashlib + import io + import itertools + import json + import pathlib + import re + import sys + + import httpx + + # ------------------------------------------------------------------ + # localdocs 보일러플레이트 (SKILL.md 5장 / 5.2장) — Part 3 L0 검증 완료본 복사. + # clientInfo 에 {{__user_hash__}} / {{__workspace_hash__}} 를 반드시 넣는다. + # ------------------------------------------------------------------ + TASK_NAME = "Task_C_FL_F0_fact_ledger_reducer" + LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp" + MCP_HEADERS = {"Content-Type": "application/json", + "Accept": "application/json, text/event-stream"} + CLIENT = httpx.Client(timeout=120) + MSG_ID_COUNTER = itertools.count(10) + + + def next_msg_id(): + return next(MSG_ID_COUNTER) + + + def _init(): + r = CLIENT.post(LOCALDOCS_URL, json={ + "jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": {"protocolVersion": "2025-03-26", "capabilities": {}, + "clientInfo": {"name": TASK_NAME, "version": "1.0", + "user_id": "{{__user_hash__}}", + "workspace_id": "{{__workspace_hash__}}"}} + }, headers=MCP_HEADERS) + r.raise_for_status() + sid = r.headers.get("mcp-session-id") + if sid: + MCP_HEADERS["mcp-session-id"] = sid + CLIENT.post(LOCALDOCS_URL, + json={"jsonrpc": "2.0", "method": "notifications/initialized"}, + headers=MCP_HEADERS).raise_for_status() + + + def _parse_mcp(text): + for line in text.strip().split("\n"): + if line.startswith("data: "): + try: + return json.loads(line[6:]) + except Exception: + return None + try: + return json.loads(text) + except Exception: + return None + + + def _call(name, args, mid): + r = CLIENT.post(LOCALDOCS_URL, json={ + "jsonrpc": "2.0", "id": mid, "method": "tools/call", + "params": {"name": name, "arguments": args} + }, headers=MCP_HEADERS) + r.raise_for_status() + p = _parse_mcp(r.text) + if not p or "result" not in p: + raise RuntimeError("MCP_CALL_FAILED:%s" % name) + return p + + + def read_raw(name): + # 재직렬화 없이 원문 그대로. 봉인·미러 해시 대조의 전제다. + p = _call("read_docs", {"doc_names": [name]}, next_msg_id()) + text = (p["result"].get("content") or [{}])[0].get("text", "") + if not text: + raise RuntimeError("EMPTY_RESPONSE:%s" % name) + try: + outer = json.loads(text) + except Exception: + return text + if isinstance(outer, str): + return outer + if isinstance(outer, dict) and "results" in outer: + r0 = (outer.get("results") or [{}])[0] + inner = r0.get("content") + if inner is None: + inner = r0.get("text") + if not isinstance(inner, str) or not inner: + raise RuntimeError("EMPTY_CONTENT:%s" % name) + return inner + raise RuntimeError("UNEXPECTED_READ_DOCS_ENVELOPE:%s" % name) + + + def read_json(name): + raw = read_raw(name) + s = raw.strip() + try: + return json.loads(s) + except json.JSONDecodeError: + obj, _ = json.JSONDecoder().raw_decode(s) + return obj + + + def try_read_json(name): + try: + return read_json(name) + except Exception: + return None + + + def write_doc(path, content): + _call("write_file", {"path": path, "content": content, "overwrite": True}, + next_msg_id()) + + # ------------------------------------------------------------------ + # 실행 뿌리 — /tmp/s1 공유(Part 3 규율. 새 뿌리를 만들지 않는다). + # ------------------------------------------------------------------ + ASSET_ROOT = "Default_Agent/" + EXECUTION_ROOT = "/tmp/s1" + RT = pathlib.Path(EXECUTION_ROOT) / "_rt" + + MODULE_MIRRORS = { + "runtime_common": "Default_Agent/stage1_runtime/runtime_common.txt", + "schema_subset_validator": "Default_Agent/stage1_runtime/schema_subset_validator.txt", + "registry_loader": "Default_Agent/stage1_runtime/registry_loader.txt", + "structure_index_compiler": "Default_Agent/stage1_runtime/structure_index_compiler.txt", + "fact_ledger_compiler": "Default_Agent/stage1_runtime/fact_ledger_compiler.txt", + } + MODULES = ["runtime_common", "schema_subset_validator", "registry_loader", + "structure_index_compiler", "fact_ledger_compiler"] + RUNTIME_MANIFEST = "Default_Agent/runtime_manifest.json" + RUNTIME_MANIFEST_SCHEMA = "stage1_runtime_manifest.v1" + + # 정적 자산 + REGISTRY_INDEX = "Default_Agent/domains/_registry_index.json" + POLICY = "Default_Agent/stage1_runtime/fact_ledger_policy.v1.json" + POLICY_SCHEMA_VERSION = "stage1_fact_ledger_policy.v1" + ALIAS_DECLARATIONS = "Default_Agent/routing/extension_payload_key_declarations.v1.json" + BUNDLE_SCHEMA = "Default_Agent/platform/schemas/fact_ledger_candidate_bundle.schema.json" + BASE_SCHEMA = "Default_Agent/platform/schemas/fact_ledger_base.schema.json" + PACK_SCHEMA = "Default_Agent/platform/schemas/fact_exception_pack.schema.json" + LES_SCHEMA = "Default_Agent/platform/schemas/legal_effect_structures.schema.json" + + # 사건 산출물 — Part 1·2·3 이 낸 것만 읽는다. 구 signal 3종 뿌리 이름은 읽지 않는다. + SIGNAL_MANIFEST = "signals/signal_manifest.json" + SG04_PATH = "signals/governing_law_version_signals.json" + SG06_PATH = "signals/timeline_notice_condition_signals.json" + SG07_PATH = "signals/asset_right_state_signals.json" + SG10_PATH = "signals/evidence_proof_conflict_signals.json" + SG11_PATH = "signals/calculation_requirements.json" + DOMAIN_SIGNAL_DIR = "signals/domain_signals" + ACTIVATION_MANIFEST = "routing/domain_activation_manifest.json" + BO_PATH = "BO.json" + LES_PATH = "legal_effect_structures.json" + P2_HANDOFF_PATH = "quality_gates/stage1_part2_review_handoff.json" + EVIDENCE_PATH = "evidence_indexed.json" + + # 산출 + BUNDLE_PATH = "stage1_tmp/fact_ledger/fact_ledger_candidate_bundle.json" + PACK_PATH = "stage1_tmp/fact_ledger/fact_exception_pack.json" + HANDOFF_PATH = "quality_gates/stage1_part4_review_handoff.json" + RECEIPT_PATH = "validation_assets/routing/part4_receipt.json" + + # 배포 게이트가 한 벌로 검사하는 필수 자산 13 리터럴 (전략서 §4.1⓪). + # 사이드카 25 · SG 스키마 · 봉투 스키마는 여기 넣지 않는다 — expected_runnable + # 것만 읽는 시점에 _verify_asset 이 지킨다(Part 3 규율). + PART4_REQUIRED_ASSETS = ( + BUNDLE_SCHEMA, + BASE_SCHEMA, + PACK_SCHEMA, + LES_SCHEMA, + POLICY, + ALIAS_DECLARATIONS, + "Default_Agent/signals/signal_registry.v2.json", + "Default_Agent/contracts/signals/s5_execution_contract.v2.json", + "Default_Agent/stage1_runtime/runtime_common.txt", + "Default_Agent/stage1_runtime/schema_subset_validator.txt", + "Default_Agent/stage1_runtime/registry_loader.txt", + "Default_Agent/stage1_runtime/structure_index_compiler.txt", + "Default_Agent/stage1_runtime/fact_ledger_compiler.txt", + ) + PART4_DEPLOYMENT_REMEDY = ( + "배포 원본 extension_research/Default_Agent 트리를 서버 Default_Agent/ 에 통째로 배포하고 " + "runtime_manifest.json 의 등재·sha256 과 대조해 결손 경로를 복원한다. 부분 복사는 허용되지 않는다.") + + TRANSACTION_ID_RE = re.compile(r"^S5TX-[a-f0-9]{20}$") + + ERRORS = [] + WARNINGS = [] + + + def warn(code, message): + WARNINGS.append({"code": code, "message": message}) + + + def sha_text(text): + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + + def canonical(value): + return json.dumps(value, ensure_ascii=False, sort_keys=True, + separators=(",", ":")) + "\n" + + + # ------------------------------------------------------------------ + # 0) 배포 게이트 — 등재·sha256·가독을 한 벌로. 첫 실패에서 멈추지 않고 모은다. + # ------------------------------------------------------------------ + def assert_deployment(): + manifest = json.loads(read_raw(RUNTIME_MANIFEST)) + if manifest.get("schema_version") != RUNTIME_MANIFEST_SCHEMA: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_RUNTIME_MANIFEST_INVALID", "detail": "schema_version", + "expected": RUNTIME_MANIFEST_SCHEMA, "actual": manifest.get("schema_version"), + }, ensure_ascii=False)) + expected = {row["path"]: row["sha256"] + for row in (manifest.get("entries") or []) if isinstance(row, dict)} + unregistered, mismatch, unreadable = [], [], [] + for logical in sorted(set(list(MODULE_MIRRORS.values()) + list(PART4_REQUIRED_ASSETS))): + rel = logical[len(ASSET_ROOT):] + want = expected.get(rel) + if want is None: + unregistered.append(rel) + try: + body = read_raw(logical) + except Exception: + unreadable.append(rel) + continue + if want is not None and want != sha_text(body): + mismatch.append({"path": rel, "expected": want, "actual": sha_text(body)}) + if unregistered or mismatch or unreadable: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_ASSET_DEPLOYMENT_INCOMPLETE", + "message": "배포 원본에 Part 4 자산이 한 벌로 반영되지 않았다.", + "unregistered": unregistered, "hash_mismatch": mismatch, + "unreadable": unreadable, "remedy": PART4_DEPLOYMENT_REMEDY, + }, ensure_ascii=False)) + return manifest + + + _MANIFEST_MAP = None + + + def _manifest_map(): + global _MANIFEST_MAP + if _MANIFEST_MAP is None: + _MANIFEST_MAP = {row["path"]: row["sha256"] + for row in (json.loads(read_raw(RUNTIME_MANIFEST)).get("entries") or []) + if isinstance(row, dict)} + return _MANIFEST_MAP + + + def _deployment_error(logical, detail, extra=None): + payload = {"reason_code": "PART4_ASSET_DEPLOYMENT_INCOMPLETE", "path": logical, + "detail": detail, "remedy": PART4_DEPLOYMENT_REMEDY} + if extra: + payload.update(extra) + return RuntimeError(json.dumps(payload, ensure_ascii=False)) + + + def _verify_asset(logical): + """원문을 돌려준다. 등재·해시 대조를 통과해야만 소비된다.""" + body = read_raw(logical) + want = _manifest_map().get(logical[len(ASSET_ROOT):]) + if want is None: + raise _deployment_error(logical, "unregistered") + if want != sha_text(body): + raise _deployment_error(logical, "hash_mismatch", {"expected": want}) + return body + + + # ------------------------------------------------------------------ + # 1) 모듈 반입 — R-1~R-5. 해시가 어긋나면 실행하지 않는다. + # ------------------------------------------------------------------ + def materialize_modules(): + RT.mkdir(parents=True, exist_ok=True) + expected = _manifest_map() + staged = [] + for name in MODULES: + logical = MODULE_MIRRORS[name] + raw = read_raw(logical).encode("utf-8") + want = expected.get(logical[len(ASSET_ROOT):]) + if want is None: + raise RuntimeError("MODULE_MIRROR_UNREGISTERED:%s" % logical) + if want != hashlib.sha256(raw).hexdigest(): + raise RuntimeError("MODULE_MIRROR_HASH_MISMATCH:%s" % logical) + (RT / (name + ".py")).write_bytes(raw) + staged.append(name) + if str(RT) not in sys.path: + sys.path.insert(0, str(RT)) + return staged + + + # ------------------------------------------------------------------ + # 3) 의미 검사 둘 — 장부 검사만으로는 닫히지 않는다(Part 2 교훈). + # ------------------------------------------------------------------ + def assert_bundle_schema_capability(bundle_schema): + row_props = (((((bundle_schema.get("properties") or {}) + .get("fact_ledger_candidate_bundle") or {}) + .get("properties") or {}) + .get("rows") or {}).get("items") or {}).get("properties") or {} + missing = [key for key in ("domain_effects", "calculation_requests") + if key not in row_props] + if missing: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_LEDGER_SCHEMA_STALE", + "message": "후보 bundle 스키마가 Part 4 계약 필드를 모른다. 배포 원본의 스키마가 개정 전 판본이다.", + "missing": missing, "remedy": PART4_DEPLOYMENT_REMEDY, + }, ensure_ascii=False)) + + + def assert_compiler_capability(result): + bundle = (result or {}).get("fact_ledger_candidate_bundle") or {} + rows = bundle.get("rows") + bad = (not isinstance(rows, list) + or "gate_report" not in bundle + or any("domain_effects" not in row for row in rows + if isinstance(row, dict))) + if bad: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_COMPILER_STALE", + "message": "컴파일러 산출에 domain_effects·gate_report 가 실려 나오지 않는다. " + "배포 원본의 fact_ledger_compiler 가 개정 전 판본이다.", + "remedy": PART4_DEPLOYMENT_REMEDY, + }, ensure_ascii=False)) + + + # ------------------------------------------------------------------ + # 4) 진입 봉인 셋 (전략서 §4.1④) + # ------------------------------------------------------------------ + def verify_signal_seal(): + doc = read_json(SIGNAL_MANIFEST) + root = doc.get("signal_manifest", doc) if isinstance(doc, dict) else {} + transaction_id = root.get("transaction_id") + if not isinstance(transaction_id, str) or not TRANSACTION_ID_RE.match(transaction_id): + raise RuntimeError(json.dumps({ + "reason_code": "PART4_SIGNAL_SEAL_FAILED", "detail": "transaction_id", + "actual": transaction_id}, ensure_ascii=False)) + checked = 0 + for row in root.get("files") or []: + if not isinstance(row, dict): + continue + # files[].path 에는 signals/ 접두사가 없다. 붙여서 대조한다. + rel = str(row.get("path") or "") + actual = sha_text(read_raw("signals/" + rel)) + if actual != row.get("file_sha256"): + raise RuntimeError(json.dumps({ + "reason_code": "PART4_SIGNAL_SEAL_FAILED", "detail": "file_sha256", + "path": rel, "expected": row.get("file_sha256"), "actual": actual, + }, ensure_ascii=False)) + checked += 1 + return {"transaction_id": transaction_id, "files_checked": checked} + + + def verify_registry_index_seal(activation_root, index_raw): + declared = activation_root.get("registry_index_sha256") + actual = sha_text(index_raw) + if declared != actual: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_REGISTRY_INDEX_SEAL_FAILED", + "declared": declared, "actual": actual}, ensure_ascii=False)) + return actual + + + def verify_les_transaction(les_root, transaction_id): + """두 갈래 — 키가 있고 값이 다르면 경성 실패, 키가 없으면 검토(fail-open).""" + if "signal_manifest_transaction_id" not in les_root: + warn("PART4_LES_TRANSACTION_UNSEALED_REVIEW", + "LES 가 거래 식별자 키 없이 왔다 — 계약상 적법. 검토로 남긴다.") + return "UNSEALED_REVIEW" + value = les_root.get("signal_manifest_transaction_id") + if value != transaction_id: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_LES_TRANSACTION_MISMATCH", + "expected": transaction_id, "actual": value}, ensure_ascii=False)) + return "MATCHED" + + + def main(): + gate_manifest = assert_deployment() + staged = materialize_modules() + import fact_ledger_compiler + import schema_subset_validator + + # 2) 정책 반입 — 판본 불일치는 PART4_POLICY_INVALID. + policy = json.loads(_verify_asset(POLICY)) + if policy.get("schema_version") != POLICY_SCHEMA_VERSION: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_POLICY_INVALID", + "expected": POLICY_SCHEMA_VERSION, + "actual": policy.get("schema_version")}, ensure_ascii=False)) + + # 3) 의미 검사 (입력 쪽) + bundle_schema = json.loads(_verify_asset(BUNDLE_SCHEMA)) + assert_bundle_schema_capability(bundle_schema) + + # 4) 진입 봉인 셋 + seal = verify_signal_seal() + activation_doc = read_json(ACTIVATION_MANIFEST) + activation_root = activation_doc.get("domain_activation_manifest", activation_doc) + index_raw = read_raw(REGISTRY_INDEX) + registry_index_sha = verify_registry_index_seal(activation_root, index_raw) + registry_index = json.loads(index_raw) + index_root = registry_index.get("domain_registry_index", registry_index) + les_doc = read_json(LES_PATH) + les_root = les_doc if isinstance(les_doc, dict) else {} + les_seal = verify_les_transaction(les_root, seal["transaction_id"]) + + expected_runnable = [d for d in (activation_root.get("expected_runnable_domain_ids") or []) + if isinstance(d, str)] + + # 5) 입력 적재 + # domain_config 무결성은 SG-01 봉인 -> _registry_index -> config_sha256 사슬. + config_sha = {row.get("domain_id"): row.get("config_sha256") + for row in (index_root.get("entries") or []) if isinstance(row, dict)} + domain_configs = {} + gate_sidecars = {} + structure_type_sidecars = {} + sidecar_gaps = [] + for domain_id in expected_runnable: + raw = read_raw("Default_Agent/domains/%s/domain_config.json" % domain_id) + if config_sha.get(domain_id) and sha_text(raw) != config_sha[domain_id]: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_REGISTRY_INDEX_SEAL_FAILED", + "detail": "config_sha256", "domain_id": domain_id}, ensure_ascii=False)) + domain_configs[domain_id] = json.loads(raw) + try: + gate_sidecars[domain_id] = json.loads(_verify_asset( + "Default_Agent/domains/%s/gates.json" % domain_id)) + except Exception as exc: + # 사이드카 부재·미등재는 컴파일러가 게이트별 + # GATE_SPEC_NOT_DEPLOYED_REVIEW 로 가시화한다. 여기서는 계수만 한다. + sidecar_gaps.append({"domain_id": domain_id, "kind": "gates", + "error": str(exc)[:160]}) + try: + structure_type_sidecars[domain_id] = json.loads(_verify_asset( + "Default_Agent/domains/%s/structure_types.json" % domain_id)) + except Exception as exc: + sidecar_gaps.append({"domain_id": domain_id, "kind": "structure_types", + "error": str(exc)[:160]}) + + alias_declarations = json.loads(_verify_asset(ALIAS_DECLARATIONS)) + + signal_documents = { + "SG-04": read_json(SG04_PATH), + "SG-06": read_json(SG06_PATH), + "SG-07": read_json(SG07_PATH), + "SG-10": read_json(SG10_PATH), + "SG-11": read_json(SG11_PATH), + } + + domain_envelopes = {} + envelope_absent = [] + for domain_id in expected_runnable: + doc = try_read_json("%s/%s.json" % (DOMAIN_SIGNAL_DIR, domain_id)) + if doc is not None: + domain_envelopes[domain_id] = doc + else: + envelope_absent.append(domain_id) + + # P2 handoff · evidence 는 상수 경로를 read_json 에 직접 넘긴다 — + # 선언표 검사기(U4)가 읽기를 코드에서 확인할 수 있는 형태다(Part 3 규율). + # 간접 호출로 감싸면 소비 선언이 실측 불가가 된다. 부재는 try 로 감싸 계수한다. + try: + p2_handoff = read_json(P2_HANDOFF_PATH) + except Exception: + warn("P2_HANDOFF_ABSENT", P2_HANDOFF_PATH) + p2_handoff = {} + + bo_doc = read_json(BO_PATH) + bo_records = bo_doc if isinstance(bo_doc, list) else [] + try: + evidence_doc = read_json(EVIDENCE_PATH) + except Exception: + warn("EVIDENCE_INDEXED_ABSENT", EVIDENCE_PATH) + evidence_doc = {} + + # 6) 모듈 호출 — 판정 규칙을 이 파일에 쓰지 않는다. + try: + result = fact_ledger_compiler.compile_fact_ledger_candidates( + bo_records, les_root, signal_documents, domain_envelopes, + p2_handoff, evidence_doc, registry_index, domain_configs, + gate_sidecars, structure_type_sidecars, activation_doc, + alias_declarations, policy) + except TypeError as exc: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_COMPILER_STALE", "detail": "signature_mismatch", + "signature_error": str(exc)[:200], + "remedy": PART4_DEPLOYMENT_REMEDY}, ensure_ascii=False)) + assert_compiler_capability(result) + + bundle = result["fact_ledger_candidate_bundle"] + pack = result["fact_exception_pack"] + review_items = result["review_items"] + audit = result["audit"] + # 진입 봉인의 거래 식별자를 bundle 에 부착한다 — F2 는 signal_manifest 를 + # 다시 읽지 않고 이 값을 승계한다. + bundle["signal_manifest_transaction_id"] = seal["transaction_id"] + bundle["les_transaction_seal"] = les_seal + bundle["created_by"] = TASK_NAME + + # 자기 산출을 닫힌 스키마로 검증한 뒤에만 기록한다. + schema_errors = schema_subset_validator.validate( + {"fact_ledger_candidate_bundle": bundle}, bundle_schema) + if schema_errors: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_LEDGER_SCHEMA_INVALID", + "stage": "F0", "errors": schema_errors[:10]}, ensure_ascii=False)) + pack_schema = json.loads(_verify_asset(PACK_SCHEMA)) + pack_errors = schema_subset_validator.validate( + {"fact_exception_pack": pack}, pack_schema) + if pack_errors: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_LEDGER_SCHEMA_INVALID", + "stage": "F0.pack", "errors": pack_errors[:10]}, ensure_ascii=False)) + + # 7) 기록 — bundle · pack · handoff(OPEN) · receipt(실측값. 상수 PASS 금지). + write_doc(BUNDLE_PATH, canonical({"fact_ledger_candidate_bundle": bundle})) + write_doc(PACK_PATH, canonical({"fact_exception_pack": pack})) + write_doc(HANDOFF_PATH, canonical({"stage1_part4_review_handoff": { + "schema_version": "stage1_part4_review_handoff.v1", + "status": "OPEN", + "review_items": review_items, + "counts": {"review_items": len(review_items)}, + "created_by": TASK_NAME, + }})) + counts = bundle.get("counts") or {} + write_doc(RECEIPT_PATH, canonical({ + "schema_version": "stage1_part4_receipt.v1", + "stage": "P4-F0", + "deployment_gate": { + "checked_count": len(set(list(MODULE_MIRRORS.values()) + + list(PART4_REQUIRED_ASSETS))), + "runtime_manifest_sha256": sha_text(read_raw(RUNTIME_MANIFEST)), + "runtime_artifact_count": gate_manifest.get("runtime_artifact_count"), + "schema_capability_checked": ["rows[].domain_effects", + "rows[].calculation_requests"], + "compiler_output_checked": ["rows[].domain_effects", "gate_report"], + "policy_schema_version": policy.get("schema_version"), + "note": "무엇을 봤는지 적는다. 상수 PASS 는 증거가 아니다.", + }, + "entry_seals": { + "signal_manifest_transaction_id": seal["transaction_id"], + "signal_files_checked": seal["files_checked"], + "registry_index_sha256": registry_index_sha, + "config_sha256_chain_checked": len(domain_configs), + "les_transaction_seal": les_seal, + }, + "gate_execution": { + "declared": counts.get("gates_declared"), + "implemented": counts.get("gates_implemented"), + "visualized_str": counts.get("gates_visualized_str"), + "visualized_unsupported": counts.get("gates_visualized_unsupported"), + "fired": counts.get("gates_fired"), + "orphan_rules": counts.get("orphan_rules"), + "sidecar_dialects": audit.get("sidecar_dialects"), + "input_counts": audit.get("gate_input_counts"), + "note": "선언 존재와 실측 비공백을 따로 적는다(§7.5-4).", + }, + "conservation": counts.get("conservation"), + "envelope_absent_domains": envelope_absent, + "sidecar_gaps": sidecar_gaps, + "evidence_truncation_slack_min": audit.get("evidence_truncation_slack_min"), + "warnings": WARNINGS, + "created_by": TASK_NAME, + })) + + return {"status": bundle.get("status"), + "modules": staged, + "expected_runnable_domain_ids": expected_runnable, + "row_count": len(bundle.get("rows") or []), + "gates": {"declared": counts.get("gates_declared"), + "implemented": counts.get("gates_implemented"), + "fired": counts.get("gates_fired")}, + "exception_count": pack.get("exception_count"), + "review_item_count": len(review_items), + "errors": ERRORS, "warnings": WARNINGS} + + + _sink = io.StringIO() + with contextlib.redirect_stdout(_sink): + _init() + RESULT = main() + print(json.dumps(RESULT, ensure_ascii=False)) + + - task_name: Task_C_FL_F1_fact_exception_adjudicator + llm_provider: google + llm_model: gemini-3.1-flash-lite + llm_reasoning: low + llm_verbosity: low + max_iterations: 1 + use_tools: + - localdocs + cache_control: + mode: auto + ttl: 20m + preflight: true + preflight_files: + - stage1_tmp/fact_ledger/fact_exception_pack.json + prompts: + - role: user + content: |- + + + - You are executing one LLM sub-task inside Stage 1 of a Korean civil-litigation complaint-generation pipeline. + - The current task's static block, role overlay, assigned inputs, output schema, and writer boundary control. + - This common prefix cannot expand the current task's input set, output set, legal domain, validation authority, writer authority, or reasoning depth. + - If any common rule appears broader than the current task, apply only the narrower current-task version. + - Stage 1 prepares verified structured artifacts. Do not draft complaint prose or final counsel-level conclusions unless the current task explicitly authorizes a validation or gate conclusion. + + + + - Use only assigned files, provided context inputs, prior outputs, and allowed tools. + - Do not import facts, law, procedural history, parties, dates, amounts, IDs, document contents, or source meanings from memory, outside knowledge, or unassigned files. + - Treat prior outputs as authority only to the extent the current task names them or provides them as context. + - If a value is unsupported, missing, conflicting, stale, or out of scope, use only the current schema's allowed null, empty, unknown, warning, blocked, or needs_review path. + + + + - Preserve exact source identifiers required by the current schema. + - Maintain separation among raw fact, inferred fact, legal signal, evidence support, fact support, validation issue, and final gate decision when the current schema distinguishes them. + - Do not upgrade meeting-only or indirect material into direct proof. + - Do not silently resolve material conflicts. If the current schema has a conflict or uncertainty field, use it; otherwise stay within the task's allowed warning or review path. + + + + - Follow required JSON shape, key names, enum values, ordering, file names, and status strings exactly. + - Do not add arbitrary keys, prose, markdown fences, alternative files, unauthorized repair, or explanatory material outside allowed fields. + - Create, mutate, normalize, merge, or finalize IDs only when the current task explicitly authorizes it. + - Write final files only when the current task is the authorized writer. Validators and guards report issues in their own authorized schema and do not silently repair unless instructed. + + + + - Prefer the current prompt and schema, assigned structured upstream artifacts, compact indexes, ledgers, manifests, bundles, and gates. + - Read raw evidence or meeting text only when the current task requires direct provenance, ambiguity resolution, or a schema-required value missing from structured artifacts. + - For map or projection tasks, process only the assigned item, domain, or batch. Reducers aggregate only the inputs assigned to them. + - Do not restate, summarize, cite, or copy this common prefix in any output. + + + + - Return only the requested structured artifact, concise allowed rationale fields, validation notes, or status object. + - Keep chain-of-thought private. + - Stop when the current schema is complete and safe. + + + + + + TASK_NAME: Task_C_FL_F1_fact_exception_adjudicator + STAGE: Part 4 조건부 예외 판정자 (Part 2 R1 패턴 승계) + MISSION: 결정적 reducer(F0)가 pack 에 실은 예외만 판정한다. 유일 생존 부류는 + legal_calculation_required(계산 근거 다중성)다. 병합·최종 파일 작성·사실 창작은 하지 않는다. + + + + - 유일한 입력은 preflight 로 제공된 `stage1_tmp/fact_ledger/fact_exception_pack.json` 이다. + - BO.json, 후보 bundle, 최종 원장, review handoff, signal 파일을 읽거나 쓰지 않는다. + - pack 에 없는 exception_id·fact_id·source_bo_id 를 창작하지 않는다. + - 금액을 계산하지 않는다. 제공된 후보 중 선택하거나 차단할 뿐이다. + - 출력 파일은 `stage1_tmp/fact_ledger/fact_adjudication.json` 하나뿐이다. + - 도구 호출은 write_file 하나만 허용된다(출력 기록 용도). 다른 read 는 금지된다. + + + + 1. preflight 로 제공된 exception pack 의 `has_exceptions` 를 확인한다. + 2. `has_exceptions == false` 이면: `write_file(overwrite=true)` 로 아래 no-exception 객체를 + `stage1_tmp/fact_ledger/fact_adjudication.json` 에 저장하고 `"NO_EXCEPTIONS"` 만 출력한 뒤 + 즉시 종료한다(terminate). 다른 어떤 파일도 읽지 않는다. + {"fact_exception_adjudication": {"schema_version": "stage1_fact_exception_adjudication.v1", "status": "READY_NO_EXCEPTIONS", "decisions": [], "blocked_review_items": [], "adjudication_gate": {"input_exception_count": 0, "decision_count": 0, "blocked_count": 0}}} + 3. `has_exceptions == true` 이면: 각 exception 을 `compact_payload` 만으로 판정한다. 추가 read 는 금지된다. + 4. 판정 규칙 (부류: legal_calculation_required — 계산 근거 다중성): + - `compact_payload.amount_candidates` 중 원문 근거가 더 구체적인 쪽(수치·통화·기간이 더 완전한 후보)을 + `selected_value` 로 선택한다. decision 은 `SELECT_AMOUNT`, `patch_field` 는 반드시 `"amount"` 다. + - 우열을 가릴 수 없으면 decision `BLOCK_REVIEW` 로 `blocked_review_items` 에 넣는다 + (확신 없는 확정 금지 — 인간 검토 라우팅). + - `patch_field` 에 `amount` 외의 값을 쓰지 않는다 — F2 가 PART4_PATCH_REJECTED 로 경성 거부한다. + - `domain_effects` 하위 키 경로는 참조(읽기)만 허용되며 수정 대상이 아니다. + 5. `write_file(overwrite=true)` 로 결과를 저장한다. root 는 `fact_exception_adjudication` 이며 + schema_version 은 `stage1_fact_exception_adjudication.v1`, status 는 전 항목 비차단이면 `READY`, + 차단 존재 시 `READY_WITH_BLOCKS` 다. 모든 decision 은 pack 의 `exception_id` 와 `fact_id` 를 인용한다. + decision 객체: {"exception_id", "fact_id", "decision", "patch_field", "selected_value", "rationale"(1문장)}. + adjudication_gate: {"input_exception_count", "decision_count", "blocked_count"}. + 6. `"F1 예외 판정 완료 (decisions=<건수>)"` 만 출력하고 작업을 끝낸다(terminate). + + + + - exception pack 부재·파싱 불가: 즉시 중단하고 채팅으로만 보고한다. adjudication 파일은 쓰지 않는다. + - tool 오류: 1회만 재시도. 재실패 시 `FAILED: ` 만 보고하고 종료한다. + + + + - task_name: Task_C_FL_F2_final_fact_ledger_gate_and_writer + mcp: code-executor + tool_name: run_code + parameters: + language: python + requirements: "httpx" + network: "agent-network" + timeout: 240 + code: |- + #!/usr/bin/env python3 + # Task_C_FL_F2_final_fact_ledger_gate_and_writer (v1) + # 1) 게이트·정책·의미 검사 -> 2) bundle·pack·판정·원본 적재 -> + # 3) 판정 커버리지·패치 적용(허용 필드만) -> 4) 보존 등식 다섯(바깥 수 직접 계수) -> + # 5) domain_effects ⊆ 활성 · fact_id 연속 · BO 보존 -> 6) 닫힌 스키마 -> + # 7) 기록·재읽기 -> 8) writer report · handoff FINALIZED + import contextlib + import hashlib + import io + import itertools + import json + import pathlib + import sys + + import httpx + + TASK_NAME = "Task_C_FL_F2_final_fact_ledger_gate_and_writer" + LOCALDOCS_URL = "http://mcp-localdocs:8012/mcp" + MCP_HEADERS = {"Content-Type": "application/json", + "Accept": "application/json, text/event-stream"} + CLIENT = httpx.Client(timeout=120) + MSG_ID_COUNTER = itertools.count(10) + + + def next_msg_id(): + return next(MSG_ID_COUNTER) + + + def _init(): + r = CLIENT.post(LOCALDOCS_URL, json={ + "jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": {"protocolVersion": "2025-03-26", "capabilities": {}, + "clientInfo": {"name": TASK_NAME, "version": "1.0", + "user_id": "{{__user_hash__}}", + "workspace_id": "{{__workspace_hash__}}"}} + }, headers=MCP_HEADERS) + r.raise_for_status() + sid = r.headers.get("mcp-session-id") + if sid: + MCP_HEADERS["mcp-session-id"] = sid + CLIENT.post(LOCALDOCS_URL, + json={"jsonrpc": "2.0", "method": "notifications/initialized"}, + headers=MCP_HEADERS).raise_for_status() + + + def _parse_mcp(text): + for line in text.strip().split("\n"): + if line.startswith("data: "): + try: + return json.loads(line[6:]) + except Exception: + return None + try: + return json.loads(text) + except Exception: + return None + + + def _call(name, args, mid): + r = CLIENT.post(LOCALDOCS_URL, json={ + "jsonrpc": "2.0", "id": mid, "method": "tools/call", + "params": {"name": name, "arguments": args} + }, headers=MCP_HEADERS) + r.raise_for_status() + p = _parse_mcp(r.text) + if not p or "result" not in p: + raise RuntimeError("MCP_CALL_FAILED:%s" % name) + return p + + + def read_raw(name): + p = _call("read_docs", {"doc_names": [name]}, next_msg_id()) + text = (p["result"].get("content") or [{}])[0].get("text", "") + if not text: + raise RuntimeError("EMPTY_RESPONSE:%s" % name) + try: + outer = json.loads(text) + except Exception: + return text + if isinstance(outer, str): + return outer + if isinstance(outer, dict) and "results" in outer: + r0 = (outer.get("results") or [{}])[0] + inner = r0.get("content") + if inner is None: + inner = r0.get("text") + if not isinstance(inner, str) or not inner: + raise RuntimeError("EMPTY_CONTENT:%s" % name) + return inner + raise RuntimeError("UNEXPECTED_READ_DOCS_ENVELOPE:%s" % name) + + + def read_json(name): + raw = read_raw(name) + s = raw.strip() + try: + return json.loads(s) + except json.JSONDecodeError: + obj, _ = json.JSONDecoder().raw_decode(s) + return obj + + + def try_read_json(name): + try: + return read_json(name) + except Exception: + return None + + + def write_doc(path, content): + _call("write_file", {"path": path, "content": content, "overwrite": True}, + next_msg_id()) + + ASSET_ROOT = "Default_Agent/" + EXECUTION_ROOT = "/tmp/s1" + RT = pathlib.Path(EXECUTION_ROOT) / "_rt" + + MODULE_MIRRORS = { + "runtime_common": "Default_Agent/stage1_runtime/runtime_common.txt", + "schema_subset_validator": "Default_Agent/stage1_runtime/schema_subset_validator.txt", + } + MODULES = ["runtime_common", "schema_subset_validator"] + RUNTIME_MANIFEST = "Default_Agent/runtime_manifest.json" + + POLICY = "Default_Agent/stage1_runtime/fact_ledger_policy.v1.json" + POLICY_SCHEMA_VERSION = "stage1_fact_ledger_policy.v1" + BASE_SCHEMA = "Default_Agent/platform/schemas/fact_ledger_base.schema.json" + + BUNDLE_PATH = "stage1_tmp/fact_ledger/fact_ledger_candidate_bundle.json" + PACK_PATH = "stage1_tmp/fact_ledger/fact_exception_pack.json" + ADJUDICATION_PATH = "stage1_tmp/fact_ledger/fact_adjudication.json" + HANDOFF_PATH = "quality_gates/stage1_part4_review_handoff.json" + ACTIVATION_MANIFEST = "routing/domain_activation_manifest.json" + BO_PATH = "BO.json" + LES_PATH = "legal_effect_structures.json" + SG11_PATH = "signals/calculation_requirements.json" + TARGET_PATH = "Fact_Ledger_base.json" + REPORT_PATH = "stage1_tmp/fact_ledger/fact_ledger_writer_report.json" + + PART4_DEPLOYMENT_REMEDY = ( + "배포 원본 extension_research/Default_Agent 트리를 서버 Default_Agent/ 에 통째로 배포하고 " + "runtime_manifest.json 의 등재·sha256 과 대조해 결손 경로를 복원한다. 부분 복사는 허용되지 않는다.") + + _MANIFEST_MAP = None + + + def sha_text(text): + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + + def canonical(value): + return json.dumps(value, ensure_ascii=False, sort_keys=True, + separators=(",", ":")) + "\n" + + + def _manifest_map(): + global _MANIFEST_MAP + if _MANIFEST_MAP is None: + _MANIFEST_MAP = {row["path"]: row["sha256"] + for row in (json.loads(read_raw(RUNTIME_MANIFEST)).get("entries") or []) + if isinstance(row, dict)} + return _MANIFEST_MAP + + + def _deployment_error(logical, detail, extra=None): + payload = {"reason_code": "PART4_ASSET_DEPLOYMENT_INCOMPLETE", "path": logical, + "detail": detail, "remedy": PART4_DEPLOYMENT_REMEDY} + if extra: + payload.update(extra) + return RuntimeError(json.dumps(payload, ensure_ascii=False)) + + + def _verify_asset(logical): + body = read_raw(logical) + want = _manifest_map().get(logical[len(ASSET_ROOT):]) + if want is None: + raise _deployment_error(logical, "unregistered") + if want != sha_text(body): + raise _deployment_error(logical, "hash_mismatch", {"expected": want}) + return body + + + def materialize_modules(): + RT.mkdir(parents=True, exist_ok=True) + expected = _manifest_map() + staged = [] + for name in MODULES: + logical = MODULE_MIRRORS[name] + raw = read_raw(logical).encode("utf-8") + want = expected.get(logical[len(ASSET_ROOT):]) + if want is None: + raise RuntimeError("MODULE_MIRROR_UNREGISTERED:%s" % logical) + if want != hashlib.sha256(raw).hexdigest(): + raise RuntimeError("MODULE_MIRROR_HASH_MISMATCH:%s" % logical) + (RT / (name + ".py")).write_bytes(raw) + staged.append(name) + if str(RT) not in sys.path: + sys.path.insert(0, str(RT)) + return staged + + + def assert_base_schema_capability(base_schema): + row_props = ((base_schema.get("items") or {}).get("properties") or {}) + missing = [key for key in ("domain_effects", "calculation_requests") + if key not in row_props] + if missing: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_LEDGER_SCHEMA_STALE", + "message": "최종 스키마가 신규 2키를 모른다. 배포 원본의 스키마가 개정 전 판본이다.", + "missing": missing, "remedy": PART4_DEPLOYMENT_REMEDY, + }, ensure_ascii=False)) + + + def main(): + staged = materialize_modules() + import schema_subset_validator + + policy = json.loads(_verify_asset(POLICY)) + if policy.get("schema_version") != POLICY_SCHEMA_VERSION: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_POLICY_INVALID", + "expected": POLICY_SCHEMA_VERSION, + "actual": policy.get("schema_version")}, ensure_ascii=False)) + base_schema = json.loads(_verify_asset(BASE_SCHEMA)) + assert_base_schema_capability(base_schema) + + # 입력은 전부 파일 계약. 메모리 전달 없음. + bundle_doc = read_json(BUNDLE_PATH) + bundle = bundle_doc.get("fact_ledger_candidate_bundle", bundle_doc) + rows = [row for row in (bundle.get("rows") or []) if isinstance(row, dict)] + pack_doc = read_json(PACK_PATH) + pack = pack_doc.get("fact_exception_pack", pack_doc) + exceptions = [e for e in (pack.get("exceptions") or []) if isinstance(e, dict)] + handoff_doc = read_json(HANDOFF_PATH) + handoff = handoff_doc.get("stage1_part4_review_handoff", handoff_doc) + activation_doc = read_json(ACTIVATION_MANIFEST) + activation_root = activation_doc.get("domain_activation_manifest", activation_doc) + expected_runnable = set(d for d in (activation_root.get("expected_runnable_domain_ids") or []) + if isinstance(d, str)) + + # ------------------------------------------------------------------ + # 판정 파일 조건부 — exception_count > 0 인데 부재면 실패. + # ------------------------------------------------------------------ + # 상수 경로를 read_json 에 직접 넘긴다(U4 가시성 — Part 3 규율). 부재는 try 로 계수. + try: + adjudication_doc = read_json(ADJUDICATION_PATH) + except Exception: + adjudication_doc = None + if adjudication_doc is None: + if len(exceptions) > 0: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_ADJUDICATION_MISSING", + "exception_count": len(exceptions)}, ensure_ascii=False)) + adjudication = {"status": "READY_NO_EXCEPTIONS", "decisions": [], + "blocked_review_items": []} + else: + adjudication = adjudication_doc.get("fact_exception_adjudication", + adjudication_doc) + accepted = tuple((policy.get("adjudication") or {}).get("accepted_status") + or ()) + if adjudication.get("status") not in accepted: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_ADJUDICATION_MISSING", + "detail": "status_out_of_contract", + "actual": adjudication.get("status")}, ensure_ascii=False)) + + # ------------------------------------------------------------------ + # 판정 커버리지 (구본 자구 승계) — expected == decided ∪ blocked · + # 교집합 공집합 · 중복·미지 ID·예외 0 의 임의 decision 은 경성 실패. + # ------------------------------------------------------------------ + expected_ids = [e.get("exception_id") for e in exceptions] + decisions = [d for d in (adjudication.get("decisions") or []) + if isinstance(d, dict)] + blocked = [b for b in (adjudication.get("blocked_review_items") or []) + if isinstance(b, dict)] + decided_ids = [d.get("exception_id") for d in decisions] + blocked_ids = [b.get("exception_id") for b in blocked] + coverage_failures = [] + if len(set(decided_ids)) != len(decided_ids): + coverage_failures.append("duplicate_decision") + if set(decided_ids) & set(blocked_ids): + coverage_failures.append("decided_and_blocked_overlap") + if set(decided_ids) | set(blocked_ids) != set(expected_ids): + coverage_failures.append("expected_neq_decided_union_blocked") + unknown = (set(decided_ids) | set(blocked_ids)) - set(expected_ids) + if unknown: + coverage_failures.append("unknown_exception_ids") + if not exceptions and (decisions or blocked): + coverage_failures.append("decision_without_exception") + if coverage_failures: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_COVERAGE_BROKEN", + "failures": coverage_failures, + "expected": expected_ids, "decided": decided_ids, + "blocked": blocked_ids}, ensure_ascii=False)) + + # ------------------------------------------------------------------ + # 패치 적용 — 정책이 선언한 허용 필드만. 밖이면 경성 거부. + # ------------------------------------------------------------------ + allowed_patch = set((policy.get("adjudication") or {}).get( + "allowed_patch_fields") or []) + rows_by_fact = {row.get("fact_id"): row for row in rows} + patched = [] + for decision in decisions: + field = decision.get("patch_field") + if decision.get("decision") == "BLOCK_REVIEW": + continue + if field not in allowed_patch: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_PATCH_REJECTED", + "exception_id": decision.get("exception_id"), + "patch_field": field, + "allowed": sorted(allowed_patch)}, ensure_ascii=False)) + target = rows_by_fact.get(decision.get("fact_id")) + if target is None: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_PATCH_REJECTED", + "detail": "unknown_fact_id", + "fact_id": decision.get("fact_id")}, ensure_ascii=False)) + target[field] = decision.get("selected_value") + patched.append({"fact_id": decision.get("fact_id"), "field": field}) + + # ------------------------------------------------------------------ + # domain_effects ⊆ SG-01 활성 — 위반 시 즉시 실패 (등식보다 앞 — G-g 기대). + # ------------------------------------------------------------------ + for row in rows: + bad = sorted(set(row.get("domain_effects") or {}) - expected_runnable) + if bad: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_DOMAIN_EFFECT_NOT_ACTIVE", + "fact_id": row.get("fact_id"), "domains": bad}, + ensure_ascii=False)) + + # ------------------------------------------------------------------ + # 보존 등식 다섯 — 바깥 수를 원본에서 직접 계수한다(bundle counts 를 믿지 않는다). + # ------------------------------------------------------------------ + bo_doc = read_json(BO_PATH) + bo_records = bo_doc if isinstance(bo_doc, list) else [] + les_doc = read_json(LES_PATH) + les_root = les_doc if isinstance(les_doc, dict) else {} + structure_records = [r for r in (les_root.get("structure_records") or []) + if isinstance(r, dict)] + record_by_id = {r.get("structure_id"): r for r in structure_records + if isinstance(r.get("structure_id"), str)} + by_bo_id = {k: [s for s in v if isinstance(s, str)] + for k, v in ((les_root.get("structure_index") or {}) + .get("by_bo_id") or {}).items()} + sg11_doc = read_json(SG11_PATH) + sg11_records = [r for r in (sg11_doc.get("records") or []) + if isinstance(r, dict)] + sg11_ids = {r.get("signal_id") for r in sg11_records + if isinstance(r.get("signal_id"), str)} + + failures = [] + # ① 행 + if len(rows) != len(bo_records): + failures.append({"reason_code": "PART4_ROW_CONSERVATION_BROKEN", + "rows": len(rows), "bo_total": len(bo_records)}) + # ② LES 부착 커버리지 + attached_bo = 0 + unattached_bo = 0 + for row in rows: + if (row.get("linked_structures") or {}).get("structure_ids"): + attached_bo += 1 + else: + unattached_bo += 1 + if attached_bo + unattached_bo != len(bo_records): + failures.append({"reason_code": "PART4_BO_ATTACHMENT_COVERAGE_BROKEN", + "attached": attached_bo, "unattached": unattached_bo, + "bo_total": len(bo_records)}) + # ③ 효과 (도메인 차원) + effect_left = sum(len(row.get("domain_effects") or {}) for row in rows) + effect_right = 0 + calc_right = 0 + used_sg11 = set() + for bo in bo_records: + bo_id = str(bo.get("BO_ID") or "") + attached = [record_by_id[s] for s in by_bo_id.get(bo_id, []) + if s in record_by_id] + effect_right += len({r.get("domain_id") for r in attached + if isinstance(r.get("domain_id"), str)}) + refs = {ref for r in attached + for ref in (r.get("calculation_request_refs") or []) + if isinstance(ref, str)} + calc_right += len(refs) + used_sg11.update(ref for ref in refs if ref in sg11_ids) + if effect_left != effect_right: + failures.append({"reason_code": "PART4_EFFECT_CONSERVATION_BROKEN", + "left": effect_left, "right": effect_right}) + # ④ 계산 (행 차원) + calc_left = sum(len(row.get("calculation_requests") or []) for row in rows) + if calc_left != calc_right: + failures.append({"reason_code": "PART4_CALC_CONSERVATION_BROKEN", + "left": calc_left, "right": calc_right}) + # ⑤ 계산 (원천 차원) + sg11_unused = len(sg11_ids) - len(used_sg11) + if len(used_sg11) + sg11_unused != len(sg11_ids): + failures.append({"reason_code": "PART4_CALC_SOURCE_CONSERVATION_BROKEN", + "attached": len(used_sg11), "unattached": sg11_unused, + "total": len(sg11_ids)}) + if failures: + raise RuntimeError(json.dumps({"conservation_failures": failures}, + ensure_ascii=False)) + + # BO 보존 — 후보 ↔ 최종 BO 집합 동일성 + fact_id 연속·유일 (구본 자구). + candidate_bo = sorted(str(b.get("BO_ID") or "") for b in bo_records) + final_bo = sorted(str(row.get("source_bo_id") or "") for row in rows) + if candidate_bo != final_bo: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_BO_CONSERVATION_BROKEN"}, ensure_ascii=False)) + fact_ids = [row.get("fact_id") for row in rows] + if fact_ids != ["F-%03d" % i for i in range(1, len(rows) + 1)]: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_FACT_ID_SEQUENCE_BROKEN"}, ensure_ascii=False)) + + # ------------------------------------------------------------------ + # 닫힌 스키마 -> 기록 -> 재읽기. + # ------------------------------------------------------------------ + schema_errors = schema_subset_validator.validate(rows, base_schema) + if schema_errors: + raise RuntimeError(json.dumps({ + "reason_code": "PART4_LEDGER_SCHEMA_INVALID", + "stage": "F2", "errors": schema_errors[:10]}, ensure_ascii=False)) + text = canonical(rows) + write_doc(TARGET_PATH, text) + reread = read_raw(TARGET_PATH) + if sha_text(reread) != sha_text(text): + raise RuntimeError("PART4_WRITE_REREAD_MISMATCH") + + # ------------------------------------------------------------------ + # writer report 3종 — 게이트별 발화 목록 · 도메인 효과 커버리지 · 계산 준비도. + # F2 는 receipt 를 쓰지 않는다(F0 단독 기록). + # ------------------------------------------------------------------ + gate_report = bundle.get("gate_report") or {} + effect_coverage = {} + for row in rows: + for domain_id in (row.get("domain_effects") or {}): + effect_coverage[domain_id] = effect_coverage.get(domain_id, 0) + 1 + readiness = {} + for row in rows: + for req in (row.get("calculation_requests") or []): + state = str(req.get("operand_state")) + readiness[state] = readiness.get(state, 0) + 1 + review_count = len(handoff.get("review_items") or []) + blocked_count = len(blocked) + row_review = any(row.get("skeleton_validation_codes") for row in rows) + status = ("READY_WITH_REVIEW" + if (review_count or blocked_count or row_review + or bundle.get("status") == "READY_WITH_REVIEW") + else "READY") + write_doc(REPORT_PATH, canonical({ + "schema_version": "stage1_fact_ledger_writer_report.v1", + "final_writer_status": status, + "row_count": len(rows), + "gate_firings": gate_report.get("fired_by_domain") or {}, + "domain_effect_coverage": effect_coverage, + "calculation_readiness": readiness, + "patched": patched, + "blocked_review_items": blocked, + "conservation": { + "row": {"rows": len(rows), "bo_total": len(bo_records)}, + "attachment": {"attached": attached_bo, "unattached": unattached_bo}, + "effect": {"left": effect_left, "right": effect_right}, + "calc_row": {"left": calc_left, "right": calc_right}, + "calc_source": {"attached": len(used_sg11), + "unattached": sg11_unused, + "total": len(sg11_ids)}, + }, + "final_sha256": sha_text(text), + "created_by": TASK_NAME, + })) + + # handoff FINALIZED 갱신(F0 과 공동 기록자). 항목을 줄이지 않는다. + handoff["status"] = "FINALIZED" + handoff["finalized_by"] = TASK_NAME + write_doc(HANDOFF_PATH, canonical({"stage1_part4_review_handoff": handoff})) + + return {"status": status, "modules": staged, + "row_count": len(rows), + "patched_count": len(patched), + "blocked_count": blocked_count, + "conservation_pass": True, + "final_sha256": sha_text(text)} + + + _sink = io.StringIO() + with contextlib.redirect_stdout(_sink): + _init() + RESULT = main() + print(json.dumps(RESULT, ensure_ascii=False)) + + task_procedure: + # F0 가 bundle 과 pack 을 내고서야 F1 이 서고, F1 판정이 있어야 F2 가 선다. 완전 직렬이다. + IN: + nexts: ["Task_C_FL_F0_fact_ledger_reducer"] + wait_until: [] + + Task_C_FL_F0_fact_ledger_reducer: + nexts: ["Task_C_FL_F1_fact_exception_adjudicator"] + wait_until: ["IN"] + + # F1 은 예외 0 이어도 READY_NO_EXCEPTIONS 를 쓰고 종료한다 — 그래프상 무조건 노드. + Task_C_FL_F1_fact_exception_adjudicator: + nexts: ["Task_C_FL_F2_final_fact_ledger_gate_and_writer"] + wait_until: ["Task_C_FL_F0_fact_ledger_reducer"] + + Task_C_FL_F2_final_fact_ledger_gate_and_writer: + nexts: ["OUT"] + wait_until: ["Task_C_FL_F1_fact_exception_adjudicator"] + + OUT: + nexts: [] + wait_until: ["Task_C_FL_F2_final_fact_ledger_gate_and_writer"] + + prevs: [] + nexts: [] diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/handoffs/stage1_part_interface.v1.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/handoffs/stage1_part_interface.v1.json index 5bf34f85..44b481a1 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/handoffs/stage1_part_interface.v1.json +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/handoffs/stage1_part_interface.v1.json @@ -1 +1,922 @@ -{"authored_not_derived":"이 표는 계약이며 코드에서 뽑지 않았다. 코드에서 뽑으면 검사가 자기 자신을 확인하는 셈이 된다. 검사기(_check_stage1_interface.py)가 코드를 이 표와 대조한다.","checker":"validation_assets/routing/_check_stage1_interface.py","conventions":{"also_written_by":"선언된 생산자 외에 갱신을 허용하는 task. 생산자 위반으로 세지 않는다.","asset_paths_excluded":"Default_Agent/ 아래 정적 자산은 이 표의 대상이 아니다. 검사 10 이 따로 본다.","optional":"부재가 곧 실패는 아니지만 조건이 붙는다. note 를 읽는다.","path_is_pattern":" 자리는 런타임 값이다. 검사기는 접두·접미로 대조한다.","produced_by_module":"생산자 task 가 키 구성을 런타임 모듈에 위임할 때 그 모듈 경로. 필수 키 검사는 그 원문까지 함께 본다."},"entry_inputs":[{"consumer_tasks":["Task_A0_domain_screener_01","Task_A0_domain_screener_02","Task_A_client_goal","Task_B1_map_doc_*","Task_B2_map_events_*","Task_C_BO_A0_context_and_domain_slice_compiler"],"note":"사건 입력. 어느 task 도 쓰지 않는다. Part 2 A0 가 read_raw 로 직접 읽는다(C-8 반영).","optional":false,"path":"client_meeting.md","path_is_pattern":false,"producer_task":null,"stage":"INPUT"},{"consumer_tasks":["Task_Evidence_shard_planner"],"note":"사건 입력. 어느 task 도 쓰지 않는다.","optional":false,"path":"evidence_all.json","path_is_pattern":false,"producer_task":null,"required_keys":["documents","source_files","total_count"],"stage":"INPUT"}],"handoff_count":45,"handoffs":[{"consumer_tasks":["Task_A0_domain_screener_01","Task_A0_domain_screener_02","Task_A_client_goal","Task_B1_map_doc_*","Task_B2_map_events_*","Task_C_BO_A0_context_and_domain_slice_compiler"],"note":"사건 입력. 어느 task 도 쓰지 않는다. Part 2 A0 가 read_raw 로 직접 읽는다(C-8 반영).","optional":false,"path":"client_meeting.md","path_is_pattern":false,"producer_task":null,"stage":"INPUT"},{"consumer_tasks":["Task_Evidence_shard_planner"],"note":"사건 입력. 어느 task 도 쓰지 않는다.","optional":false,"path":"evidence_all.json","path_is_pattern":false,"producer_task":null,"required_keys":["documents","source_files","total_count"],"stage":"INPUT"},{"consumer_tasks":["Task_A0_domain_screener_02"],"note":"LLM 초안. 비판단 6종(schema_version·registry_version·registry_index_sha256·source_document_refs·screening_policy·contract_guards)을 담지 않는다. candidates 원소 키 구성은 닫힌 스키마의 후보 객체와 같아야 한다.","optional":false,"path":"routing/domain_screening_draft.json","path_is_pattern":false,"producer_task":"Task_A0_domain_screener_01","required_keys":["candidates","review_items","status","unmatched_issue_refs"],"root_key":"domain_screening_draft","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_A0_domain_screener_03","Task_A_client_goal","Task_D0_domain_activation_gate","Task_B2_SHA256_soft_gate_handoff_writer","Task_C_BO_A0_context_and_domain_slice_compiler"],"note":"screening_sha256 의 원문. 재직렬화 금지. Part 2 A0 는 봉인 원문 바이트와 별도로 candidates[].requested_calculation_domains 를 파싱해 슬라이스로 통과시킨다(R-2).","optional":false,"path":"routing/domain_screening.json","path_is_pattern":false,"producer_task":"Task_A0_domain_screener_02","required_keys":["candidates","contract_guards","registry_index_sha256","registry_version","review_items","schema_version","screening_policy","source_document_refs","status","unmatched_issue_refs"],"schema_version":"stage1_domain_screening.v1","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_A_client_goal"],"optional":false,"path":"routing/candidate_profile_vocabulary.md","path_is_pattern":false,"producer_task":"Task_A0_domain_screener_03","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_B1_map_doc_*","Task_B1_quality_gate_evidence_indexed","Task_B2_quality_gate_event_candidates"],"optional":false,"path":"evidence_shard_plan.json","path_is_pattern":false,"producer_task":"Task_Evidence_shard_planner","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_D0_domain_activation_gate","Task_C_BO_A0_context_and_domain_slice_compiler"],"note":"T5 get_single_item 이 items[0] 을 통째로 싣는다. registry_component_ids 가 여기를 통과한다(C-1).","optional":false,"path":"evidence_indexed.json","path_is_pattern":false,"producer_task":"Task_B1_quality_gate_evidence_indexed","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_D0_domain_activation_gate","Task_C_BO_A0_context_and_domain_slice_compiler"],"optional":false,"path":"evidence_event_candidates.json","path_is_pattern":false,"producer_task":"Task_B2_quality_gate_event_candidates","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_B2_quality_gate_event_candidates","Task_B12_gate_audit_finalizer"],"optional":false,"path":"stage1_tmp/quality_gate/B1_precheck.json","path_is_pattern":false,"producer_task":"Task_B1_quality_gate_evidence_indexed","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_B2_quality_gate_event_candidates"],"optional":false,"path":"stage1_tmp/quality_gate/finalized_evidence_id_manifest.json","path_is_pattern":false,"producer_task":"Task_B1_quality_gate_evidence_indexed","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_B12_gate_audit_finalizer"],"optional":false,"path":"stage1_tmp/quality_gate/B2_precheck.json","path_is_pattern":false,"producer_task":"Task_B2_quality_gate_event_candidates","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_B12_gate_llm_adjudicator","Task_B12_gate_audit_finalizer"],"optional":false,"path":"stage1_tmp/quality_gate/B12_exception_pack.json","path_is_pattern":false,"producer_task":"Task_B2_quality_gate_event_candidates","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_B12_gate_audit_finalizer"],"optional":false,"path":"stage1_tmp/quality_gate/B12_adjudication_decisions.json","path_is_pattern":false,"producer_task":"Task_B12_gate_llm_adjudicator","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_D0_domain_activation_gate","Task_B2_SHA256_soft_gate_handoff_writer"],"optional":false,"path":"quality_gates/B1_evidence_indexed_gate.json","path_is_pattern":false,"producer_task":"Task_B12_gate_audit_finalizer","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_D0_domain_activation_gate","Task_B2_SHA256_soft_gate_handoff_writer"],"optional":false,"path":"quality_gates/B2_event_candidates_gate.json","path_is_pattern":false,"producer_task":"Task_B12_gate_audit_finalizer","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_B2_SHA256_soft_gate_handoff_writer","Task_C_BO_A0_context_and_domain_slice_compiler","Task_C_BO_S0_signal_bundle_writer","Task_C_LE_L0_structure_seed_reducer","Task_C_LE_L2_final_structure_index_writer"],"note":"D-3 확정: execution_eligible 이 유일한 결정 필드. active ⊆ eligible. D0 은 활성 판정을 activation_gate 모듈에 위임한다 — 필수 키는 그 모듈이 만든다.","optional":false,"path":"routing/domain_activation_manifest.json","path_is_pattern":false,"produced_by_module":"stage1_runtime/activation_gate.py","producer_task":"Task_D0_domain_activation_gate","required_keys":["active_domain_ids","conservation_gate","contract_guards","domain_entries","expected_runnable_domain_ids","fail_open_policy","monitor_domain_ids","registry_index_sha256","registry_version","required_calculation_domains","review_items","schema_version","screening_sha256","signal_id","status","supporting_domain_ids","unrouted_material"],"root_key":"domain_activation_manifest","schema_version":"stage1_domain_activation_manifest.v1","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_C_BO_A0_context_and_domain_slice_compiler"],"note":"루트 래핑 없이 쓴다. 소비자는 stage1_part1_soft_gate_handoff 래핑 유무 둘 다 받아야 한다. digest_guard 는 screening_sha256·activation_manifest_sha256·registry_index_sha256 셋을 담는다.","optional":false,"path":"quality_gates/stage1_part1_soft_gate_handoff.json","path_is_pattern":false,"producer_task":"Task_B2_SHA256_soft_gate_handoff_writer","required_keys":["digest_guard","handoff_status","review_items","schema_version","source_stage"],"schema_version":"stage1_part1_soft_gate_handoff.v1","stage":"Stage_1.Part_1"},{"consumer_tasks":["Task_C_BO_R0_seed_reducer_and_exception_planner","Task_C_BO_F0_final_bo_compiler_gate_writer"],"note":"v3 1104행이 정한 경로다. 옮기지 않는다.","optional":false,"path":"stage1_tmp/task_c_bo/stage_a_context.json","path_is_pattern":false,"produced_by_module":"stage1_runtime/stage_a_context_builder.py","producer_task":"Task_C_BO_A0_context_and_domain_slice_compiler","required_keys":["created_at_utc","event_candidate_map","evidence_authority_map","input_digests_sha256","meeting_clause_map","schema_version","status"],"root_key":"stage_a_context","schema_version":"task_c_bo_stage_a_context.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_BO_R0_seed_reducer_and_exception_planner","Task_C_BO_S0_signal_bundle_writer"],"note":"v1 세 목록은 소비자 계약이므로 유지한다. v2 가 더한 evidence_count·registry_index_sha256 은 덧붙이기다(P0 판정 C 형태).","optional":false,"path":"stage1_tmp/task_c_bo/source_universe_manifest.json","path_is_pattern":false,"produced_by_module":"stage1_runtime/stage_a_context_builder.py","producer_task":"Task_C_BO_A0_context_and_domain_slice_compiler","required_keys":["event_candidate_ids","evidence_index_set","meeting_clause_ids","schema_version"],"schema_version":"task_c_bo_source_universe_manifest.v2","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_B_domain_worker_*","Task_C_BO_R0_seed_reducer_and_exception_planner","Task_C_BO_S0_signal_bundle_writer"],"note":"task_instances[*] 는 domain_id 와 expected_output_path 를 갖는다.","optional":false,"path":"fanout/domain_fanout_plan.json","path_is_pattern":false,"producer_task":"Task_C_BO_A0_context_and_domain_slice_compiler","required_keys":["schema_version","task_instances"],"root_key":"domain_fanout_plan","schema_version":"domain_fanout_plan.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_BO_R0_seed_reducer_and_exception_planner","Task_C_BO_S0_signal_bundle_writer","Task_C_B_domain_worker_*","Task_C_LE_L0_structure_seed_reducer"],"note":"R-1/R-2 로 domain_declarations 를 실은다. R0 는 worker 산출 검증에, S0 는 emits_signals 대조에 읽는다.","optional":false,"path":"runtime/domain_slices/.json","path_is_pattern":true,"producer_task":"Task_C_BO_A0_context_and_domain_slice_compiler","root_key":"stage_b_domain_slice","schema_version":"task_c_bo_stage_b_domain_slice.v2","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_B_domain_worker_*"],"note":"worker 는 이 프롬프트를 다시 만들지 않는다. compiled_prompt_sha256 이 일치해야 한다.","optional":false,"path":"runtime/compiled_prompts/.md","path_is_pattern":true,"producer_task":"Task_C_BO_A0_context_and_domain_slice_compiler","stage":"Stage_1.Part_2"},{"consumer_tasks":[],"note":"원인 분리 영수증. 동결기가 slice_sha256_by_domain 을 읽는다.","optional":false,"path":"validation_assets/routing/stage_receipt.json","path_is_pattern":false,"producer_task":"Task_C_BO_A0_context_and_domain_slice_compiler","required_keys":["activation_source","created_by","loader_mode","schema_injection","schema_version","slice_sha256_by_domain","stage","worker_mode"],"schema_version":"stage1_stage_receipt.v2","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_BO_R0_seed_reducer_and_exception_planner","Task_C_BO_S0_signal_bundle_writer"],"optional":false,"path":"runtime/domain_seed_outputs/.json","path_is_pattern":true,"producer_task":"Task_C_B_domain_worker_*","root_key":"stage_b_domain_bo_seed_output","schema_version":"task_c_bo_stage_b_domain_bo_seed.v3","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_BO_F0_final_bo_compiler_gate_writer"],"optional":false,"path":"stage1_tmp/task_c_bo/postb_seed_ledger.json","path_is_pattern":false,"producer_task":"Task_C_BO_R0_seed_reducer_and_exception_planner","root_key":"postb_seed_ledger","schema_version":"task_c_bo_postb_seed_ledger.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_BO_R1_exception_adjudicator","Task_C_BO_F0_final_bo_compiler_gate_writer"],"note":"exception_count 가 R1 산출의 필수 여부를 정한다.","optional":false,"path":"quality_gates/stage1_part2_exception_pack.json","path_is_pattern":false,"producer_task":"Task_C_BO_R0_seed_reducer_and_exception_planner","stage":"Stage_1.Part_2"},{"also_written_by":["Task_C_BO_F0_final_bo_compiler_gate_writer"],"consumer_tasks":["Task_C_BO_F0_final_bo_compiler_gate_writer"],"note":"R0 가 만들고 F0 이 FINALIZED 로 갱신해 다시 쓴다. 공동 기록자다.","optional":false,"path":"quality_gates/stage1_part2_review_handoff.json","path_is_pattern":false,"producer_task":"Task_C_BO_R0_seed_reducer_and_exception_planner","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_BO_F0_final_bo_compiler_gate_writer"],"note":"조건부. exception_pack 의 exception_count 가 0 보다 크면 필수이고 부재는 실패다. 0 이면 부재를 허용하되 R1_SKIPPED 로 남긴다.","optional":true,"path":"stage1_tmp/task_c_bo/postb_adjudication_decisions.json","path_is_pattern":false,"producer_task":"Task_C_BO_R1_exception_adjudicator","root_key":"postb_exception_adjudication","schema_version":"task_c_bo_postb_exception_adjudication.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_BO_S0_signal_bundle_writer","Stage_1.Part_4","Stage_2","Task_C_LE_L0_structure_seed_reducer","Task_C_LE_L2_final_structure_index_writer"],"note":"최대 호환면. 레코드 구조를 바꾸지 않는다.","optional":false,"path":"BO.json","path_is_pattern":false,"producer_task":"Task_C_BO_F0_final_bo_compiler_gate_writer","stage":"Stage_1.Part_2"},{"consumer_tasks":[],"note":" Part 3 개정(신전략서)은 읽지 않는다 — 소비자는 Part 4 착수 시 재평가.","optional":false,"path":"stage1_tmp/task_c_bo/postb_compiled_bundle_compact.json","path_is_pattern":false,"producer_task":"Task_C_BO_F0_final_bo_compiler_gate_writer","schema_version":"task_c_bo_postb_compiled_bundle_compact.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Stage_1.Part_4","Stage_2","Task_C_LE_L0_structure_seed_reducer"],"note":"transaction_id 는 ^S5TX-[a-f0-9]{20}$. downstream_read_sets 가 하류 정본 집합을 선언한다.","optional":false,"path":"signals/signal_manifest.json","path_is_pattern":false,"producer_task":"Task_C_BO_S0_signal_bundle_writer","schema_version":"signal_manifest.v2","stage":"Stage_1.Part_2"},{"consumer_tasks":["Stage_1.Part_4"],"note":"구 이름 호환면. signals/compatibility_views/ 의 같은 바이트다. Part 3 는 더 이상 읽지 않는다 — 정본 집합(downstream_read_sets.part3_L0)으로 이관 완료(C-7).","optional":false,"path":"actio_case_signals.json","path_is_pattern":false,"producer_task":"Task_C_BO_S0_signal_bundle_writer","schema_version":"actio_case_signals.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Stage_1.Part_4"],"note":" Part 3 는 더 이상 읽지 않는다 — 정본 집합(downstream_read_sets.part3_L0)으로 이관 완료(C-7).","optional":false,"path":"case_liability_signals.json","path_is_pattern":false,"producer_task":"Task_C_BO_S0_signal_bundle_writer","schema_version":"case_liability_signals.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Stage_1.Part_4"],"note":" Part 3 는 더 이상 읽지 않는다 — 정본 집합(downstream_read_sets.part3_L0)으로 이관 완료(C-7).","optional":false,"path":"legal_effect_signals.json","path_is_pattern":false,"producer_task":"Task_C_BO_S0_signal_bundle_writer","schema_version":"legal_effect_signals.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_LE_L0_structure_seed_reducer","Task_C_LE_L2_final_structure_index_writer"],"note":"SG-13 — Part 3 효과 경로 원천. L2 는 근거 보존 등식의 바깥 수를 여기서 직접 센다.","optional":false,"path":"signals/legal_effect_routes.json","path_is_pattern":false,"produced_by_module":"signals/compiler/common.py","producer_task":"Task_C_BO_S0_signal_bundle_writer","required_keys":["schema_version","signal_id","status","records"],"schema_version":"stage1_signal_sg13.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_LE_L0_structure_seed_reducer"],"note":"SG-05 — 근거 refs 원자.","optional":false,"path":"signals/legal_relation_lifecycle_signals.json","path_is_pattern":false,"produced_by_module":"signals/compiler/common.py","producer_task":"Task_C_BO_S0_signal_bundle_writer","required_keys":["schema_version","signal_id","status","records"],"schema_version":"stage1_signal_sg05.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_LE_L0_structure_seed_reducer"],"note":"SG-11 — 조인 키는 레코드 signal_id(worker request_id 승계).","optional":false,"path":"signals/calculation_requirements.json","path_is_pattern":false,"produced_by_module":"signals/compiler/common.py","producer_task":"Task_C_BO_S0_signal_bundle_writer","required_keys":["schema_version","signal_id","status","records"],"schema_version":"stage1_signal_sg11.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_LE_L0_structure_seed_reducer"],"note":"SG-02 — 레코드 0 이 정상(생산자 없음). L0 는 조인 없이 형식화 기록만 한다.","optional":true,"path":"signals/procedural_posture_relief_signals.json","path_is_pattern":false,"produced_by_module":"signals/compiler/common.py","producer_task":"Task_C_BO_S0_signal_bundle_writer","required_keys":["schema_version","signal_id","status","records"],"schema_version":"stage1_signal_sg02.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_LE_L0_structure_seed_reducer"],"note":"SG-07 — 레코드 0 이 정상. SG-02 와 같은 처리.","optional":true,"path":"signals/asset_right_state_signals.json","path_is_pattern":false,"produced_by_module":"signals/compiler/common.py","producer_task":"Task_C_BO_S0_signal_bundle_writer","required_keys":["schema_version","signal_id","status","records"],"schema_version":"stage1_signal_sg07.v1","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_LE_L0_structure_seed_reducer"],"note":"expected_runnable 도메인 중 실재하는 것만 읽는다 — 부재는 영수증 계수.","optional":true,"path":"signals/domain_signals/.json","path_is_pattern":true,"produced_by_module":"signals/compiler/common.py","producer_task":"Task_C_BO_S0_signal_bundle_writer","root_key":"domain_signal_envelope","schema_version":"stage1_domain_signals.v2","stage":"Stage_1.Part_2"},{"consumer_tasks":["Task_C_LE_L2_final_structure_index_writer"],"note":"구조 seed — Θ·전순서·부착. 닫힌 스키마 platform/schemas/structure_seed_bundle.schema.json.","optional":false,"path":"stage1_tmp/task_le/structure_seed_bundle.json","path_is_pattern":false,"produced_by_module":"stage1_runtime/structure_index_compiler.py","producer_task":"Task_C_LE_L0_structure_seed_reducer","root_key":"structure_seed_bundle","schema_version":"stage1_structure_seed_bundle.v1","stage":"Stage_1.Part_3"},{"also_written_by":["Task_C_LE_L2_final_structure_index_writer"],"consumer_tasks":["Task_C_LE_L2_final_structure_index_writer"],"note":"검토 채널 — 예외 pack 은 두지 않는다(L1 부재). L2 가 FINALIZED 로 갱신.","optional":false,"path":"quality_gates/stage1_part3_review_handoff.json","path_is_pattern":false,"producer_task":"Task_C_LE_L0_structure_seed_reducer","root_key":"stage1_part3_review_handoff","schema_version":"stage1_part3_review_handoff.v1","stage":"Stage_1.Part_3"},{"consumer_tasks":[],"note":"Part 4 F0 · Stage 2 가 읽는다. task 이름은 Part 4 착수 시 기입.","optional":false,"path":"legal_effect_structures.json","path_is_pattern":false,"producer_task":"Task_C_LE_L2_final_structure_index_writer","required_keys":["schema_version","status","structure_records","structure_index","quality_gate"],"schema_version":"stage1_legal_effect_structures.v1","stage":"Stage_1.Part_3"},{"consumer_tasks":[],"note":"검증 전용. 소비 task 없음.","optional":false,"path":"validation_assets/routing/part3_receipt.json","path_is_pattern":false,"producer_task":"Task_C_LE_L0_structure_seed_reducer","schema_version":"stage1_part3_receipt.v1","stage":"Stage_1.Part_3"},{"consumer_tasks":[],"note":"T1 최종 산출. Part 3 이후 스테이지가 읽는다 — task 이름은 그 착수 시 기입(C-8 등재).","optional":false,"path":"client_goal.json","path_is_pattern":false,"producer_task":"Task_A_client_goal","stage":"Stage_1.Part_1"}],"purpose":"Stage 1 각 Part 의 인계 파일을 한 곳에 선언한다. 생산자·소비자·루트 키·필수 키가 코드에 흩어져 있으면 어긋나도 아무도 모른다.","schema_version":"stage1_part_interface.v1","status":"ACTIVE"} +{ + "authored_not_derived": "이 표는 계약이며 코드에서 뽑지 않았다. 코드에서 뽑으면 검사가 자기 자신을 확인하는 셈이 된다. 검사기(_check_stage1_interface.py)가 코드를 이 표와 대조한다.", + "checker": "validation_assets/routing/_check_stage1_interface.py", + "conventions": { + "also_written_by": "선언된 생산자 외에 갱신을 허용하는 task. 생산자 위반으로 세지 않는다.", + "asset_paths_excluded": "Default_Agent/ 아래 정적 자산은 이 표의 대상이 아니다. 검사 10 이 따로 본다.", + "optional": "부재가 곧 실패는 아니지만 조건이 붙는다. note 를 읽는다.", + "path_is_pattern": " 자리는 런타임 값이다. 검사기는 접두·접미로 대조한다.", + "produced_by_module": "생산자 task 가 키 구성을 런타임 모듈에 위임할 때 그 모듈 경로. 필수 키 검사는 그 원문까지 함께 본다." + }, + "entry_inputs": [ + { + "consumer_tasks": [ + "Task_A0_domain_screener_01", + "Task_A0_domain_screener_02", + "Task_A_client_goal", + "Task_B1_map_doc_*", + "Task_B2_map_events_*", + "Task_C_BO_A0_context_and_domain_slice_compiler" + ], + "note": "사건 입력. 어느 task 도 쓰지 않는다. Part 2 A0 가 read_raw 로 직접 읽는다(C-8 반영).", + "optional": false, + "path": "client_meeting.md", + "path_is_pattern": false, + "producer_task": null, + "stage": "INPUT" + }, + { + "consumer_tasks": [ + "Task_Evidence_shard_planner" + ], + "note": "사건 입력. 어느 task 도 쓰지 않는다.", + "optional": false, + "path": "evidence_all.json", + "path_is_pattern": false, + "producer_task": null, + "required_keys": [ + "documents", + "source_files", + "total_count" + ], + "stage": "INPUT" + } + ], + "handoff_count": 55, + "handoffs": [ + { + "consumer_tasks": [ + "Task_A0_domain_screener_01", + "Task_A0_domain_screener_02", + "Task_A_client_goal", + "Task_B1_map_doc_*", + "Task_B2_map_events_*", + "Task_C_BO_A0_context_and_domain_slice_compiler" + ], + "note": "사건 입력. 어느 task 도 쓰지 않는다. Part 2 A0 가 read_raw 로 직접 읽는다(C-8 반영).", + "optional": false, + "path": "client_meeting.md", + "path_is_pattern": false, + "producer_task": null, + "stage": "INPUT" + }, + { + "consumer_tasks": [ + "Task_Evidence_shard_planner" + ], + "note": "사건 입력. 어느 task 도 쓰지 않는다.", + "optional": false, + "path": "evidence_all.json", + "path_is_pattern": false, + "producer_task": null, + "required_keys": [ + "documents", + "source_files", + "total_count" + ], + "stage": "INPUT" + }, + { + "consumer_tasks": [ + "Task_A0_domain_screener_02" + ], + "note": "LLM 초안. 비판단 6종(schema_version·registry_version·registry_index_sha256·source_document_refs·screening_policy·contract_guards)을 담지 않는다. candidates 원소 키 구성은 닫힌 스키마의 후보 객체와 같아야 한다.", + "optional": false, + "path": "routing/domain_screening_draft.json", + "path_is_pattern": false, + "producer_task": "Task_A0_domain_screener_01", + "required_keys": [ + "candidates", + "review_items", + "status", + "unmatched_issue_refs" + ], + "root_key": "domain_screening_draft", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_A0_domain_screener_03", + "Task_A_client_goal", + "Task_D0_domain_activation_gate", + "Task_B2_SHA256_soft_gate_handoff_writer", + "Task_C_BO_A0_context_and_domain_slice_compiler" + ], + "note": "screening_sha256 의 원문. 재직렬화 금지. Part 2 A0 는 봉인 원문 바이트와 별도로 candidates[].requested_calculation_domains 를 파싱해 슬라이스로 통과시킨다(R-2).", + "optional": false, + "path": "routing/domain_screening.json", + "path_is_pattern": false, + "producer_task": "Task_A0_domain_screener_02", + "required_keys": [ + "candidates", + "contract_guards", + "registry_index_sha256", + "registry_version", + "review_items", + "schema_version", + "screening_policy", + "source_document_refs", + "status", + "unmatched_issue_refs" + ], + "schema_version": "stage1_domain_screening.v1", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_A_client_goal" + ], + "optional": false, + "path": "routing/candidate_profile_vocabulary.md", + "path_is_pattern": false, + "producer_task": "Task_A0_domain_screener_03", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_B1_map_doc_*", + "Task_B1_quality_gate_evidence_indexed", + "Task_B2_quality_gate_event_candidates" + ], + "optional": false, + "path": "evidence_shard_plan.json", + "path_is_pattern": false, + "producer_task": "Task_Evidence_shard_planner", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_D0_domain_activation_gate", + "Task_C_BO_A0_context_and_domain_slice_compiler", + "Task_C_FL_F0_fact_ledger_reducer" + ], + "note": "T5 get_single_item 이 items[0] 을 통째로 싣는다. registry_component_ids 가 여기를 통과한다(C-1).", + "optional": false, + "path": "evidence_indexed.json", + "path_is_pattern": false, + "producer_task": "Task_B1_quality_gate_evidence_indexed", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_D0_domain_activation_gate", + "Task_C_BO_A0_context_and_domain_slice_compiler" + ], + "optional": false, + "path": "evidence_event_candidates.json", + "path_is_pattern": false, + "producer_task": "Task_B2_quality_gate_event_candidates", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_B2_quality_gate_event_candidates", + "Task_B12_gate_audit_finalizer" + ], + "optional": false, + "path": "stage1_tmp/quality_gate/B1_precheck.json", + "path_is_pattern": false, + "producer_task": "Task_B1_quality_gate_evidence_indexed", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_B2_quality_gate_event_candidates" + ], + "optional": false, + "path": "stage1_tmp/quality_gate/finalized_evidence_id_manifest.json", + "path_is_pattern": false, + "producer_task": "Task_B1_quality_gate_evidence_indexed", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_B12_gate_audit_finalizer" + ], + "optional": false, + "path": "stage1_tmp/quality_gate/B2_precheck.json", + "path_is_pattern": false, + "producer_task": "Task_B2_quality_gate_event_candidates", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_B12_gate_llm_adjudicator", + "Task_B12_gate_audit_finalizer" + ], + "optional": false, + "path": "stage1_tmp/quality_gate/B12_exception_pack.json", + "path_is_pattern": false, + "producer_task": "Task_B2_quality_gate_event_candidates", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_B12_gate_audit_finalizer" + ], + "optional": false, + "path": "stage1_tmp/quality_gate/B12_adjudication_decisions.json", + "path_is_pattern": false, + "producer_task": "Task_B12_gate_llm_adjudicator", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_D0_domain_activation_gate", + "Task_B2_SHA256_soft_gate_handoff_writer" + ], + "optional": false, + "path": "quality_gates/B1_evidence_indexed_gate.json", + "path_is_pattern": false, + "producer_task": "Task_B12_gate_audit_finalizer", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_D0_domain_activation_gate", + "Task_B2_SHA256_soft_gate_handoff_writer" + ], + "optional": false, + "path": "quality_gates/B2_event_candidates_gate.json", + "path_is_pattern": false, + "producer_task": "Task_B12_gate_audit_finalizer", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_B2_SHA256_soft_gate_handoff_writer", + "Task_C_BO_A0_context_and_domain_slice_compiler", + "Task_C_BO_S0_signal_bundle_writer", + "Task_C_LE_L0_structure_seed_reducer", + "Task_C_LE_L2_final_structure_index_writer", + "Task_C_FL_F0_fact_ledger_reducer", + "Task_C_FL_F2_final_fact_ledger_gate_and_writer" + ], + "note": "D-3 확정: execution_eligible 이 유일한 결정 필드. active ⊆ eligible. D0 은 활성 판정을 activation_gate 모듈에 위임한다 — 필수 키는 그 모듈이 만든다.", + "optional": false, + "path": "routing/domain_activation_manifest.json", + "path_is_pattern": false, + "produced_by_module": "stage1_runtime/activation_gate.py", + "producer_task": "Task_D0_domain_activation_gate", + "required_keys": [ + "active_domain_ids", + "conservation_gate", + "contract_guards", + "domain_entries", + "expected_runnable_domain_ids", + "fail_open_policy", + "monitor_domain_ids", + "registry_index_sha256", + "registry_version", + "required_calculation_domains", + "review_items", + "schema_version", + "screening_sha256", + "signal_id", + "status", + "supporting_domain_ids", + "unrouted_material" + ], + "root_key": "domain_activation_manifest", + "schema_version": "stage1_domain_activation_manifest.v1", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_C_BO_A0_context_and_domain_slice_compiler" + ], + "note": "루트 래핑 없이 쓴다. 소비자는 stage1_part1_soft_gate_handoff 래핑 유무 둘 다 받아야 한다. digest_guard 는 screening_sha256·activation_manifest_sha256·registry_index_sha256 셋을 담는다.", + "optional": false, + "path": "quality_gates/stage1_part1_soft_gate_handoff.json", + "path_is_pattern": false, + "producer_task": "Task_B2_SHA256_soft_gate_handoff_writer", + "required_keys": [ + "digest_guard", + "handoff_status", + "review_items", + "schema_version", + "source_stage" + ], + "schema_version": "stage1_part1_soft_gate_handoff.v1", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_C_BO_R0_seed_reducer_and_exception_planner", + "Task_C_BO_F0_final_bo_compiler_gate_writer" + ], + "note": "v3 1104행이 정한 경로다. 옮기지 않는다.", + "optional": false, + "path": "stage1_tmp/task_c_bo/stage_a_context.json", + "path_is_pattern": false, + "produced_by_module": "stage1_runtime/stage_a_context_builder.py", + "producer_task": "Task_C_BO_A0_context_and_domain_slice_compiler", + "required_keys": [ + "created_at_utc", + "event_candidate_map", + "evidence_authority_map", + "input_digests_sha256", + "meeting_clause_map", + "schema_version", + "status" + ], + "root_key": "stage_a_context", + "schema_version": "task_c_bo_stage_a_context.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_BO_R0_seed_reducer_and_exception_planner", + "Task_C_BO_S0_signal_bundle_writer" + ], + "note": "v1 세 목록은 소비자 계약이므로 유지한다. v2 가 더한 evidence_count·registry_index_sha256 은 덧붙이기다(P0 판정 C 형태).", + "optional": false, + "path": "stage1_tmp/task_c_bo/source_universe_manifest.json", + "path_is_pattern": false, + "produced_by_module": "stage1_runtime/stage_a_context_builder.py", + "producer_task": "Task_C_BO_A0_context_and_domain_slice_compiler", + "required_keys": [ + "event_candidate_ids", + "evidence_index_set", + "meeting_clause_ids", + "schema_version" + ], + "schema_version": "task_c_bo_source_universe_manifest.v2", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_B_domain_worker_*", + "Task_C_BO_R0_seed_reducer_and_exception_planner", + "Task_C_BO_S0_signal_bundle_writer" + ], + "note": "task_instances[*] 는 domain_id 와 expected_output_path 를 갖는다.", + "optional": false, + "path": "fanout/domain_fanout_plan.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_A0_context_and_domain_slice_compiler", + "required_keys": [ + "schema_version", + "task_instances" + ], + "root_key": "domain_fanout_plan", + "schema_version": "domain_fanout_plan.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_BO_R0_seed_reducer_and_exception_planner", + "Task_C_BO_S0_signal_bundle_writer", + "Task_C_B_domain_worker_*", + "Task_C_LE_L0_structure_seed_reducer" + ], + "note": "R-1/R-2 로 domain_declarations 를 실은다. R0 는 worker 산출 검증에, S0 는 emits_signals 대조에 읽는다.", + "optional": false, + "path": "runtime/domain_slices/.json", + "path_is_pattern": true, + "producer_task": "Task_C_BO_A0_context_and_domain_slice_compiler", + "root_key": "stage_b_domain_slice", + "schema_version": "task_c_bo_stage_b_domain_slice.v2", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_B_domain_worker_*" + ], + "note": "worker 는 이 프롬프트를 다시 만들지 않는다. compiled_prompt_sha256 이 일치해야 한다.", + "optional": false, + "path": "runtime/compiled_prompts/.md", + "path_is_pattern": true, + "producer_task": "Task_C_BO_A0_context_and_domain_slice_compiler", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [], + "note": "원인 분리 영수증. 동결기가 slice_sha256_by_domain 을 읽는다.", + "optional": false, + "path": "validation_assets/routing/stage_receipt.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_A0_context_and_domain_slice_compiler", + "required_keys": [ + "activation_source", + "created_by", + "loader_mode", + "schema_injection", + "schema_version", + "slice_sha256_by_domain", + "stage", + "worker_mode" + ], + "schema_version": "stage1_stage_receipt.v2", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_BO_R0_seed_reducer_and_exception_planner", + "Task_C_BO_S0_signal_bundle_writer" + ], + "optional": false, + "path": "runtime/domain_seed_outputs/.json", + "path_is_pattern": true, + "producer_task": "Task_C_B_domain_worker_*", + "root_key": "stage_b_domain_bo_seed_output", + "schema_version": "task_c_bo_stage_b_domain_bo_seed.v3", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_BO_F0_final_bo_compiler_gate_writer" + ], + "optional": false, + "path": "stage1_tmp/task_c_bo/postb_seed_ledger.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_R0_seed_reducer_and_exception_planner", + "root_key": "postb_seed_ledger", + "schema_version": "task_c_bo_postb_seed_ledger.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_BO_R1_exception_adjudicator", + "Task_C_BO_F0_final_bo_compiler_gate_writer" + ], + "note": "exception_count 가 R1 산출의 필수 여부를 정한다.", + "optional": false, + "path": "quality_gates/stage1_part2_exception_pack.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_R0_seed_reducer_and_exception_planner", + "stage": "Stage_1.Part_2" + }, + { + "also_written_by": [ + "Task_C_BO_F0_final_bo_compiler_gate_writer" + ], + "consumer_tasks": [ + "Task_C_BO_F0_final_bo_compiler_gate_writer", + "Task_C_FL_F0_fact_ledger_reducer" + ], + "note": "R0 가 만들고 F0 이 FINALIZED 로 갱신해 다시 쓴다. 공동 기록자다.", + "optional": false, + "path": "quality_gates/stage1_part2_review_handoff.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_R0_seed_reducer_and_exception_planner", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_BO_F0_final_bo_compiler_gate_writer" + ], + "note": "조건부. exception_pack 의 exception_count 가 0 보다 크면 필수이고 부재는 실패다. 0 이면 부재를 허용하되 R1_SKIPPED 로 남긴다.", + "optional": true, + "path": "stage1_tmp/task_c_bo/postb_adjudication_decisions.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_R1_exception_adjudicator", + "root_key": "postb_exception_adjudication", + "schema_version": "task_c_bo_postb_exception_adjudication.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_BO_S0_signal_bundle_writer", + "Stage_2", + "Task_C_LE_L0_structure_seed_reducer", + "Task_C_LE_L2_final_structure_index_writer", + "Task_C_FL_F0_fact_ledger_reducer", + "Task_C_FL_F2_final_fact_ledger_gate_and_writer" + ], + "note": "최대 호환면. 레코드 구조를 바꾸지 않는다.", + "optional": false, + "path": "BO.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_F0_final_bo_compiler_gate_writer", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [], + "note": " Part 3 개정(신전략서)은 읽지 않는다 — 소비자는 Part 4 착수 시 재평가.", + "optional": false, + "path": "stage1_tmp/task_c_bo/postb_compiled_bundle_compact.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_F0_final_bo_compiler_gate_writer", + "schema_version": "task_c_bo_postb_compiled_bundle_compact.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Stage_2", + "Task_C_LE_L0_structure_seed_reducer", + "Task_C_FL_F0_fact_ledger_reducer" + ], + "note": "transaction_id 는 ^S5TX-[a-f0-9]{20}$. downstream_read_sets 가 하류 정본 집합을 선언한다.", + "optional": false, + "path": "signals/signal_manifest.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "schema_version": "signal_manifest.v2", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [], + "note": "구 이름 호환면. signals/compatibility_views/ 의 같은 바이트다. Part 3 는 더 이상 읽지 않는다 — 정본 집합(downstream_read_sets.part3_L0)으로 이관 완료(C-7).", + "optional": false, + "path": "actio_case_signals.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "schema_version": "actio_case_signals.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [], + "note": " Part 3 는 더 이상 읽지 않는다 — 정본 집합(downstream_read_sets.part3_L0)으로 이관 완료(C-7).", + "optional": false, + "path": "case_liability_signals.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "schema_version": "case_liability_signals.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [], + "note": " Part 3 는 더 이상 읽지 않는다 — 정본 집합(downstream_read_sets.part3_L0)으로 이관 완료(C-7).", + "optional": false, + "path": "legal_effect_signals.json", + "path_is_pattern": false, + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "schema_version": "legal_effect_signals.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_LE_L0_structure_seed_reducer", + "Task_C_LE_L2_final_structure_index_writer" + ], + "note": "SG-13 — Part 3 효과 경로 원천. L2 는 근거 보존 등식의 바깥 수를 여기서 직접 센다.", + "optional": false, + "path": "signals/legal_effect_routes.json", + "path_is_pattern": false, + "produced_by_module": "signals/compiler/common.py", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "required_keys": [ + "schema_version", + "signal_id", + "status", + "records" + ], + "schema_version": "stage1_signal_sg13.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_LE_L0_structure_seed_reducer" + ], + "note": "SG-05 — 근거 refs 원자.", + "optional": false, + "path": "signals/legal_relation_lifecycle_signals.json", + "path_is_pattern": false, + "produced_by_module": "signals/compiler/common.py", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "required_keys": [ + "schema_version", + "signal_id", + "status", + "records" + ], + "schema_version": "stage1_signal_sg05.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_LE_L0_structure_seed_reducer", + "Task_C_FL_F0_fact_ledger_reducer", + "Task_C_FL_F2_final_fact_ledger_gate_and_writer" + ], + "note": "SG-11 — 조인 키는 레코드 signal_id(worker request_id 승계).", + "optional": false, + "path": "signals/calculation_requirements.json", + "path_is_pattern": false, + "produced_by_module": "signals/compiler/common.py", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "required_keys": [ + "schema_version", + "signal_id", + "status", + "records" + ], + "schema_version": "stage1_signal_sg11.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_LE_L0_structure_seed_reducer" + ], + "note": "SG-02 — 레코드 0 이 정상(생산자 없음). L0 는 조인 없이 형식화 기록만 한다.", + "optional": true, + "path": "signals/procedural_posture_relief_signals.json", + "path_is_pattern": false, + "produced_by_module": "signals/compiler/common.py", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "required_keys": [ + "schema_version", + "signal_id", + "status", + "records" + ], + "schema_version": "stage1_signal_sg02.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_LE_L0_structure_seed_reducer", + "Task_C_FL_F0_fact_ledger_reducer" + ], + "note": "SG-07 — 레코드 0 이 정상. SG-02 와 같은 처리.", + "optional": true, + "path": "signals/asset_right_state_signals.json", + "path_is_pattern": false, + "produced_by_module": "signals/compiler/common.py", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "required_keys": [ + "schema_version", + "signal_id", + "status", + "records" + ], + "schema_version": "stage1_signal_sg07.v1", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_LE_L0_structure_seed_reducer", + "Task_C_FL_F0_fact_ledger_reducer" + ], + "note": "expected_runnable 도메인 중 실재하는 것만 읽는다 — 부재는 영수증 계수.", + "optional": true, + "path": "signals/domain_signals/.json", + "path_is_pattern": true, + "produced_by_module": "signals/compiler/common.py", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "root_key": "domain_signal_envelope", + "schema_version": "stage1_domain_signals.v2", + "stage": "Stage_1.Part_2" + }, + { + "consumer_tasks": [ + "Task_C_LE_L2_final_structure_index_writer" + ], + "note": "구조 seed — Θ·전순서·부착. 닫힌 스키마 platform/schemas/structure_seed_bundle.schema.json.", + "optional": false, + "path": "stage1_tmp/task_le/structure_seed_bundle.json", + "path_is_pattern": false, + "produced_by_module": "stage1_runtime/structure_index_compiler.py", + "producer_task": "Task_C_LE_L0_structure_seed_reducer", + "root_key": "structure_seed_bundle", + "schema_version": "stage1_structure_seed_bundle.v1", + "stage": "Stage_1.Part_3" + }, + { + "also_written_by": [ + "Task_C_LE_L2_final_structure_index_writer" + ], + "consumer_tasks": [ + "Task_C_LE_L2_final_structure_index_writer" + ], + "note": "검토 채널 — 예외 pack 은 두지 않는다(L1 부재). L2 가 FINALIZED 로 갱신.", + "optional": false, + "path": "quality_gates/stage1_part3_review_handoff.json", + "path_is_pattern": false, + "producer_task": "Task_C_LE_L0_structure_seed_reducer", + "root_key": "stage1_part3_review_handoff", + "schema_version": "stage1_part3_review_handoff.v1", + "stage": "Stage_1.Part_3" + }, + { + "consumer_tasks": [ + "Task_C_FL_F0_fact_ledger_reducer", + "Task_C_FL_F2_final_fact_ledger_gate_and_writer", + "Stage_2" + ], + "note": "Part 4 F0·F2 · Stage 2 가 읽는다. F0 는 signal_manifest_transaction_id 승계를 두 갈래(불일치 경성/부재 검토)로 대조한다.", + "optional": false, + "path": "legal_effect_structures.json", + "path_is_pattern": false, + "producer_task": "Task_C_LE_L2_final_structure_index_writer", + "required_keys": [ + "schema_version", + "status", + "structure_records", + "structure_index", + "quality_gate", + "signal_manifest_transaction_id" + ], + "schema_version": "stage1_legal_effect_structures.v1", + "stage": "Stage_1.Part_3" + }, + { + "consumer_tasks": [], + "note": "검증 전용. 소비 task 없음.", + "optional": false, + "path": "validation_assets/routing/part3_receipt.json", + "path_is_pattern": false, + "producer_task": "Task_C_LE_L0_structure_seed_reducer", + "schema_version": "stage1_part3_receipt.v1", + "stage": "Stage_1.Part_3" + }, + { + "consumer_tasks": [], + "note": "T1 최종 산출. Part 3 이후 스테이지가 읽는다 — task 이름은 그 착수 시 기입(C-8 등재).", + "optional": false, + "path": "client_goal.json", + "path_is_pattern": false, + "producer_task": "Task_A_client_goal", + "stage": "Stage_1.Part_1" + }, + { + "consumer_tasks": [ + "Task_C_FL_F0_fact_ledger_reducer" + ], + "note": "SG-04 — calculation_requests[].law_version_refs 참조만. 버전 확정 판단 금지.", + "optional": false, + "path": "signals/governing_law_version_signals.json", + "path_is_pattern": false, + "produced_by_module": "signals/compiler/common.py", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "required_keys": [ + "schema_version", + "signal_id", + "status", + "records" + ], + "root_key": null, + "schema_version": "stage1_signal_sg04.v1" + }, + { + "consumer_tasks": [ + "Task_C_FL_F0_fact_ledger_reducer" + ], + "note": "SG-06 — 통지 lifecycle 보조 참조. 게이트 원천 아님.", + "optional": false, + "path": "signals/timeline_notice_condition_signals.json", + "path_is_pattern": false, + "produced_by_module": "signals/compiler/common.py", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "required_keys": [ + "schema_version", + "signal_id", + "status", + "records" + ], + "root_key": null, + "schema_version": "stage1_signal_sg06.v1" + }, + { + "consumer_tasks": [ + "Task_C_FL_F0_fact_ledger_reducer" + ], + "note": "SG-10 — 증거력 산정 근거 참조. 값 자체는 정하지 않음.", + "optional": false, + "path": "signals/evidence_proof_conflict_signals.json", + "path_is_pattern": false, + "produced_by_module": "signals/compiler/common.py", + "producer_task": "Task_C_BO_S0_signal_bundle_writer", + "required_keys": [ + "schema_version", + "signal_id", + "status", + "records" + ], + "root_key": null, + "schema_version": "stage1_signal_sg10.v1" + }, + { + "consumer_tasks": [ + "Task_C_FL_F1_fact_exception_adjudicator", + "Task_C_FL_F2_final_fact_ledger_gate_and_writer" + ], + "note": "F0 -> F1·F2 예외 pack. 판정 부류는 정책 f1_exception_types 선언.", + "optional": false, + "path": "stage1_tmp/fact_ledger/fact_exception_pack.json", + "path_is_pattern": false, + "produced_by_module": "stage1_runtime/fact_ledger_compiler.py", + "producer_task": "Task_C_FL_F0_fact_ledger_reducer", + "required_keys": [ + "schema_version", + "has_exceptions", + "exception_count", + "exceptions" + ], + "root_key": "fact_exception_pack", + "schema_version": "stage1_fact_exception_pack.v1" + }, + { + "consumer_tasks": [ + "Task_C_FL_F2_final_fact_ledger_gate_and_writer" + ], + "note": "F0 -> F2 후보 bundle. 행 29키 = 구 27 + domain_effects·calculation_requests.", + "optional": false, + "path": "stage1_tmp/fact_ledger/fact_ledger_candidate_bundle.json", + "path_is_pattern": false, + "produced_by_module": "stage1_runtime/fact_ledger_compiler.py", + "producer_task": "Task_C_FL_F0_fact_ledger_reducer", + "required_keys": [ + "schema_version", + "status", + "rows", + "gate_report" + ], + "root_key": "fact_ledger_candidate_bundle", + "schema_version": "stage1_fact_ledger_candidate_bundle.v1" + }, + { + "consumer_tasks": [ + "Task_C_FL_F2_final_fact_ledger_gate_and_writer" + ], + "note": "F1 -> F2 판정. exception_count > 0 인데 부재면 PART4_ADJUDICATION_MISSING.", + "optional": true, + "path": "stage1_tmp/fact_ledger/fact_adjudication.json", + "path_is_pattern": false, + "produced_by_module": null, + "producer_task": "Task_C_FL_F1_fact_exception_adjudicator", + "required_keys": [ + "schema_version", + "status", + "decisions", + "blocked_review_items" + ], + "root_key": "fact_exception_adjudication", + "schema_version": "stage1_fact_exception_adjudication.v1" + }, + { + "consumer_tasks": [ + "Stage_2" + ], + "note": "최종 사실원장 — 래핑 없는 배열. fact_id F-### 연속(F2 경성 단언).", + "optional": false, + "path": "Fact_Ledger_base.json", + "path_is_pattern": false, + "produced_by_module": "stage1_runtime/fact_ledger_compiler.py", + "producer_task": "Task_C_FL_F2_final_fact_ledger_gate_and_writer", + "required_keys": [], + "root_key": null, + "schema_version": "stage1_fact_ledger_base.v1" + }, + { + "consumer_tasks": [ + "Stage_2" + ], + "note": "F2 자기 실측 — 게이트별 발화·도메인 효과 커버리지·계산 준비도. receipt 는 F0 단독.", + "optional": false, + "path": "stage1_tmp/fact_ledger/fact_ledger_writer_report.json", + "path_is_pattern": false, + "produced_by_module": null, + "producer_task": "Task_C_FL_F2_final_fact_ledger_gate_and_writer", + "required_keys": [ + "schema_version", + "final_writer_status", + "row_count" + ], + "root_key": null, + "schema_version": "stage1_fact_ledger_writer_report.v1" + }, + { + "also_written_by": [ + "Task_C_FL_F2_final_fact_ledger_gate_and_writer" + ], + "consumer_tasks": [ + "Stage_2" + ], + "note": "F0 가 OPEN 으로 만들고 F2 가 FINALIZED 로 갱신한다. 공동 기록자다.", + "optional": false, + "path": "quality_gates/stage1_part4_review_handoff.json", + "path_is_pattern": false, + "produced_by_module": null, + "producer_task": "Task_C_FL_F0_fact_ledger_reducer", + "required_keys": [ + "schema_version", + "status", + "review_items" + ], + "root_key": "stage1_part4_review_handoff", + "schema_version": "stage1_part4_review_handoff.v1" + }, + { + "consumer_tasks": [], + "note": "F0 단독 기록 — 배포 게이트·봉인 셋·게이트 실행·보존 등식 실측값. 상수 PASS 금지.", + "optional": false, + "path": "validation_assets/routing/part4_receipt.json", + "path_is_pattern": false, + "produced_by_module": null, + "producer_task": "Task_C_FL_F0_fact_ledger_reducer", + "required_keys": [ + "schema_version", + "stage", + "deployment_gate", + "entry_seals" + ], + "root_key": null, + "schema_version": "stage1_part4_receipt.v1" + } + ], + "purpose": "Stage 1 각 Part 의 인계 파일을 한 곳에 선언한다. 생산자·소비자·루트 키·필수 키가 코드에 흩어져 있으면 어긋나도 아무도 모른다.", + "schema_version": "stage1_part_interface.v1", + "status": "ACTIVE" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/handoffs/stage1_stage_chain.v1.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/handoffs/stage1_stage_chain.v1.json index 57e0cd4f..1a5d3381 100644 --- a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/handoffs/stage1_stage_chain.v1.json +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/handoffs/stage1_stage_chain.v1.json @@ -1 +1,87 @@ -{"enforcement":{"declared":true,"note":"강제와 선언은 다르다. 이 파일이 선언을 맡는다.","runtime_guard":"Task_C_BO_A0 가 봉인 세 해시를 대조하므로 Part 1 미수행 시 SEAL_FAILED 로 멈춘다."},"interface_table":"handoffs/stage1_part_interface.v1.json","purpose":"Part 1~4 의 실행 순서와 인계 파일을 선언한다. 지금은 각 통합본의 prevs/nexts 가 비어 있어 순서가 A0 의 파일 부재 실패로 사실상 강제될 뿐 선언되어 있지 않다.","schema_version":"stage1_stage_chain.v1","stages":[{"order":1,"part":"Stage_1.Part_1","produces":["routing/domain_activation_manifest.json","quality_gates/stage1_part1_soft_gate_handoff.json","routing/domain_screening.json","evidence_indexed.json","evidence_event_candidates.json"],"requires":["client_meeting.md","evidence_all.json"],"yaml":"stage_1_part_1_v.8.yml"},{"order":2,"part":"Stage_1.Part_2","produces":["BO.json","signals/signal_manifest.json","actio_case_signals.json","case_liability_signals.json","legal_effect_signals.json","stage1_tmp/task_c_bo/postb_compiled_bundle_compact.json"],"requires":["routing/domain_activation_manifest.json","quality_gates/stage1_part1_soft_gate_handoff.json","routing/domain_screening.json","evidence_indexed.json","evidence_event_candidates.json"],"yaml":"stage_1_part_2_v.8.yml"},{"note":"정본 signal 집합(downstream_read_sets.part3_L0) 이관 완료. L1 없음 — 2 task 직렬.","order":3,"part":"Stage_1.Part_3","produces":["legal_effect_structures.json","stage1_tmp/task_le/structure_seed_bundle.json","quality_gates/stage1_part3_review_handoff.json","validation_assets/routing/part3_receipt.json"],"requires":["BO.json","signals/signal_manifest.json","routing/domain_activation_manifest.json"],"yaml":"stage_1_part_3_v.8.yml"},{"note":"같음.","order":4,"part":"Stage_1.Part_4","produces":[],"requires":["BO.json","signals/signal_manifest.json"],"yaml":"Stage_1_Part_4_Claude_v3.yml"}],"status":"ACTIVE"} +{ + "enforcement": { + "declared": true, + "note": "강제와 선언은 다르다. 이 파일이 선언을 맡는다.", + "runtime_guard": "Task_C_BO_A0 가 봉인 세 해시를 대조하므로 Part 1 미수행 시 SEAL_FAILED 로 멈춘다." + }, + "interface_table": "handoffs/stage1_part_interface.v1.json", + "purpose": "Part 1~4 의 실행 순서와 인계 파일을 선언한다. 지금은 각 통합본의 prevs/nexts 가 비어 있어 순서가 A0 의 파일 부재 실패로 사실상 강제될 뿐 선언되어 있지 않다.", + "schema_version": "stage1_stage_chain.v1", + "stages": [ + { + "order": 1, + "part": "Stage_1.Part_1", + "produces": [ + "routing/domain_activation_manifest.json", + "quality_gates/stage1_part1_soft_gate_handoff.json", + "routing/domain_screening.json", + "evidence_indexed.json", + "evidence_event_candidates.json" + ], + "requires": [ + "client_meeting.md", + "evidence_all.json" + ], + "yaml": "stage_1_part_1_v.8.yml" + }, + { + "order": 2, + "part": "Stage_1.Part_2", + "produces": [ + "BO.json", + "signals/signal_manifest.json", + "actio_case_signals.json", + "case_liability_signals.json", + "legal_effect_signals.json", + "stage1_tmp/task_c_bo/postb_compiled_bundle_compact.json" + ], + "requires": [ + "routing/domain_activation_manifest.json", + "quality_gates/stage1_part1_soft_gate_handoff.json", + "routing/domain_screening.json", + "evidence_indexed.json", + "evidence_event_candidates.json" + ], + "yaml": "stage_1_part_2_v.8.yml" + }, + { + "note": "정본 signal 집합(downstream_read_sets.part3_L0) 이관 완료. L1 없음 — 2 task 직렬.", + "order": 3, + "part": "Stage_1.Part_3", + "produces": [ + "legal_effect_structures.json", + "stage1_tmp/task_le/structure_seed_bundle.json", + "quality_gates/stage1_part3_review_handoff.json", + "validation_assets/routing/part3_receipt.json" + ], + "requires": [ + "BO.json", + "signals/signal_manifest.json", + "routing/domain_activation_manifest.json" + ], + "yaml": "stage_1_part_3_v.8.yml" + }, + { + "note": "같음.", + "order": 4, + "part": "Stage_1.Part_4", + "produces": [ + "Fact_Ledger_base.json", + "stage1_tmp/fact_ledger/fact_ledger_writer_report.json", + "quality_gates/stage1_part4_review_handoff.json", + "validation_assets/routing/part4_receipt.json" + ], + "requires": [ + "BO.json", + "signals/signal_manifest.json", + "legal_effect_structures.json", + "routing/domain_activation_manifest.json", + "evidence_indexed.json", + "signals/domain_signals/.json", + "quality_gates/stage1_part2_review_handoff.json" + ], + "yaml": "stage_1_part_4_v.8.yml" + } + ], + "status": "ACTIVE" +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/fact_exception_pack.schema.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/fact_exception_pack.schema.json new file mode 100644 index 00000000..00cc8012 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/fact_exception_pack.schema.json @@ -0,0 +1,93 @@ +{ + "$id": "https://schemas.liti-agent.local/stage1/part4/fact_exception_pack.schema.json", + "title": "stage1_fact_exception_pack.v1", + "description": "Part 4 F0 -> F1 예외 pack. 판정 부류는 정책 f1_exception_types 가 선언하며 unreachable_in_v8 부류가 실리면 PART4_UNREACHABLE_EXCEPTION_TYPE 경성 실패다.", + "type": "object", + "additionalProperties": false, + "required": [ + "fact_exception_pack" + ], + "properties": { + "fact_exception_pack": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "has_exceptions", + "exception_count", + "exceptions", + "budget" + ], + "properties": { + "schema_version": { + "const": "stage1_fact_exception_pack.v1" + }, + "has_exceptions": { + "type": "boolean" + }, + "exception_count": { + "type": "integer", + "minimum": 0 + }, + "exceptions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "exception_id", + "exception_type", + "fact_id", + "source_bo_id", + "conflict_flags", + "compact_payload" + ], + "properties": { + "exception_id": { + "type": "string", + "pattern": "^FX-[0-9]{3}$" + }, + "exception_type": { + "type": "string" + }, + "fact_id": { + "type": "string", + "pattern": "^F-[0-9]{3}$" + }, + "source_bo_id": { + "type": "string", + "minLength": 1 + }, + "conflict_flags": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + } + }, + "compact_payload": { + "type": "object" + }, + "oversize_review": { + "type": "boolean" + } + } + } + }, + "budget": { + "type": "object", + "additionalProperties": false, + "required": [ + "pack_item_char_limit" + ], + "properties": { + "pack_item_char_limit": { + "type": "integer", + "minimum": 1 + } + } + } + } + } + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/fact_ledger_base.schema.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/fact_ledger_base.schema.json new file mode 100644 index 00000000..9aff0851 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/fact_ledger_base.schema.json @@ -0,0 +1,406 @@ +{ + "$id": "https://schemas.liti-agent.local/stage1/part4/fact_ledger_base.schema.json", + "title": "stage1_fact_ledger_base.v1", + "description": "Part 4 최종 산출 — 래핑 없는 배열. 행 29키 = 구 27(순서·이름 불변) + domain_effects·calculation_requests(required 에 올리지 않는다 — Stage 2 호환 D-P4-4). 열거를 하드코딩하지 않는다 — 도메인 ID·검토 코드는 패턴만 강제한다.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "fact_id", + "source_bo_id", + "type", + "date", + "parties", + "object_spec", + "amount", + "action", + "evidence_refs", + "credibility", + "legal_centrality", + "proof_strength", + "legal_effect_roles", + "claim_chain_ref", + "linked_structures", + "must_not_drop_in_claim_types", + "must_consider", + "state_context", + "money_claim_effect", + "commercial_successor_effect", + "actio_roles", + "linked_actio_structures", + "downstream_module_candidates", + "skeleton_validation_codes", + "derived_fact", + "derivation_basis", + "legal_calculation_object" + ], + "properties": { + "fact_id": { + "type": "string", + "pattern": "^F-[0-9]{3}$" + }, + "source_bo_id": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": [ + "string", + "null" + ] + }, + "date": { + "type": [ + "string", + "null" + ] + }, + "parties": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "role", + "value", + "source" + ], + "properties": { + "role": { + "type": "string" + }, + "value": { + "type": [ + "string", + "null" + ] + }, + "source": { + "type": "string" + } + } + } + }, + "object_spec": { + "type": [ + "string", + "null" + ] + }, + "amount": { + "type": [ + "string", + "null" + ] + }, + "action": { + "type": [ + "string", + "null" + ] + }, + "evidence_refs": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "credibility": { + "enum": [ + "high", + "medium", + "low" + ] + }, + "legal_centrality": { + "enum": [ + "critical", + "high", + "medium", + "low" + ] + }, + "proof_strength": { + "enum": [ + "documentary", + "high", + "medium", + "meeting_only_core", + "low" + ] + }, + "legal_effect_roles": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "claim_chain_ref": { + "type": "object", + "additionalProperties": false, + "required": [ + "claim_group_ids", + "liability_group_ids", + "signal_count" + ], + "properties": { + "claim_group_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "liability_group_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "signal_count": { + "type": "integer", + "minimum": 0 + } + } + }, + "linked_structures": { + "type": "object", + "additionalProperties": false, + "required": [ + "structure_ids" + ], + "properties": { + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "must_not_drop_in_claim_types": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "must_consider": { + "type": "boolean" + }, + "state_context": { + "type": "object", + "additionalProperties": false, + "required": [ + "bo_type", + "domain_ids" + ], + "properties": { + "bo_type": { + "type": [ + "string", + "null" + ] + }, + "domain_ids": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + }, + "uniqueItems": true + } + } + }, + "money_claim_effect": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "structure_ids" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "commercial_successor_effect": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "structure_ids" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "actio_roles": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "linked_actio_structures": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "downstream_module_candidates": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "skeleton_validation_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + } + }, + "derived_fact": { + "type": [ + "string", + "null" + ] + }, + "derivation_basis": { + "type": "string" + }, + "legal_calculation_object": { + "type": [ + "string", + "null" + ] + }, + "domain_effects": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "required": [ + "module", + "role_projection", + "structure_ids", + "type_ids", + "review_codes" + ], + "properties": { + "module": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "role_projection": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "type_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "review_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + }, + "uniqueItems": true + } + } + } + }, + "calculation_requests": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "calculation_id" + ], + "properties": { + "calculation_id": { + "type": [ + "string", + "null" + ] + }, + "request_id": { + "type": [ + "string", + "null" + ] + }, + "operand_state": { + "enum": [ + "ready", + "partial", + "blocked", + "deferred", + null + ] + }, + "missing_operands": { + "type": "array", + "items": { + "type": "string" + } + }, + "law_version_refs": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "provenance": { + "type": "object" + } + } + } + } + } + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/fact_ledger_candidate_bundle.schema.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/fact_ledger_candidate_bundle.schema.json new file mode 100644 index 00000000..82cfe4bb --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/fact_ledger_candidate_bundle.schema.json @@ -0,0 +1,471 @@ +{ + "$id": "https://schemas.liti-agent.local/stage1/part4/fact_ledger_candidate_bundle.schema.json", + "title": "stage1_fact_ledger_candidate_bundle.v1", + "description": "Part 4 F0 산출 — 후보 행 묶음. domain_effects·calculation_requests 를 아는 것이 F0 의미 검사(PART4_LEDGER_SCHEMA_STALE)의 대상이다.", + "type": "object", + "additionalProperties": false, + "required": [ + "fact_ledger_candidate_bundle" + ], + "properties": { + "fact_ledger_candidate_bundle": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "status", + "rows", + "gate_report", + "counts", + "signal_manifest_transaction_id", + "created_by" + ], + "properties": { + "schema_version": { + "const": "stage1_fact_ledger_candidate_bundle.v1" + }, + "status": { + "enum": [ + "READY", + "READY_WITH_REVIEW" + ] + }, + "rows": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "fact_id", + "source_bo_id", + "type", + "date", + "parties", + "object_spec", + "amount", + "action", + "evidence_refs", + "credibility", + "legal_centrality", + "proof_strength", + "legal_effect_roles", + "claim_chain_ref", + "linked_structures", + "must_not_drop_in_claim_types", + "must_consider", + "state_context", + "money_claim_effect", + "commercial_successor_effect", + "actio_roles", + "linked_actio_structures", + "downstream_module_candidates", + "skeleton_validation_codes", + "derived_fact", + "derivation_basis", + "legal_calculation_object" + ], + "properties": { + "fact_id": { + "type": "string", + "pattern": "^F-[0-9]{3}$" + }, + "source_bo_id": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": [ + "string", + "null" + ] + }, + "date": { + "type": [ + "string", + "null" + ] + }, + "parties": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "role", + "value", + "source" + ], + "properties": { + "role": { + "type": "string" + }, + "value": { + "type": [ + "string", + "null" + ] + }, + "source": { + "type": "string" + } + } + } + }, + "object_spec": { + "type": [ + "string", + "null" + ] + }, + "amount": { + "type": [ + "string", + "null" + ] + }, + "action": { + "type": [ + "string", + "null" + ] + }, + "evidence_refs": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "credibility": { + "enum": [ + "high", + "medium", + "low" + ] + }, + "legal_centrality": { + "enum": [ + "critical", + "high", + "medium", + "low" + ] + }, + "proof_strength": { + "enum": [ + "documentary", + "high", + "medium", + "meeting_only_core", + "low" + ] + }, + "legal_effect_roles": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "claim_chain_ref": { + "type": "object", + "additionalProperties": false, + "required": [ + "claim_group_ids", + "liability_group_ids", + "signal_count" + ], + "properties": { + "claim_group_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "liability_group_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "signal_count": { + "type": "integer", + "minimum": 0 + } + } + }, + "linked_structures": { + "type": "object", + "additionalProperties": false, + "required": [ + "structure_ids" + ], + "properties": { + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "must_not_drop_in_claim_types": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "must_consider": { + "type": "boolean" + }, + "state_context": { + "type": "object", + "additionalProperties": false, + "required": [ + "bo_type", + "domain_ids" + ], + "properties": { + "bo_type": { + "type": [ + "string", + "null" + ] + }, + "domain_ids": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + }, + "uniqueItems": true + } + } + }, + "money_claim_effect": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "structure_ids" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "commercial_successor_effect": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "structure_ids" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "actio_roles": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "linked_actio_structures": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "downstream_module_candidates": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "skeleton_validation_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + } + }, + "derived_fact": { + "type": [ + "string", + "null" + ] + }, + "derivation_basis": { + "type": "string" + }, + "legal_calculation_object": { + "type": [ + "string", + "null" + ] + }, + "domain_effects": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "required": [ + "module", + "role_projection", + "structure_ids", + "type_ids", + "review_codes" + ], + "properties": { + "module": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "role_projection": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "structure_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "type_ids": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "review_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + }, + "uniqueItems": true + } + } + } + }, + "calculation_requests": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "calculation_id" + ], + "properties": { + "calculation_id": { + "type": [ + "string", + "null" + ] + }, + "request_id": { + "type": [ + "string", + "null" + ] + }, + "operand_state": { + "enum": [ + "ready", + "partial", + "blocked", + "deferred", + null + ] + }, + "missing_operands": { + "type": "array", + "items": { + "type": "string" + } + }, + "law_version_refs": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "provenance": { + "type": "object" + } + } + } + } + } + } + }, + "gate_report": { + "type": "object" + }, + "counts": { + "type": "object" + }, + "signal_manifest_transaction_id": { + "type": [ + "string", + "null" + ] + }, + "les_transaction_seal": { + "enum": [ + "MATCHED", + "UNSEALED_REVIEW" + ] + }, + "expected_runnable_domain_ids": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + }, + "uniqueItems": true + }, + "alias_resolution": { + "type": "object" + }, + "created_by": { + "type": "string" + } + } + } + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/legal_effect_structures.schema.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/legal_effect_structures.schema.json new file mode 100644 index 00000000..420b1c99 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/platform/schemas/legal_effect_structures.schema.json @@ -0,0 +1,381 @@ +{ + "$id": "https://schemas.liti-agent.local/stage1/part3/legal_effect_structures.schema.json", + "title": "stage1_legal_effect_structures.v1", + "description": "Part 3 L2 최종 산출 — 법률효과 구조와 역인덱스 셋(by_domain_id · by_module · by_bo_id). by_claim_form 은 만들지 않는다(claim-form 어휘가 registry 에 없다 — D-4 라). 열거를 하드코딩하지 않는다.", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "status", + "expected_runnable_domain_ids", + "theta", + "structure_records", + "structure_index", + "quality_gate", + "created_by", + "signal_manifest_transaction_id" + ], + "properties": { + "schema_version": { + "const": "stage1_legal_effect_structures.v1" + }, + "status": { + "enum": [ + "READY", + "READY_WITH_REVIEW" + ] + }, + "registry_version": { + "type": [ + "string", + "null" + ] + }, + "registry_index_sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "activation_manifest_sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "signal_manifest_transaction_id": { + "type": "string", + "pattern": "^S5TX-[a-f0-9]{20}$" + }, + "expected_runnable_domain_ids": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + } + }, + "theta": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]{1,127}$" + } + }, + "structure_records": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "structure_id", + "domain_id", + "type_id", + "priority_rank", + "order_key", + "module", + "role_projection", + "admission_policy", + "routes", + "route_count", + "evidence_refs", + "meeting_clause_refs", + "calculation_request_refs", + "source_bo_ids", + "review_codes", + "review_required" + ], + "properties": { + "structure_id": { + "type": "string", + "pattern": "^LES-[A-Za-z0-9._:-]{2,64}-[a-z][a-z0-9_]{1,127}$" + }, + "domain_id": { + "type": "string", + "pattern": "^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$" + }, + "type_id": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]{1,127}$" + }, + "priority_rank": { + "type": [ + "integer", + "null" + ] + }, + "order_key": { + "type": "array", + "minItems": 3, + "maxItems": 3 + }, + "module": { + "type": [ + "string", + "null" + ] + }, + "role_projection": { + "type": [ + "string", + "null" + ] + }, + "admission_policy": { + "type": "string", + "minLength": 1 + }, + "admission_rule": { + "type": "string" + }, + "registered_declared": { + "type": "boolean" + }, + "extension_key": { + "type": "string" + }, + "routes": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "signal_id", + "source_seed_id", + "registered", + "status" + ], + "properties": { + "signal_id": { + "type": [ + "string", + "null" + ] + }, + "source_seed_id": { + "type": [ + "string", + "null" + ] + }, + "registered": { + "type": [ + "boolean", + "null" + ] + }, + "status": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "route_count": { + "type": "integer", + "minimum": 0 + }, + "evidence_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "meeting_clause_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "calculation_request_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "source_bo_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "review_codes": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + } + }, + "review_required": { + "type": "boolean" + } + } + } + }, + "structure_index": { + "type": "object", + "additionalProperties": false, + "required": [ + "by_domain_id", + "by_module", + "by_bo_id" + ], + "properties": { + "by_domain_id": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "by_module": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "by_bo_id": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "quality_gate": { + "type": "object", + "additionalProperties": false, + "required": [ + "declaration_conservation", + "route_conservation", + "bo_conservation", + "review_queue" + ], + "properties": { + "declaration_conservation": { + "type": "object", + "additionalProperties": false, + "required": [ + "structure_records", + "declared_total_from_config", + "conservation_pass" + ], + "properties": { + "structure_records": { + "type": "integer", + "minimum": 0 + }, + "declared_total_from_config": { + "type": "integer", + "minimum": 0 + }, + "conservation_pass": { + "type": "boolean" + } + } + }, + "route_conservation": { + "type": "object", + "additionalProperties": false, + "required": [ + "routes_attached", + "routes_unattached", + "sg13_route_total_from_source", + "conservation_pass" + ], + "properties": { + "routes_attached": { + "type": "integer", + "minimum": 0 + }, + "routes_unattached": { + "type": "integer", + "minimum": 0 + }, + "sg13_route_total_from_source": { + "type": "integer", + "minimum": 0 + }, + "conservation_pass": { + "type": "boolean" + } + } + }, + "bo_conservation": { + "type": "object", + "additionalProperties": false, + "required": [ + "bo_attached", + "bo_unattached", + "bo_total_from_source", + "conservation_pass" + ], + "properties": { + "bo_attached": { + "type": "integer", + "minimum": 0 + }, + "bo_unattached": { + "type": "integer", + "minimum": 0 + }, + "bo_total_from_source": { + "type": "integer", + "minimum": 0 + }, + "conservation_pass": { + "type": "boolean" + } + } + }, + "review_queue": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "review_code", + "severity" + ], + "properties": { + "review_code": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]{2,127}$" + }, + "severity": { + "enum": [ + "SOFT_WARNING", + "HARD_WARNING" + ] + }, + "domain_id": { + "type": [ + "string", + "null" + ] + }, + "structure_id": { + "type": [ + "string", + "null" + ] + }, + "count": { + "type": "integer", + "minimum": 1 + } + } + } + } + } + }, + "created_by": { + "const": "Task_C_LE_L2_final_structure_index_writer" + } + } +} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/stage1_runtime/fact_ledger_compiler.py b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/stage1_runtime/fact_ledger_compiler.py new file mode 100644 index 00000000..641e4871 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/stage1_runtime/fact_ledger_compiler.py @@ -0,0 +1,910 @@ +#!/usr/bin/env python3 +"""Compile v.8 Fact Ledger candidate rows from BO, LES, signals, envelopes, and gate sidecars. + +순수 함수 모듈 — I/O·시각·난수 없음. 규칙 선언은 fact_ledger_policy.v1.json 이 유일 정본이며 +이 모듈은 그것을 읽어 dispatch 만 한다. gate_id·27필드·registry type_id 리터럴을 코드에 두지 +않는다(정적 검사 11·12). 사건 종류 이름을 어디에도 두지 않는다(137종 원칙). + +공개 API + normalize_gate_rules(sidecar_doc, policy) 17축 방언 정규화 리더 + resolve_alias_table(alias_declarations, sidecars, policy) + compile_fact_ledger_candidates(...) 조립 본체 (아래 시그니처) +""" +from __future__ import annotations + +import json +import re +from typing import Any + +_WS_RE = re.compile(r"\s+") +_CODE_RE = re.compile(r"^[A-Z][A-Z0-9_]{2,127}$") +_DOMAIN_RE = re.compile(r"^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$") + + +# --------------------------------------------------------------------------- +# 공용 헬퍼 +# --------------------------------------------------------------------------- + +def _dict(value: Any) -> dict: + return value if isinstance(value, dict) else {} + + +def _list(value: Any) -> list: + return value if isinstance(value, list) else [] + + +def _strings(value: Any) -> list[str]: + return [item for item in _list(value) if isinstance(item, str)] + + +def _nonblank(value: Any) -> bool: + return value not in (None, "", [], {}) + + +def _text(value: Any, limit: int) -> str | None: + """키 오름차순 JSON -> 공백열 단일 공백 -> limit 절단 -> 빈 문자열이면 None.""" + if value is None: + return None + if isinstance(value, str): + rendered = value + else: + rendered = json.dumps(value, ensure_ascii=False, sort_keys=True) + rendered = _WS_RE.sub(" ", rendered).strip() + if not rendered: + return None + return rendered[:limit] + + +def _first_value(*values: Any) -> Any: + for value in values: + if _nonblank(value): + return value + return None + + +def _first_key(record: dict, keys: list[str]) -> Any: + for key in keys: + if _nonblank(record.get(key)): + return record[key] + return None + + +def _dedupe_keep_order(items: list[str]) -> list[str]: + seen: set[str] = set() + out: list[str] = [] + for item in items: + if item not in seen: + seen.add(item) + out.append(item) + return out + + +# --------------------------------------------------------------------------- +# F0-0c — BO 압축·정규화 (구본 _compact_bo 등가 계층. (가) 9필드 전부의 전제) +# --------------------------------------------------------------------------- + +def compact_bo(bo: dict, policy: dict) -> dict: + norm = _dict(policy.get("bo_normalization")) + key_map = _dict(norm.get("key_map")) + keep = _strings(norm.get("domain_payload_keep_keys")) + out: dict[str, Any] = {} + for key, value in bo.items(): + out[key_map.get(key, key)] = value + payload = _dict(_dict(bo.get("extensions")).get("domain_payload")) + out["domain_payload"] = {k: payload[k] for k in keep + if k in payload and _nonblank(payload[k])} + out["core"] = _dict(bo.get("core_field_base")) + return out + + +# --------------------------------------------------------------------------- +# (다) — 증거력 산정 (정책 evidence_scoring_da_2 의 5갈래 · 순서 그대로) +# --------------------------------------------------------------------------- + +def _flatten_evidence(document: Any, depth: int = 3) -> list[dict]: + rows: list[dict] = [] + + def visit(node: Any, level: int) -> None: + if level > depth: + return + if isinstance(node, dict): + if any(k in node for k in ("evidence_index", "id", "evidence_id", + "eid", "index", "doc_id")): + rows.append(node) + return + for value in node.values(): + visit(value, level + 1) + elif isinstance(node, list): + for item in node: + visit(item, level + 1) + + visit(document, 0) + return rows + + +def _evidence_id(record: dict) -> str | None: + value = _first_key(record, ["evidence_index", "id", "evidence_id", + "eid", "index", "doc_id"]) + return value if isinstance(value, str) else None + + +def _excerpt_from_indexed(record: dict) -> str: + facts = _strings(record.get("key_facts")) + if facts: + return " ".join(facts) + parts = _strings(record.get("key_dates")) + _strings(record.get("key_amounts")) + if parts: + return " ".join(parts) + for value in record.values(): + if isinstance(value, str) and value.strip(): + return value + return "" + + +def build_evidence_authority(evidence_document: Any) -> dict[str, dict]: + authority: dict[str, dict] = {} + for record in _flatten_evidence(evidence_document): + eid = _evidence_id(record) + if not eid: + continue + authority[eid] = { + "evidence_index": eid, + "title": _first_key(record, ["title", "Title", "name", "doc_title"]), + "excerpt": _excerpt_from_indexed(record), + "priority": _first_key(record, ["priority", "Priority", "weight"]), + "authentication": _first_key(record, ["authentication", "auth", + "source_authentication"]), + "source": "evidence_indexed.json", + } + return authority + + +def score_evidence(evidence_refs: list[str], authority: dict[str, dict], + policy: dict, reviews: list[dict], + fact_id: str) -> tuple[str, str, int]: + """(credibility, proof_strength, 절단여유 최소값) — 정책 5갈래 순서 그대로.""" + scoring = _dict(policy.get("evidence_scoring_da_2")) + markers = _strings(scoring.get("markers_documentary")) + rows: list[dict] = [] + for ref in evidence_refs: + row = authority.get(ref) + if row is None: + # 우주 밖 참조 — stub 도 비어 있지 않은 증거 행으로 계수된다(정책 승계). + reviews.append({"review_code": "EVIDENCE_REF_NOT_IN_EVIDENCE_UNIVERSE", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + row = {"evidence_index": ref, "missing": True} + rows.append(row) + if not rows: + return "low", "low", -1 + pieces: list[str] = [] + slack = 10 ** 9 + for row in rows: + rendered = _WS_RE.sub(" ", json.dumps(row, ensure_ascii=False, + sort_keys=True)).strip() + slack = min(slack, 300 - len(rendered)) + pieces.append(rendered[:300]) + text = " ".join(pieces) + if any(marker in text for marker in markers): + return "high", "documentary", slack + if len(rows) >= 2: + return "high", "high", slack + if ("meeting" in text) or ("회의" in text) or ("면담" in text): + return "medium", "meeting_only_core", slack + return "medium", "medium", slack + + +# --------------------------------------------------------------------------- +# F0-0 — 17축 사이드카 방언 정규화 리더 +# --------------------------------------------------------------------------- + +def normalize_gate_rules(sidecar_doc: Any, policy: dict) -> dict: + """사이드카 한 파일을 정규화한다. {rules: {gate_id: norm}, file_meta, warnings}.""" + axes = _dict(_dict(policy.get("gate_execution")).get("normalization_axes_17")) + array_keys = _strings(axes.get("rule_array_keys")) or ["rules", "gates"] + code_keys = _strings(axes.get("emitted_code_keys")) + doc = _dict(sidecar_doc) + raw_rules: list[dict] = [] + array_key = None + for key in array_keys: + if isinstance(doc.get(key), list): + array_key = key + raw_rules = [r for r in doc[key] if isinstance(r, dict)] + break + warnings: list[dict] = [] + norm_rules: dict[str, dict] = {} + for rule in raw_rules: + gate_id = rule.get("gate_id") + if not isinstance(gate_id, str): + warnings.append({"code": "GATE_RULE_WITHOUT_ID", "detail": str(rule)[:120]}) + continue + writer = _dict(rule.get("writer_effect")) + row_eff = _dict(rule.get("row_effect")) + on_true = _dict(rule.get("on_true")) + condition = rule.get("condition") + cond_kind = "dict" if isinstance(condition, dict) else "str" + cond_key = next(iter(condition), None) if cond_kind == "dict" else None + cond_value = condition.get(cond_key) if cond_kind == "dict" else condition + emitted: list[str] = [] + for key in code_keys: + value = rule.get(key) + if isinstance(value, str): + emitted.append(value) + else: + emitted.extend(_strings(value)) + blocks = None + for container, key in (("writer_effect", "final_drafting_block"), + ("writer_effect", "blocks_final_drafting"), + ("on_true", "blocks_final_drafting")): + holder = writer if container == "writer_effect" else on_true + if key in holder: + blocks = bool(holder[key]) + break + status_fire = (writer.get("status_when_triggered") + or writer.get("status_on_fire") + or on_true.get("writer_status")) + evaluation = (rule.get("evaluation_mode") or rule.get("implementation_type") + or rule.get("evaluation")) + norm_rules[gate_id] = { + "gate_id": gate_id, + "condition_kind": cond_kind, + "condition_key": cond_key, + "condition_value": cond_value, + "emitted_codes": _dedupe_keep_order(emitted), + "blocks_final_drafting": blocks, + "status_when_triggered": status_fire, + "row_must_consider": bool(row_eff.get("must_consider")), + "preserve_seed": bool(on_true.get("preserve_seed")), + "unknown_or_missing_policy": rule.get("unknown_or_missing_policy"), + "evaluation": evaluation, + "writes_fact_ledger": writer.get("writes_fact_ledger"), + "silent_repair_forbidden": rule.get("silent_repair_forbidden"), + "source_membership_required": rule.get("source_membership_required"), + "input_declaration": rule.get("input_fields") or rule.get("input_contracts"), + } + if doc.get("global_status_rule") is not None: + warnings.append({"code": "SIDECAR_GLOBAL_STATUS_RULE_IGNORED", + "detail": str(doc.get("global_status_rule"))[:120]}) + return {"rules": norm_rules, + "file_meta": {"array_key": array_key, + "schema_version": doc.get("schema_version"), + "status_key": ("closure_status" if "closure_status" in doc + else ("status" if "status" in doc else None))}, + "warnings": warnings} + + +# --------------------------------------------------------------------------- +# F0-0b — 별칭 해소 (D-P4-7 — 전수 자기선언 대조 · 침묵 탈락 금지) +# --------------------------------------------------------------------------- + +def resolve_alias_table(alias_declarations: Any, structure_type_sidecars: dict, + policy: dict) -> dict: + root = _dict(alias_declarations) + root = _dict(root.get("extension_payload_key_declarations", root)) + table = {k: _strings(v) for k, v in _dict(root.get("legacy_alias_targets")).items()} + derived_mismatches = _list(root.get("legacy_declaration_mismatches")) + abbrev = _dict(_dict(policy.get("legacy_alias")).get("abbreviation_map")) + self_decl: dict[str, set] = {} + for domain_id, doc in sorted(_dict(structure_type_sidecars).items()): + doc = _dict(doc) + rows = None + for key in ("types", "structure_types", "records", "entries"): + if isinstance(doc.get(key), list): + rows = doc[key] + break + for row in rows or []: + if not isinstance(row, dict): + continue + value = row.get("legacy_source") or row.get("legacy_source_domain") + if not isinstance(value, str): + continue + head = value.split("_")[0] + if head not in abbrev: + continue # 'Part3 fallback' 등 비 B* 값은 선언이 아니다 + self_decl.setdefault(abbrev[head], set()).add(domain_id) + reviews: list[dict] = [] + mismatch_map: dict[str, dict] = {} + for legacy in sorted(set(table) | set(self_decl)): + declared = self_decl.get(legacy, set()) + aliased = set(table.get(legacy, [])) + missing = sorted(declared - aliased) + excess = sorted(aliased - declared) + if missing: + mismatch_map[legacy] = {"missing": missing, "excess": excess} + reviews.append({"review_code": "LEGACY_ALIAS_SELF_MISMATCH_REVIEW", + "severity": "SOFT_WARNING", "legacy": legacy, + "missing_domain_ids": missing, + "excess_domain_ids": excess}) + derived_missing = sorted({str(_dict(m).get("domain_id")) + for m in derived_mismatches if _dict(m).get("domain_id")}) + actual_missing = sorted({d for m in mismatch_map.values() for d in m["missing"]}) + if derived_missing != actual_missing: + reviews.append({"review_code": "LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", + "derived_list": derived_missing, + "actual_missing": actual_missing}) + return {"table": table, "self_declarations": {k: sorted(v) for k, v in self_decl.items()}, + "mismatches": mismatch_map, "reviews": reviews} + + +# --------------------------------------------------------------------------- +# 조립 본체 +# --------------------------------------------------------------------------- + +def compile_fact_ledger_candidates( + bo_document: Any, + les_document: Any, + signal_documents: dict, + domain_envelopes: dict, + p2_handoff_document: Any, + evidence_document: Any, + registry_index: Any, + domain_configs: dict, + gate_sidecars: dict, + structure_type_sidecars: dict, + activation_document: Any, + alias_declarations: Any, + policy: dict) -> dict: + reviews: list[dict] = [] + audit: dict[str, Any] = {} + + activation_root = _dict(activation_document) + activation_root = _dict(activation_root.get("domain_activation_manifest", + activation_root)) + expected_runnable = _strings(activation_root.get("expected_runnable_domain_ids")) + + # ---- F0-0 리더 · F0-0b 별칭 ---------------------------------------- + normalized: dict[str, dict] = {} + dialect_counts: dict[str, Any] = {"array_key": {}, "files": 0} + reader_warnings: list[dict] = [] + for domain_id, sidecar in sorted(_dict(gate_sidecars).items()): + result = normalize_gate_rules(sidecar, policy) + normalized[domain_id] = result + dialect_counts["files"] += 1 + ak = result["file_meta"]["array_key"] + dialect_counts["array_key"][ak] = dialect_counts["array_key"].get(ak, 0) + 1 + reader_warnings.extend(result["warnings"]) + audit["sidecar_dialects"] = dialect_counts + audit["reader_warnings"] = reader_warnings + + alias = resolve_alias_table(alias_declarations, structure_type_sidecars, policy) + reviews.extend(alias["reviews"]) + + # ---- LES 적재 ------------------------------------------------------- + les_root = _dict(les_document) + structure_records = [r for r in _list(les_root.get("structure_records")) + if isinstance(r, dict)] + record_by_id = {r.get("structure_id"): r for r in structure_records + if isinstance(r.get("structure_id"), str)} + index = _dict(les_root.get("structure_index")) + by_bo_id = {k: _strings(v) for k, v in _dict(index.get("by_bo_id")).items()} + + # ---- SG 적재 -------------------------------------------------------- + def signal_records(sg: str) -> list[dict]: + doc = _dict(_dict(signal_documents).get(sg)) + return [r for r in _list(doc.get("records")) if isinstance(r, dict)] + + sg11_records = signal_records("SG-11") + sg11_by_signal_id = {r.get("signal_id"): r for r in sg11_records + if isinstance(r.get("signal_id"), str)} + sg04_by_domain: dict[str, list[str]] = {} + for record in signal_records("SG-04"): + for domain_id in _strings(record.get("domain_ids")): + if isinstance(record.get("signal_id"), str): + sg04_by_domain.setdefault(domain_id, []).append(record["signal_id"]) + + # ---- F0-7b — 게이트 입력 정규화 ------------------------------------ + handoff_root = _dict(p2_handoff_document) + handoff_root = _dict(handoff_root.get("stage1_part2_review_handoff", handoff_root)) + handoff_items = [r for r in _list(handoff_root.get("review_items")) + if isinstance(r, dict)] + p2_rules = _dict(policy.get("p2_handoff_rules")) + code_pool: dict[str, list[str]] = {} + gate_input_counts = {"envelope_review_items": 0, "handoff_codes": 0, + "handoff_not_a_code": 0, "handoff_domain_unresolved": 0, + "slot_entries": 0, "operand_states": 0} + for domain_id in expected_runnable: + env = _dict(_dict(domain_envelopes).get(domain_id)) + env = _dict(env.get("domain_signal_envelope", env)) + for item in _list(env.get("review_items")): + code = _dict(item).get("review_code") or _dict(item).get("code") + if isinstance(code, str) and _CODE_RE.match(code): + code_pool.setdefault(domain_id, []).append(code) + gate_input_counts["envelope_review_items"] += 1 + for item in handoff_items: + raw_code = item.get("source_review_code") + if not (isinstance(raw_code, str) and _CODE_RE.match(raw_code)): + if raw_code is not None: + gate_input_counts["handoff_not_a_code"] += 1 + reviews.append({"review_code": "P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW", + "severity": "SOFT_WARNING", + "detail": str(raw_code)[:80]}) + continue + source_domain = item.get("source_domain") + targets: list[str] = [] + if isinstance(source_domain, str): + if _DOMAIN_RE.match(source_domain): + targets = [source_domain] + elif source_domain in alias["table"]: + targets = alias["table"][source_domain] + if not targets: + gate_input_counts["handoff_domain_unresolved"] += 1 + reviews.append({"review_code": "P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW", + "severity": "SOFT_WARNING", + "detail": str(source_domain)[:80]}) + continue + for domain_id in targets: + code_pool.setdefault(domain_id, []).append(raw_code) + gate_input_counts["handoff_codes"] += 1 + slot_status: dict[str, list[dict]] = {} + for domain_id in expected_runnable: + env = _dict(_dict(domain_envelopes).get(domain_id)) + env = _dict(env.get("domain_signal_envelope", env)) + entries = [e for e in _list(env.get("evidence_slot_status")) + if isinstance(e, dict)] + if entries: + slot_status[domain_id] = entries + gate_input_counts["slot_entries"] += len(entries) + operand_by_domain: dict[str, list[str]] = {} + for record in sg11_records: + details = _dict(record.get("details")) + state = record.get("operand_state") or details.get("operand_state") + if not isinstance(state, str): + continue + for domain_id in (_strings(record.get("domain_ids")) + or _strings(details.get("domain_ids"))): + operand_by_domain.setdefault(domain_id, []).append(state) + gate_input_counts["operand_states"] += 1 + audit["gate_input_counts"] = gate_input_counts + + # ---- F0-1 ~ F0-6b — 행 생성 ---------------------------------------- + bo_records = [b for b in _list(bo_document) if isinstance(b, dict)] + bo_sorted = sorted(bo_records, key=lambda b: str(b.get("BO_ID") or "")) + authority = build_evidence_authority(evidence_document) + calc_types = set(_strings(policy.get("legal_calculation_object_types"))) + na_defaults = _dict(policy.get("field_synthesis_na_15")) + rows: list[dict] = [] + attach_stats = {"attached_bo": 0, "unattached_bo": 0} + used_structure_ids: set[str] = set() + used_sg11: set[str] = set() + slack_min = 10 ** 9 + for idx, bo in enumerate(bo_sorted, 1): + compact = compact_bo(bo, policy) + core = compact["core"] + fact_id = "F-%03d" % idx + bo_id = str(bo.get("BO_ID") or "") + # (가) 9필드 + parties = [] + if _nonblank(core.get("Performer")): + parties.append({"role": "actor", "value": _text(core.get("Performer"), 160), + "source": "BO.core_field_base"}) + if _nonblank(core.get("Subject")): + parties.append({"role": "counterparty", + "value": _text(core.get("Subject"), 160), + "source": "BO.core_field_base"}) + amount = _text(_first_value(compact.get("amount"), core.get("Amount")), 240) + action = _first_value(compact.get("action"), core.get("Action")) + evidence_refs = _strings(bo.get("source_evidence_indexes")) + # F0-3 — LES 조인 + structure_ids = sorted(set(by_bo_id.get(bo_id, []))) + attached = [record_by_id[s] for s in structure_ids if s in record_by_id] + if attached: + attach_stats["attached_bo"] += 1 + else: + attach_stats["unattached_bo"] += 1 + reviews.append({"review_code": "FACT_ROW_UNATTACHED_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": bo_id}) + used_structure_ids.update(s for s in structure_ids if s in record_by_id) + domain_effects: dict[str, dict] = {} + for record in attached: + domain_id = record.get("domain_id") + if not isinstance(domain_id, str): + continue + slot = domain_effects.setdefault(domain_id, { + "module": set(), "role_projection": set(), "structure_ids": set(), + "type_ids": set(), "review_codes": set()}) + if isinstance(record.get("module"), str): + slot["module"].add(record["module"]) + if isinstance(record.get("role_projection"), str): + slot["role_projection"].add(record["role_projection"]) + if isinstance(record.get("structure_id"), str): + slot["structure_ids"].add(record["structure_id"]) + if isinstance(record.get("type_id"), str): + slot["type_ids"].add(record["type_id"]) + slot["review_codes"].update( + c for c in _strings(record.get("review_codes")) if _CODE_RE.match(c)) + domain_effects = {d: {k: sorted(v) for k, v in slot.items()} + for d, slot in sorted(domain_effects.items())} + modules = sorted({m for slot in domain_effects.values() for m in slot["module"]}) + roles = sorted({r for slot in domain_effects.values() + for r in slot["role_projection"]}) + type_ids = sorted({t for slot in domain_effects.values() + for t in slot["type_ids"]}) + # F0-5 — SG-11 조인 + operand_state 승계 (재산정 금지) + calculation_requests: list[dict] = [] + route_refs: list[str] = [] + for record in attached: + for ref in _strings(record.get("calculation_request_refs")): + route_refs.append(ref) + sg = sg11_by_signal_id.get(ref) + if sg is None: + calculation_requests.append({"calculation_id": None, + "request_id": ref, + "provenance": {"signal_id": ref, + "dangling": True}}) + reviews.append({"review_code": "CALCULATION_REF_DANGLING_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + continue + used_sg11.add(ref) + details = _dict(sg.get("details")) + calc_id = details.get("calculation_id") + if not isinstance(calc_id, str) or not calc_id: + calc_id = ref + reviews.append({"review_code": "CALCULATION_ID_SUBSTITUTED_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + law_refs = sorted(set( + _strings(sg.get("law_version_refs")) + + _strings(details.get("law_version_refs")) + + [ref2 for d in _strings(sg.get("domain_ids")) + for ref2 in sg04_by_domain.get(d, [])])) + calculation_requests.append({ + "calculation_id": calc_id, + "request_id": ref, + "operand_state": (sg.get("operand_state") + or details.get("operand_state")), + "missing_operands": _strings(details.get("missing_operands")), + "law_version_refs": law_refs, + "provenance": {"signal_id": ref, + "structure_ids": sorted( + s for s in structure_ids + if ref in _strings( + _dict(record_by_id.get(s)).get( + "calculation_request_refs")))}, + }) + # (다) 증거력 + credibility, proof_strength, slack = score_evidence( + evidence_refs, authority, policy, reviews, fact_id) + slack_min = min(slack_min, slack) + # (나) 15필드 + object_spec = _text(core.get("Object"), 160) + calc_obj_candidates = sorted(calc_types & set(type_ids)) + legal_calculation_object = calc_obj_candidates[0] if calc_obj_candidates else None + alias_vocab = _dict(policy.get("alias_module_vocabulary")) + mod_actio = alias_vocab.get("actio_fields") + mod_money = alias_vocab.get("money_claim_effect") + mod_successor = alias_vocab.get("commercial_successor_effect") + actio_ids = sorted({s for slot in domain_effects.values() + for s in slot["structure_ids"] + if mod_actio in slot["module"]}) + actio_roles = sorted({r for slot in domain_effects.values() + if mod_actio in slot["module"] + for r in slot["role_projection"]}) + money_ids = sorted({s for slot in domain_effects.values() + if mod_money in slot["module"] + for s in slot["structure_ids"]}) + successor_ids = sorted({s for slot in domain_effects.values() + if mod_successor in slot["module"] + for s in slot["structure_ids"]}) + row = { + "fact_id": fact_id, + "source_bo_id": bo_id, + "type": compact.get("bo_type") if isinstance(compact.get("bo_type"), str) else None, + "date": _first_value(core.get("BehaviorTime"), compact.get("date")), + "parties": parties, + "object_spec": object_spec, + "amount": amount, + "action": action if isinstance(action, str) else _text(action, 240), + "evidence_refs": evidence_refs, + "credibility": credibility, + "legal_centrality": "high" if attached else "medium", + "proof_strength": proof_strength, + "legal_effect_roles": roles, + "claim_chain_ref": {"claim_group_ids": [], "liability_group_ids": [], + "signal_count": len(route_refs)}, + "linked_structures": {"structure_ids": structure_ids}, + "must_not_drop_in_claim_types": modules, + "must_consider": bool(attached), + "state_context": {"bo_type": compact.get("bo_type") + if isinstance(compact.get("bo_type"), str) else None, + "domain_ids": sorted(domain_effects)}, + "money_claim_effect": {"enabled": bool(money_ids), + "structure_ids": money_ids}, + "commercial_successor_effect": {"enabled": bool(successor_ids), + "structure_ids": successor_ids}, + "actio_roles": actio_roles, + "linked_actio_structures": actio_ids, + "downstream_module_candidates": modules, + "skeleton_validation_codes": [], + "derived_fact": action if isinstance(action, str) else _text(action, 240), + "derivation_basis": ("structure_ids=" + ",".join(structure_ids) + if structure_ids else "bo_only"), + "legal_calculation_object": legal_calculation_object, + "domain_effects": domain_effects, + "calculation_requests": calculation_requests, + } + rows.append(row) + audit["evidence_truncation_slack_min"] = (None if slack_min == 10 ** 9 else slack_min) + + # ---- F0-8 — 게이트 합집합 실행 (§4.4 그대로) ------------------------ + gate_exec = _dict(policy.get("gate_execution")) + implemented = set(_strings(gate_exec.get("implemented_condition_keys"))) + default_missing_policy = str(gate_exec.get("unknown_or_missing_policy_default") + or "fail_open_to_review") + declared_gates: dict[str, dict] = {} + for domain_id in expected_runnable: + config = _dict(_dict(domain_configs).get(domain_id)) + config_root = _dict(config.get("domain_config", config)) + for gate in _list(config_root.get("gates")): + gate = _dict(gate) + gate_id = gate.get("gate_id") + if isinstance(gate_id, str): + declared_gates[gate_id] = {"domain_id": domain_id, "declared": gate} + all_rules: dict[str, dict] = {} + for domain_id, result in normalized.items(): + for gate_id, rule in result["rules"].items(): + all_rules[gate_id] = {"domain_id": domain_id, "rule": rule} + gate_report: list[dict] = [] + fired_by_domain: dict[str, list[dict]] = {} + for gate_id in sorted(declared_gates): + info = declared_gates[gate_id] + domain_id = info["domain_id"] + declared = info["declared"] + registry_codes = [c for c in _strings(declared.get("review_codes")) + if _CODE_RE.match(c)] + entry: dict[str, Any] = {"gate_id": gate_id, "domain_id": domain_id, + "fired": False, "input_state": "n/a"} + rule_info = all_rules.get(gate_id) + if rule_info is None: + entry["disposition"] = "GATE_SPEC_NOT_DEPLOYED_REVIEW" + reviews.append({"review_code": "GATE_SPEC_NOT_DEPLOYED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + gate_report.append(entry) + continue + rule = rule_info["rule"] + fired = False + if rule["condition_kind"] == "dict": + key = rule["condition_key"] + if key in implemented: + entry["disposition"] = "implemented" + value = rule["condition_value"] + if key == "any_review_code_present": + pool = code_pool.get(domain_id) + if pool is None and domain_id not in _dict(domain_envelopes): + entry["input_state"] = "input_missing" + entry["missing_policy"] = ( + rule["unknown_or_missing_policy"] or default_missing_policy) + else: + entry["input_state"] = "supplied" + fired = bool(set(_strings(value)) & set(pool or [])) + elif key == "required_slots_missing_or_conflicting": + entries = slot_status.get(domain_id) + if entries is None and domain_id not in _dict(domain_envelopes): + entry["input_state"] = "input_missing" + entry["missing_policy"] = ( + rule["unknown_or_missing_policy"] or default_missing_policy) + else: + entry["input_state"] = "supplied" + wanted = set(_strings(value)) + fired = any( + (e.get("slot_id") in wanted) + and (e.get("status") in ("missing", "conflicted")) + for e in entries or []) + elif key == "calculation_operand_state_in": + states = operand_by_domain.get(domain_id) + entry["input_state"] = ("supplied" if states else "input_missing") + fired = bool(set(_strings(value)) & set(states or [])) + else: + entry["disposition"] = "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW" + entry["condition_key"] = key + reviews.append({"review_code": "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": str(key)}) + else: + entry["disposition"] = "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW" + entry["predicate_spec"] = _text(rule["condition_value"], 200) + reviews.append({"review_code": "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": _text(rule["condition_value"], 160)}) + # 차단 표지 대조 — registry ↔ 정규화된 사이드카. + declared_blocks = declared.get("blocks_final_drafting") + if (rule["blocks_final_drafting"] is not None + and declared_blocks is not None + and bool(declared_blocks) != rule["blocks_final_drafting"]): + reviews.append({"review_code": "GATE_BLOCK_FLAG_MISMATCH_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + entry["block_flag_mismatch"] = True + emitted = _dedupe_keep_order(rule["emitted_codes"] + registry_codes) + excess = [c for c in rule["emitted_codes"] if c not in registry_codes] + if excess: + entry["codes_not_in_registry"] = excess + reviews.append({"review_code": "GATE_EMITTED_CODE_NOT_IN_REGISTRY", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": ",".join(excess)[:160]}) + entry["fired"] = fired + entry["blocks_final_drafting"] = (rule["blocks_final_drafting"] + if rule["blocks_final_drafting"] is not None + else bool(declared_blocks)) + if fired: + projection_rows = [r["fact_id"] for r in rows + if domain_id in r["domain_effects"]] + entry["projected_row_count"] = len(projection_rows) + fired_by_domain.setdefault(domain_id, []).append({ + "gate_id": gate_id, "codes": emitted, + "row_must_consider": rule["row_must_consider"], + "preserve_seed": rule["preserve_seed"], + "blocks_final_drafting": entry["blocks_final_drafting"], + "status_when_triggered": rule["status_when_triggered"], + "projected_rows": projection_rows, + }) + gate_report.append(entry) + orphan_rules = sorted(set(all_rules) - set(declared_gates)) + for gate_id in orphan_rules: + reviews.append({"review_code": "GATE_RULE_NOT_IN_REGISTRY", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + + # ---- 행 투영 3분기 + F0-8b 스켈레톤 -------------------------------- + skeleton_order = _strings(_dict(policy.get("skeleton_checks")).get("order")) + for row in rows: + codes: list[str] = [] + if not _nonblank(row["derived_fact"]): + codes.append(skeleton_order[0]) + if not row["evidence_refs"]: + codes.append(skeleton_order[1]) + if not row["linked_structures"]["structure_ids"]: + codes.append(skeleton_order[2]) + gate_codes: list[str] = [] + for domain_id in sorted(row["domain_effects"]): + for firing in sorted(fired_by_domain.get(domain_id, []), + key=lambda f: f["gate_id"]): + gate_codes.extend(firing["codes"]) + if firing["row_must_consider"]: + row["must_consider"] = row["must_consider"] or True + row["skeleton_validation_codes"] = _dedupe_keep_order(codes + gate_codes) + + # ---- STRUCTURE_UNATTACHED_REVIEW ----------------------------------- + unattached_structures = sorted(set(record_by_id) - used_structure_ids) + for structure_id in unattached_structures: + reviews.append({"review_code": "STRUCTURE_UNATTACHED_REVIEW", + "severity": "SOFT_WARNING", "detail": structure_id}) + + # ---- F0-9 — 예외 pack ---------------------------------------------- + pack_policy = _dict(policy.get("exception_pack")) + char_limit = int(pack_policy.get("pack_item_char_limit") or 2000) + exceptions: list[dict] = [] + for row in rows: + if row["legal_calculation_object"] is None: + continue + bo = next((b for b in bo_sorted + if str(b.get("BO_ID") or "") == row["source_bo_id"]), {}) + core = _dict(bo.get("core_field_base")) + amount_candidates = [v for v in (bo.get("amount"), core.get("Amount")) + if _nonblank(v)] + if len(amount_candidates) >= 2: + payload = {"amount_candidates": [_text(v, 240) for v in amount_candidates], + "object_spec": row["object_spec"], + "calculation_request_ids": sorted( + str(c.get("calculation_id")) for c in + row["calculation_requests"] if c.get("calculation_id"))} + item = {"exception_id": "FX-%03d" % (len(exceptions) + 1), + "exception_type": "legal_calculation_required", + "fact_id": row["fact_id"], + "source_bo_id": row["source_bo_id"], + "conflict_flags": ["CALCULATION_BASIS_CONFLICT"], + "compact_payload": payload} + rendered = json.dumps(item, ensure_ascii=False) + if len(rendered) > char_limit: + item["oversize_review"] = True + reviews.append({"review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", + "fact_id": row["fact_id"], + "detail": "pack item oversize %d>%d" % (len(rendered), + char_limit)}) + exceptions.append(item) + elif (not _nonblank(row["amount"])) or (not _nonblank(row["object_spec"])): + reviews.append({"review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", "fact_id": row["fact_id"], + "detail": "amount_or_object_spec_blank"}) + unreachable = {name for name, meta in + _dict(policy.get("f1_exception_types")).items() + if _dict(meta).get("unreachable_in_v8")} + for item in exceptions: + if item["exception_type"] in unreachable: + raise ValueError(json.dumps({ + "reason_code": "PART4_UNREACHABLE_EXCEPTION_TYPE", + "exception_type": item["exception_type"]}, ensure_ascii=False)) + + # ---- F0-10 — 보존 등식 계수 ---------------------------------------- + effect_left = sum(len(r["domain_effects"]) for r in rows) + effect_right = 0 + calc_right = 0 + for bo in bo_sorted: + bo_id = str(bo.get("BO_ID") or "") + attached = [record_by_id[s] for s in by_bo_id.get(bo_id, []) + if s in record_by_id] + effect_right += len({r.get("domain_id") for r in attached + if isinstance(r.get("domain_id"), str)}) + calc_right += len({ref for r in attached + for ref in _strings(r.get("calculation_request_refs"))}) + calc_left = sum(len(r["calculation_requests"]) for r in rows) + sg11_total = len(sg11_records) + sg11_unused = sg11_total - len(used_sg11) + equations = { + "row": {"left": len(rows), "right": len(bo_sorted), + "holds": len(rows) == len(bo_sorted)}, + "attachment_coverage": { + "attached": attach_stats["attached_bo"], + "unattached": attach_stats["unattached_bo"], + "total": len(bo_sorted), + "holds": (attach_stats["attached_bo"] + attach_stats["unattached_bo"] + == len(bo_sorted))}, + "effect": {"left": effect_left, "right": effect_right, + "holds": effect_left == effect_right}, + "calc_row": {"left": calc_left, "right": calc_right, + "holds": calc_left == calc_right}, + "calc_source": {"attached_distinct": len(used_sg11), + "unattached_distinct": sg11_unused, + "total": sg11_total, + "holds": len(used_sg11) + sg11_unused == sg11_total}, + } + + # ---- 상태·집계 ------------------------------------------------------ + les_review_codes = set(_strings(policy.get("les_inherited_review_codes"))) + les_review_present = any( + (set(slot["review_codes"]) & les_review_codes) + for row in rows for slot in row["domain_effects"].values()) or any( + _dict(r).get("review_required") for r in structure_records) + gate_fired_any = any(f for firings in fired_by_domain.values() for f in firings) + status = ("READY_WITH_REVIEW" + if (reviews or exceptions or les_review_present or gate_fired_any) + else "READY") + counts = { + "rows": len(rows), + "bo_records": len(bo_sorted), + "structure_records": len(structure_records), + "gates_declared": len(declared_gates), + "gates_implemented": sum(1 for e in gate_report + if e.get("disposition") == "implemented"), + "gates_visualized_str": sum( + 1 for e in gate_report + if e.get("disposition") == "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW"), + "gates_visualized_unsupported": sum( + 1 for e in gate_report + if e.get("disposition") == "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW"), + "gates_fired": sum(1 for e in gate_report if e.get("fired")), + "orphan_rules": len(orphan_rules), + "review_items": len(reviews), + "exceptions": len(exceptions), + "conservation": equations, + } + bundle = { + "schema_version": "stage1_fact_ledger_candidate_bundle.v1", + "status": status, + "rows": rows, + "gate_report": {"gates": gate_report, + "fired_by_domain": {d: f for d, f in + sorted(fired_by_domain.items())}, + "orphan_rule_ids": orphan_rules}, + "counts": counts, + "signal_manifest_transaction_id": None, + "expected_runnable_domain_ids": expected_runnable, + "alias_resolution": {"table": alias["table"], + "self_declarations": alias["self_declarations"], + "mismatches": alias["mismatches"]}, + "created_by": "fact_ledger_compiler", + } + pack = { + "schema_version": "stage1_fact_exception_pack.v1", + "has_exceptions": bool(exceptions), + "exception_count": len(exceptions), + "exceptions": exceptions, + "budget": {"pack_item_char_limit": char_limit}, + } + audit["unattached_structure_ids"] = unattached_structures + audit["equations"] = equations + return {"fact_ledger_candidate_bundle": bundle, + "fact_exception_pack": pack, + "review_items": reviews, + "audit": audit} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/stage1_runtime/fact_ledger_compiler.txt b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/stage1_runtime/fact_ledger_compiler.txt new file mode 100644 index 00000000..641e4871 --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/stage1_runtime/fact_ledger_compiler.txt @@ -0,0 +1,910 @@ +#!/usr/bin/env python3 +"""Compile v.8 Fact Ledger candidate rows from BO, LES, signals, envelopes, and gate sidecars. + +순수 함수 모듈 — I/O·시각·난수 없음. 규칙 선언은 fact_ledger_policy.v1.json 이 유일 정본이며 +이 모듈은 그것을 읽어 dispatch 만 한다. gate_id·27필드·registry type_id 리터럴을 코드에 두지 +않는다(정적 검사 11·12). 사건 종류 이름을 어디에도 두지 않는다(137종 원칙). + +공개 API + normalize_gate_rules(sidecar_doc, policy) 17축 방언 정규화 리더 + resolve_alias_table(alias_declarations, sidecars, policy) + compile_fact_ledger_candidates(...) 조립 본체 (아래 시그니처) +""" +from __future__ import annotations + +import json +import re +from typing import Any + +_WS_RE = re.compile(r"\s+") +_CODE_RE = re.compile(r"^[A-Z][A-Z0-9_]{2,127}$") +_DOMAIN_RE = re.compile(r"^(?:(?:EC|E)-[A-Z0-9]{2,8}|X[A-Z0-9]{1,8})$") + + +# --------------------------------------------------------------------------- +# 공용 헬퍼 +# --------------------------------------------------------------------------- + +def _dict(value: Any) -> dict: + return value if isinstance(value, dict) else {} + + +def _list(value: Any) -> list: + return value if isinstance(value, list) else [] + + +def _strings(value: Any) -> list[str]: + return [item for item in _list(value) if isinstance(item, str)] + + +def _nonblank(value: Any) -> bool: + return value not in (None, "", [], {}) + + +def _text(value: Any, limit: int) -> str | None: + """키 오름차순 JSON -> 공백열 단일 공백 -> limit 절단 -> 빈 문자열이면 None.""" + if value is None: + return None + if isinstance(value, str): + rendered = value + else: + rendered = json.dumps(value, ensure_ascii=False, sort_keys=True) + rendered = _WS_RE.sub(" ", rendered).strip() + if not rendered: + return None + return rendered[:limit] + + +def _first_value(*values: Any) -> Any: + for value in values: + if _nonblank(value): + return value + return None + + +def _first_key(record: dict, keys: list[str]) -> Any: + for key in keys: + if _nonblank(record.get(key)): + return record[key] + return None + + +def _dedupe_keep_order(items: list[str]) -> list[str]: + seen: set[str] = set() + out: list[str] = [] + for item in items: + if item not in seen: + seen.add(item) + out.append(item) + return out + + +# --------------------------------------------------------------------------- +# F0-0c — BO 압축·정규화 (구본 _compact_bo 등가 계층. (가) 9필드 전부의 전제) +# --------------------------------------------------------------------------- + +def compact_bo(bo: dict, policy: dict) -> dict: + norm = _dict(policy.get("bo_normalization")) + key_map = _dict(norm.get("key_map")) + keep = _strings(norm.get("domain_payload_keep_keys")) + out: dict[str, Any] = {} + for key, value in bo.items(): + out[key_map.get(key, key)] = value + payload = _dict(_dict(bo.get("extensions")).get("domain_payload")) + out["domain_payload"] = {k: payload[k] for k in keep + if k in payload and _nonblank(payload[k])} + out["core"] = _dict(bo.get("core_field_base")) + return out + + +# --------------------------------------------------------------------------- +# (다) — 증거력 산정 (정책 evidence_scoring_da_2 의 5갈래 · 순서 그대로) +# --------------------------------------------------------------------------- + +def _flatten_evidence(document: Any, depth: int = 3) -> list[dict]: + rows: list[dict] = [] + + def visit(node: Any, level: int) -> None: + if level > depth: + return + if isinstance(node, dict): + if any(k in node for k in ("evidence_index", "id", "evidence_id", + "eid", "index", "doc_id")): + rows.append(node) + return + for value in node.values(): + visit(value, level + 1) + elif isinstance(node, list): + for item in node: + visit(item, level + 1) + + visit(document, 0) + return rows + + +def _evidence_id(record: dict) -> str | None: + value = _first_key(record, ["evidence_index", "id", "evidence_id", + "eid", "index", "doc_id"]) + return value if isinstance(value, str) else None + + +def _excerpt_from_indexed(record: dict) -> str: + facts = _strings(record.get("key_facts")) + if facts: + return " ".join(facts) + parts = _strings(record.get("key_dates")) + _strings(record.get("key_amounts")) + if parts: + return " ".join(parts) + for value in record.values(): + if isinstance(value, str) and value.strip(): + return value + return "" + + +def build_evidence_authority(evidence_document: Any) -> dict[str, dict]: + authority: dict[str, dict] = {} + for record in _flatten_evidence(evidence_document): + eid = _evidence_id(record) + if not eid: + continue + authority[eid] = { + "evidence_index": eid, + "title": _first_key(record, ["title", "Title", "name", "doc_title"]), + "excerpt": _excerpt_from_indexed(record), + "priority": _first_key(record, ["priority", "Priority", "weight"]), + "authentication": _first_key(record, ["authentication", "auth", + "source_authentication"]), + "source": "evidence_indexed.json", + } + return authority + + +def score_evidence(evidence_refs: list[str], authority: dict[str, dict], + policy: dict, reviews: list[dict], + fact_id: str) -> tuple[str, str, int]: + """(credibility, proof_strength, 절단여유 최소값) — 정책 5갈래 순서 그대로.""" + scoring = _dict(policy.get("evidence_scoring_da_2")) + markers = _strings(scoring.get("markers_documentary")) + rows: list[dict] = [] + for ref in evidence_refs: + row = authority.get(ref) + if row is None: + # 우주 밖 참조 — stub 도 비어 있지 않은 증거 행으로 계수된다(정책 승계). + reviews.append({"review_code": "EVIDENCE_REF_NOT_IN_EVIDENCE_UNIVERSE", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + row = {"evidence_index": ref, "missing": True} + rows.append(row) + if not rows: + return "low", "low", -1 + pieces: list[str] = [] + slack = 10 ** 9 + for row in rows: + rendered = _WS_RE.sub(" ", json.dumps(row, ensure_ascii=False, + sort_keys=True)).strip() + slack = min(slack, 300 - len(rendered)) + pieces.append(rendered[:300]) + text = " ".join(pieces) + if any(marker in text for marker in markers): + return "high", "documentary", slack + if len(rows) >= 2: + return "high", "high", slack + if ("meeting" in text) or ("회의" in text) or ("면담" in text): + return "medium", "meeting_only_core", slack + return "medium", "medium", slack + + +# --------------------------------------------------------------------------- +# F0-0 — 17축 사이드카 방언 정규화 리더 +# --------------------------------------------------------------------------- + +def normalize_gate_rules(sidecar_doc: Any, policy: dict) -> dict: + """사이드카 한 파일을 정규화한다. {rules: {gate_id: norm}, file_meta, warnings}.""" + axes = _dict(_dict(policy.get("gate_execution")).get("normalization_axes_17")) + array_keys = _strings(axes.get("rule_array_keys")) or ["rules", "gates"] + code_keys = _strings(axes.get("emitted_code_keys")) + doc = _dict(sidecar_doc) + raw_rules: list[dict] = [] + array_key = None + for key in array_keys: + if isinstance(doc.get(key), list): + array_key = key + raw_rules = [r for r in doc[key] if isinstance(r, dict)] + break + warnings: list[dict] = [] + norm_rules: dict[str, dict] = {} + for rule in raw_rules: + gate_id = rule.get("gate_id") + if not isinstance(gate_id, str): + warnings.append({"code": "GATE_RULE_WITHOUT_ID", "detail": str(rule)[:120]}) + continue + writer = _dict(rule.get("writer_effect")) + row_eff = _dict(rule.get("row_effect")) + on_true = _dict(rule.get("on_true")) + condition = rule.get("condition") + cond_kind = "dict" if isinstance(condition, dict) else "str" + cond_key = next(iter(condition), None) if cond_kind == "dict" else None + cond_value = condition.get(cond_key) if cond_kind == "dict" else condition + emitted: list[str] = [] + for key in code_keys: + value = rule.get(key) + if isinstance(value, str): + emitted.append(value) + else: + emitted.extend(_strings(value)) + blocks = None + for container, key in (("writer_effect", "final_drafting_block"), + ("writer_effect", "blocks_final_drafting"), + ("on_true", "blocks_final_drafting")): + holder = writer if container == "writer_effect" else on_true + if key in holder: + blocks = bool(holder[key]) + break + status_fire = (writer.get("status_when_triggered") + or writer.get("status_on_fire") + or on_true.get("writer_status")) + evaluation = (rule.get("evaluation_mode") or rule.get("implementation_type") + or rule.get("evaluation")) + norm_rules[gate_id] = { + "gate_id": gate_id, + "condition_kind": cond_kind, + "condition_key": cond_key, + "condition_value": cond_value, + "emitted_codes": _dedupe_keep_order(emitted), + "blocks_final_drafting": blocks, + "status_when_triggered": status_fire, + "row_must_consider": bool(row_eff.get("must_consider")), + "preserve_seed": bool(on_true.get("preserve_seed")), + "unknown_or_missing_policy": rule.get("unknown_or_missing_policy"), + "evaluation": evaluation, + "writes_fact_ledger": writer.get("writes_fact_ledger"), + "silent_repair_forbidden": rule.get("silent_repair_forbidden"), + "source_membership_required": rule.get("source_membership_required"), + "input_declaration": rule.get("input_fields") or rule.get("input_contracts"), + } + if doc.get("global_status_rule") is not None: + warnings.append({"code": "SIDECAR_GLOBAL_STATUS_RULE_IGNORED", + "detail": str(doc.get("global_status_rule"))[:120]}) + return {"rules": norm_rules, + "file_meta": {"array_key": array_key, + "schema_version": doc.get("schema_version"), + "status_key": ("closure_status" if "closure_status" in doc + else ("status" if "status" in doc else None))}, + "warnings": warnings} + + +# --------------------------------------------------------------------------- +# F0-0b — 별칭 해소 (D-P4-7 — 전수 자기선언 대조 · 침묵 탈락 금지) +# --------------------------------------------------------------------------- + +def resolve_alias_table(alias_declarations: Any, structure_type_sidecars: dict, + policy: dict) -> dict: + root = _dict(alias_declarations) + root = _dict(root.get("extension_payload_key_declarations", root)) + table = {k: _strings(v) for k, v in _dict(root.get("legacy_alias_targets")).items()} + derived_mismatches = _list(root.get("legacy_declaration_mismatches")) + abbrev = _dict(_dict(policy.get("legacy_alias")).get("abbreviation_map")) + self_decl: dict[str, set] = {} + for domain_id, doc in sorted(_dict(structure_type_sidecars).items()): + doc = _dict(doc) + rows = None + for key in ("types", "structure_types", "records", "entries"): + if isinstance(doc.get(key), list): + rows = doc[key] + break + for row in rows or []: + if not isinstance(row, dict): + continue + value = row.get("legacy_source") or row.get("legacy_source_domain") + if not isinstance(value, str): + continue + head = value.split("_")[0] + if head not in abbrev: + continue # 'Part3 fallback' 등 비 B* 값은 선언이 아니다 + self_decl.setdefault(abbrev[head], set()).add(domain_id) + reviews: list[dict] = [] + mismatch_map: dict[str, dict] = {} + for legacy in sorted(set(table) | set(self_decl)): + declared = self_decl.get(legacy, set()) + aliased = set(table.get(legacy, [])) + missing = sorted(declared - aliased) + excess = sorted(aliased - declared) + if missing: + mismatch_map[legacy] = {"missing": missing, "excess": excess} + reviews.append({"review_code": "LEGACY_ALIAS_SELF_MISMATCH_REVIEW", + "severity": "SOFT_WARNING", "legacy": legacy, + "missing_domain_ids": missing, + "excess_domain_ids": excess}) + derived_missing = sorted({str(_dict(m).get("domain_id")) + for m in derived_mismatches if _dict(m).get("domain_id")}) + actual_missing = sorted({d for m in mismatch_map.values() for d in m["missing"]}) + if derived_missing != actual_missing: + reviews.append({"review_code": "LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", + "derived_list": derived_missing, + "actual_missing": actual_missing}) + return {"table": table, "self_declarations": {k: sorted(v) for k, v in self_decl.items()}, + "mismatches": mismatch_map, "reviews": reviews} + + +# --------------------------------------------------------------------------- +# 조립 본체 +# --------------------------------------------------------------------------- + +def compile_fact_ledger_candidates( + bo_document: Any, + les_document: Any, + signal_documents: dict, + domain_envelopes: dict, + p2_handoff_document: Any, + evidence_document: Any, + registry_index: Any, + domain_configs: dict, + gate_sidecars: dict, + structure_type_sidecars: dict, + activation_document: Any, + alias_declarations: Any, + policy: dict) -> dict: + reviews: list[dict] = [] + audit: dict[str, Any] = {} + + activation_root = _dict(activation_document) + activation_root = _dict(activation_root.get("domain_activation_manifest", + activation_root)) + expected_runnable = _strings(activation_root.get("expected_runnable_domain_ids")) + + # ---- F0-0 리더 · F0-0b 별칭 ---------------------------------------- + normalized: dict[str, dict] = {} + dialect_counts: dict[str, Any] = {"array_key": {}, "files": 0} + reader_warnings: list[dict] = [] + for domain_id, sidecar in sorted(_dict(gate_sidecars).items()): + result = normalize_gate_rules(sidecar, policy) + normalized[domain_id] = result + dialect_counts["files"] += 1 + ak = result["file_meta"]["array_key"] + dialect_counts["array_key"][ak] = dialect_counts["array_key"].get(ak, 0) + 1 + reader_warnings.extend(result["warnings"]) + audit["sidecar_dialects"] = dialect_counts + audit["reader_warnings"] = reader_warnings + + alias = resolve_alias_table(alias_declarations, structure_type_sidecars, policy) + reviews.extend(alias["reviews"]) + + # ---- LES 적재 ------------------------------------------------------- + les_root = _dict(les_document) + structure_records = [r for r in _list(les_root.get("structure_records")) + if isinstance(r, dict)] + record_by_id = {r.get("structure_id"): r for r in structure_records + if isinstance(r.get("structure_id"), str)} + index = _dict(les_root.get("structure_index")) + by_bo_id = {k: _strings(v) for k, v in _dict(index.get("by_bo_id")).items()} + + # ---- SG 적재 -------------------------------------------------------- + def signal_records(sg: str) -> list[dict]: + doc = _dict(_dict(signal_documents).get(sg)) + return [r for r in _list(doc.get("records")) if isinstance(r, dict)] + + sg11_records = signal_records("SG-11") + sg11_by_signal_id = {r.get("signal_id"): r for r in sg11_records + if isinstance(r.get("signal_id"), str)} + sg04_by_domain: dict[str, list[str]] = {} + for record in signal_records("SG-04"): + for domain_id in _strings(record.get("domain_ids")): + if isinstance(record.get("signal_id"), str): + sg04_by_domain.setdefault(domain_id, []).append(record["signal_id"]) + + # ---- F0-7b — 게이트 입력 정규화 ------------------------------------ + handoff_root = _dict(p2_handoff_document) + handoff_root = _dict(handoff_root.get("stage1_part2_review_handoff", handoff_root)) + handoff_items = [r for r in _list(handoff_root.get("review_items")) + if isinstance(r, dict)] + p2_rules = _dict(policy.get("p2_handoff_rules")) + code_pool: dict[str, list[str]] = {} + gate_input_counts = {"envelope_review_items": 0, "handoff_codes": 0, + "handoff_not_a_code": 0, "handoff_domain_unresolved": 0, + "slot_entries": 0, "operand_states": 0} + for domain_id in expected_runnable: + env = _dict(_dict(domain_envelopes).get(domain_id)) + env = _dict(env.get("domain_signal_envelope", env)) + for item in _list(env.get("review_items")): + code = _dict(item).get("review_code") or _dict(item).get("code") + if isinstance(code, str) and _CODE_RE.match(code): + code_pool.setdefault(domain_id, []).append(code) + gate_input_counts["envelope_review_items"] += 1 + for item in handoff_items: + raw_code = item.get("source_review_code") + if not (isinstance(raw_code, str) and _CODE_RE.match(raw_code)): + if raw_code is not None: + gate_input_counts["handoff_not_a_code"] += 1 + reviews.append({"review_code": "P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW", + "severity": "SOFT_WARNING", + "detail": str(raw_code)[:80]}) + continue + source_domain = item.get("source_domain") + targets: list[str] = [] + if isinstance(source_domain, str): + if _DOMAIN_RE.match(source_domain): + targets = [source_domain] + elif source_domain in alias["table"]: + targets = alias["table"][source_domain] + if not targets: + gate_input_counts["handoff_domain_unresolved"] += 1 + reviews.append({"review_code": "P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW", + "severity": "SOFT_WARNING", + "detail": str(source_domain)[:80]}) + continue + for domain_id in targets: + code_pool.setdefault(domain_id, []).append(raw_code) + gate_input_counts["handoff_codes"] += 1 + slot_status: dict[str, list[dict]] = {} + for domain_id in expected_runnable: + env = _dict(_dict(domain_envelopes).get(domain_id)) + env = _dict(env.get("domain_signal_envelope", env)) + entries = [e for e in _list(env.get("evidence_slot_status")) + if isinstance(e, dict)] + if entries: + slot_status[domain_id] = entries + gate_input_counts["slot_entries"] += len(entries) + operand_by_domain: dict[str, list[str]] = {} + for record in sg11_records: + details = _dict(record.get("details")) + state = record.get("operand_state") or details.get("operand_state") + if not isinstance(state, str): + continue + for domain_id in (_strings(record.get("domain_ids")) + or _strings(details.get("domain_ids"))): + operand_by_domain.setdefault(domain_id, []).append(state) + gate_input_counts["operand_states"] += 1 + audit["gate_input_counts"] = gate_input_counts + + # ---- F0-1 ~ F0-6b — 행 생성 ---------------------------------------- + bo_records = [b for b in _list(bo_document) if isinstance(b, dict)] + bo_sorted = sorted(bo_records, key=lambda b: str(b.get("BO_ID") or "")) + authority = build_evidence_authority(evidence_document) + calc_types = set(_strings(policy.get("legal_calculation_object_types"))) + na_defaults = _dict(policy.get("field_synthesis_na_15")) + rows: list[dict] = [] + attach_stats = {"attached_bo": 0, "unattached_bo": 0} + used_structure_ids: set[str] = set() + used_sg11: set[str] = set() + slack_min = 10 ** 9 + for idx, bo in enumerate(bo_sorted, 1): + compact = compact_bo(bo, policy) + core = compact["core"] + fact_id = "F-%03d" % idx + bo_id = str(bo.get("BO_ID") or "") + # (가) 9필드 + parties = [] + if _nonblank(core.get("Performer")): + parties.append({"role": "actor", "value": _text(core.get("Performer"), 160), + "source": "BO.core_field_base"}) + if _nonblank(core.get("Subject")): + parties.append({"role": "counterparty", + "value": _text(core.get("Subject"), 160), + "source": "BO.core_field_base"}) + amount = _text(_first_value(compact.get("amount"), core.get("Amount")), 240) + action = _first_value(compact.get("action"), core.get("Action")) + evidence_refs = _strings(bo.get("source_evidence_indexes")) + # F0-3 — LES 조인 + structure_ids = sorted(set(by_bo_id.get(bo_id, []))) + attached = [record_by_id[s] for s in structure_ids if s in record_by_id] + if attached: + attach_stats["attached_bo"] += 1 + else: + attach_stats["unattached_bo"] += 1 + reviews.append({"review_code": "FACT_ROW_UNATTACHED_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": bo_id}) + used_structure_ids.update(s for s in structure_ids if s in record_by_id) + domain_effects: dict[str, dict] = {} + for record in attached: + domain_id = record.get("domain_id") + if not isinstance(domain_id, str): + continue + slot = domain_effects.setdefault(domain_id, { + "module": set(), "role_projection": set(), "structure_ids": set(), + "type_ids": set(), "review_codes": set()}) + if isinstance(record.get("module"), str): + slot["module"].add(record["module"]) + if isinstance(record.get("role_projection"), str): + slot["role_projection"].add(record["role_projection"]) + if isinstance(record.get("structure_id"), str): + slot["structure_ids"].add(record["structure_id"]) + if isinstance(record.get("type_id"), str): + slot["type_ids"].add(record["type_id"]) + slot["review_codes"].update( + c for c in _strings(record.get("review_codes")) if _CODE_RE.match(c)) + domain_effects = {d: {k: sorted(v) for k, v in slot.items()} + for d, slot in sorted(domain_effects.items())} + modules = sorted({m for slot in domain_effects.values() for m in slot["module"]}) + roles = sorted({r for slot in domain_effects.values() + for r in slot["role_projection"]}) + type_ids = sorted({t for slot in domain_effects.values() + for t in slot["type_ids"]}) + # F0-5 — SG-11 조인 + operand_state 승계 (재산정 금지) + calculation_requests: list[dict] = [] + route_refs: list[str] = [] + for record in attached: + for ref in _strings(record.get("calculation_request_refs")): + route_refs.append(ref) + sg = sg11_by_signal_id.get(ref) + if sg is None: + calculation_requests.append({"calculation_id": None, + "request_id": ref, + "provenance": {"signal_id": ref, + "dangling": True}}) + reviews.append({"review_code": "CALCULATION_REF_DANGLING_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + continue + used_sg11.add(ref) + details = _dict(sg.get("details")) + calc_id = details.get("calculation_id") + if not isinstance(calc_id, str) or not calc_id: + calc_id = ref + reviews.append({"review_code": "CALCULATION_ID_SUBSTITUTED_REVIEW", + "severity": "SOFT_WARNING", "fact_id": fact_id, + "detail": ref}) + law_refs = sorted(set( + _strings(sg.get("law_version_refs")) + + _strings(details.get("law_version_refs")) + + [ref2 for d in _strings(sg.get("domain_ids")) + for ref2 in sg04_by_domain.get(d, [])])) + calculation_requests.append({ + "calculation_id": calc_id, + "request_id": ref, + "operand_state": (sg.get("operand_state") + or details.get("operand_state")), + "missing_operands": _strings(details.get("missing_operands")), + "law_version_refs": law_refs, + "provenance": {"signal_id": ref, + "structure_ids": sorted( + s for s in structure_ids + if ref in _strings( + _dict(record_by_id.get(s)).get( + "calculation_request_refs")))}, + }) + # (다) 증거력 + credibility, proof_strength, slack = score_evidence( + evidence_refs, authority, policy, reviews, fact_id) + slack_min = min(slack_min, slack) + # (나) 15필드 + object_spec = _text(core.get("Object"), 160) + calc_obj_candidates = sorted(calc_types & set(type_ids)) + legal_calculation_object = calc_obj_candidates[0] if calc_obj_candidates else None + alias_vocab = _dict(policy.get("alias_module_vocabulary")) + mod_actio = alias_vocab.get("actio_fields") + mod_money = alias_vocab.get("money_claim_effect") + mod_successor = alias_vocab.get("commercial_successor_effect") + actio_ids = sorted({s for slot in domain_effects.values() + for s in slot["structure_ids"] + if mod_actio in slot["module"]}) + actio_roles = sorted({r for slot in domain_effects.values() + if mod_actio in slot["module"] + for r in slot["role_projection"]}) + money_ids = sorted({s for slot in domain_effects.values() + if mod_money in slot["module"] + for s in slot["structure_ids"]}) + successor_ids = sorted({s for slot in domain_effects.values() + if mod_successor in slot["module"] + for s in slot["structure_ids"]}) + row = { + "fact_id": fact_id, + "source_bo_id": bo_id, + "type": compact.get("bo_type") if isinstance(compact.get("bo_type"), str) else None, + "date": _first_value(core.get("BehaviorTime"), compact.get("date")), + "parties": parties, + "object_spec": object_spec, + "amount": amount, + "action": action if isinstance(action, str) else _text(action, 240), + "evidence_refs": evidence_refs, + "credibility": credibility, + "legal_centrality": "high" if attached else "medium", + "proof_strength": proof_strength, + "legal_effect_roles": roles, + "claim_chain_ref": {"claim_group_ids": [], "liability_group_ids": [], + "signal_count": len(route_refs)}, + "linked_structures": {"structure_ids": structure_ids}, + "must_not_drop_in_claim_types": modules, + "must_consider": bool(attached), + "state_context": {"bo_type": compact.get("bo_type") + if isinstance(compact.get("bo_type"), str) else None, + "domain_ids": sorted(domain_effects)}, + "money_claim_effect": {"enabled": bool(money_ids), + "structure_ids": money_ids}, + "commercial_successor_effect": {"enabled": bool(successor_ids), + "structure_ids": successor_ids}, + "actio_roles": actio_roles, + "linked_actio_structures": actio_ids, + "downstream_module_candidates": modules, + "skeleton_validation_codes": [], + "derived_fact": action if isinstance(action, str) else _text(action, 240), + "derivation_basis": ("structure_ids=" + ",".join(structure_ids) + if structure_ids else "bo_only"), + "legal_calculation_object": legal_calculation_object, + "domain_effects": domain_effects, + "calculation_requests": calculation_requests, + } + rows.append(row) + audit["evidence_truncation_slack_min"] = (None if slack_min == 10 ** 9 else slack_min) + + # ---- F0-8 — 게이트 합집합 실행 (§4.4 그대로) ------------------------ + gate_exec = _dict(policy.get("gate_execution")) + implemented = set(_strings(gate_exec.get("implemented_condition_keys"))) + default_missing_policy = str(gate_exec.get("unknown_or_missing_policy_default") + or "fail_open_to_review") + declared_gates: dict[str, dict] = {} + for domain_id in expected_runnable: + config = _dict(_dict(domain_configs).get(domain_id)) + config_root = _dict(config.get("domain_config", config)) + for gate in _list(config_root.get("gates")): + gate = _dict(gate) + gate_id = gate.get("gate_id") + if isinstance(gate_id, str): + declared_gates[gate_id] = {"domain_id": domain_id, "declared": gate} + all_rules: dict[str, dict] = {} + for domain_id, result in normalized.items(): + for gate_id, rule in result["rules"].items(): + all_rules[gate_id] = {"domain_id": domain_id, "rule": rule} + gate_report: list[dict] = [] + fired_by_domain: dict[str, list[dict]] = {} + for gate_id in sorted(declared_gates): + info = declared_gates[gate_id] + domain_id = info["domain_id"] + declared = info["declared"] + registry_codes = [c for c in _strings(declared.get("review_codes")) + if _CODE_RE.match(c)] + entry: dict[str, Any] = {"gate_id": gate_id, "domain_id": domain_id, + "fired": False, "input_state": "n/a"} + rule_info = all_rules.get(gate_id) + if rule_info is None: + entry["disposition"] = "GATE_SPEC_NOT_DEPLOYED_REVIEW" + reviews.append({"review_code": "GATE_SPEC_NOT_DEPLOYED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + gate_report.append(entry) + continue + rule = rule_info["rule"] + fired = False + if rule["condition_kind"] == "dict": + key = rule["condition_key"] + if key in implemented: + entry["disposition"] = "implemented" + value = rule["condition_value"] + if key == "any_review_code_present": + pool = code_pool.get(domain_id) + if pool is None and domain_id not in _dict(domain_envelopes): + entry["input_state"] = "input_missing" + entry["missing_policy"] = ( + rule["unknown_or_missing_policy"] or default_missing_policy) + else: + entry["input_state"] = "supplied" + fired = bool(set(_strings(value)) & set(pool or [])) + elif key == "required_slots_missing_or_conflicting": + entries = slot_status.get(domain_id) + if entries is None and domain_id not in _dict(domain_envelopes): + entry["input_state"] = "input_missing" + entry["missing_policy"] = ( + rule["unknown_or_missing_policy"] or default_missing_policy) + else: + entry["input_state"] = "supplied" + wanted = set(_strings(value)) + fired = any( + (e.get("slot_id") in wanted) + and (e.get("status") in ("missing", "conflicted")) + for e in entries or []) + elif key == "calculation_operand_state_in": + states = operand_by_domain.get(domain_id) + entry["input_state"] = ("supplied" if states else "input_missing") + fired = bool(set(_strings(value)) & set(states or [])) + else: + entry["disposition"] = "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW" + entry["condition_key"] = key + reviews.append({"review_code": "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": str(key)}) + else: + entry["disposition"] = "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW" + entry["predicate_spec"] = _text(rule["condition_value"], 200) + reviews.append({"review_code": "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": _text(rule["condition_value"], 160)}) + # 차단 표지 대조 — registry ↔ 정규화된 사이드카. + declared_blocks = declared.get("blocks_final_drafting") + if (rule["blocks_final_drafting"] is not None + and declared_blocks is not None + and bool(declared_blocks) != rule["blocks_final_drafting"]): + reviews.append({"review_code": "GATE_BLOCK_FLAG_MISMATCH_REVIEW", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + entry["block_flag_mismatch"] = True + emitted = _dedupe_keep_order(rule["emitted_codes"] + registry_codes) + excess = [c for c in rule["emitted_codes"] if c not in registry_codes] + if excess: + entry["codes_not_in_registry"] = excess + reviews.append({"review_code": "GATE_EMITTED_CODE_NOT_IN_REGISTRY", + "severity": "SOFT_WARNING", "gate_id": gate_id, + "detail": ",".join(excess)[:160]}) + entry["fired"] = fired + entry["blocks_final_drafting"] = (rule["blocks_final_drafting"] + if rule["blocks_final_drafting"] is not None + else bool(declared_blocks)) + if fired: + projection_rows = [r["fact_id"] for r in rows + if domain_id in r["domain_effects"]] + entry["projected_row_count"] = len(projection_rows) + fired_by_domain.setdefault(domain_id, []).append({ + "gate_id": gate_id, "codes": emitted, + "row_must_consider": rule["row_must_consider"], + "preserve_seed": rule["preserve_seed"], + "blocks_final_drafting": entry["blocks_final_drafting"], + "status_when_triggered": rule["status_when_triggered"], + "projected_rows": projection_rows, + }) + gate_report.append(entry) + orphan_rules = sorted(set(all_rules) - set(declared_gates)) + for gate_id in orphan_rules: + reviews.append({"review_code": "GATE_RULE_NOT_IN_REGISTRY", + "severity": "SOFT_WARNING", "gate_id": gate_id}) + + # ---- 행 투영 3분기 + F0-8b 스켈레톤 -------------------------------- + skeleton_order = _strings(_dict(policy.get("skeleton_checks")).get("order")) + for row in rows: + codes: list[str] = [] + if not _nonblank(row["derived_fact"]): + codes.append(skeleton_order[0]) + if not row["evidence_refs"]: + codes.append(skeleton_order[1]) + if not row["linked_structures"]["structure_ids"]: + codes.append(skeleton_order[2]) + gate_codes: list[str] = [] + for domain_id in sorted(row["domain_effects"]): + for firing in sorted(fired_by_domain.get(domain_id, []), + key=lambda f: f["gate_id"]): + gate_codes.extend(firing["codes"]) + if firing["row_must_consider"]: + row["must_consider"] = row["must_consider"] or True + row["skeleton_validation_codes"] = _dedupe_keep_order(codes + gate_codes) + + # ---- STRUCTURE_UNATTACHED_REVIEW ----------------------------------- + unattached_structures = sorted(set(record_by_id) - used_structure_ids) + for structure_id in unattached_structures: + reviews.append({"review_code": "STRUCTURE_UNATTACHED_REVIEW", + "severity": "SOFT_WARNING", "detail": structure_id}) + + # ---- F0-9 — 예외 pack ---------------------------------------------- + pack_policy = _dict(policy.get("exception_pack")) + char_limit = int(pack_policy.get("pack_item_char_limit") or 2000) + exceptions: list[dict] = [] + for row in rows: + if row["legal_calculation_object"] is None: + continue + bo = next((b for b in bo_sorted + if str(b.get("BO_ID") or "") == row["source_bo_id"]), {}) + core = _dict(bo.get("core_field_base")) + amount_candidates = [v for v in (bo.get("amount"), core.get("Amount")) + if _nonblank(v)] + if len(amount_candidates) >= 2: + payload = {"amount_candidates": [_text(v, 240) for v in amount_candidates], + "object_spec": row["object_spec"], + "calculation_request_ids": sorted( + str(c.get("calculation_id")) for c in + row["calculation_requests"] if c.get("calculation_id"))} + item = {"exception_id": "FX-%03d" % (len(exceptions) + 1), + "exception_type": "legal_calculation_required", + "fact_id": row["fact_id"], + "source_bo_id": row["source_bo_id"], + "conflict_flags": ["CALCULATION_BASIS_CONFLICT"], + "compact_payload": payload} + rendered = json.dumps(item, ensure_ascii=False) + if len(rendered) > char_limit: + item["oversize_review"] = True + reviews.append({"review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", + "fact_id": row["fact_id"], + "detail": "pack item oversize %d>%d" % (len(rendered), + char_limit)}) + exceptions.append(item) + elif (not _nonblank(row["amount"])) or (not _nonblank(row["object_spec"])): + reviews.append({"review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "severity": "SOFT_WARNING", "fact_id": row["fact_id"], + "detail": "amount_or_object_spec_blank"}) + unreachable = {name for name, meta in + _dict(policy.get("f1_exception_types")).items() + if _dict(meta).get("unreachable_in_v8")} + for item in exceptions: + if item["exception_type"] in unreachable: + raise ValueError(json.dumps({ + "reason_code": "PART4_UNREACHABLE_EXCEPTION_TYPE", + "exception_type": item["exception_type"]}, ensure_ascii=False)) + + # ---- F0-10 — 보존 등식 계수 ---------------------------------------- + effect_left = sum(len(r["domain_effects"]) for r in rows) + effect_right = 0 + calc_right = 0 + for bo in bo_sorted: + bo_id = str(bo.get("BO_ID") or "") + attached = [record_by_id[s] for s in by_bo_id.get(bo_id, []) + if s in record_by_id] + effect_right += len({r.get("domain_id") for r in attached + if isinstance(r.get("domain_id"), str)}) + calc_right += len({ref for r in attached + for ref in _strings(r.get("calculation_request_refs"))}) + calc_left = sum(len(r["calculation_requests"]) for r in rows) + sg11_total = len(sg11_records) + sg11_unused = sg11_total - len(used_sg11) + equations = { + "row": {"left": len(rows), "right": len(bo_sorted), + "holds": len(rows) == len(bo_sorted)}, + "attachment_coverage": { + "attached": attach_stats["attached_bo"], + "unattached": attach_stats["unattached_bo"], + "total": len(bo_sorted), + "holds": (attach_stats["attached_bo"] + attach_stats["unattached_bo"] + == len(bo_sorted))}, + "effect": {"left": effect_left, "right": effect_right, + "holds": effect_left == effect_right}, + "calc_row": {"left": calc_left, "right": calc_right, + "holds": calc_left == calc_right}, + "calc_source": {"attached_distinct": len(used_sg11), + "unattached_distinct": sg11_unused, + "total": sg11_total, + "holds": len(used_sg11) + sg11_unused == sg11_total}, + } + + # ---- 상태·집계 ------------------------------------------------------ + les_review_codes = set(_strings(policy.get("les_inherited_review_codes"))) + les_review_present = any( + (set(slot["review_codes"]) & les_review_codes) + for row in rows for slot in row["domain_effects"].values()) or any( + _dict(r).get("review_required") for r in structure_records) + gate_fired_any = any(f for firings in fired_by_domain.values() for f in firings) + status = ("READY_WITH_REVIEW" + if (reviews or exceptions or les_review_present or gate_fired_any) + else "READY") + counts = { + "rows": len(rows), + "bo_records": len(bo_sorted), + "structure_records": len(structure_records), + "gates_declared": len(declared_gates), + "gates_implemented": sum(1 for e in gate_report + if e.get("disposition") == "implemented"), + "gates_visualized_str": sum( + 1 for e in gate_report + if e.get("disposition") == "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW"), + "gates_visualized_unsupported": sum( + 1 for e in gate_report + if e.get("disposition") == "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW"), + "gates_fired": sum(1 for e in gate_report if e.get("fired")), + "orphan_rules": len(orphan_rules), + "review_items": len(reviews), + "exceptions": len(exceptions), + "conservation": equations, + } + bundle = { + "schema_version": "stage1_fact_ledger_candidate_bundle.v1", + "status": status, + "rows": rows, + "gate_report": {"gates": gate_report, + "fired_by_domain": {d: f for d, f in + sorted(fired_by_domain.items())}, + "orphan_rule_ids": orphan_rules}, + "counts": counts, + "signal_manifest_transaction_id": None, + "expected_runnable_domain_ids": expected_runnable, + "alias_resolution": {"table": alias["table"], + "self_declarations": alias["self_declarations"], + "mismatches": alias["mismatches"]}, + "created_by": "fact_ledger_compiler", + } + pack = { + "schema_version": "stage1_fact_exception_pack.v1", + "has_exceptions": bool(exceptions), + "exception_count": len(exceptions), + "exceptions": exceptions, + "budget": {"pack_item_char_limit": char_limit}, + } + audit["unattached_structure_ids"] = unattached_structures + audit["equations"] = equations + return {"fact_ledger_candidate_bundle": bundle, + "fact_exception_pack": pack, + "review_items": reviews, + "audit": audit} diff --git a/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/stage1_runtime/fact_ledger_policy.v1.json b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/stage1_runtime/fact_ledger_policy.v1.json new file mode 100644 index 00000000..8d428c5e --- /dev/null +++ b/Case_02_Comparison_Research/YAML_Prompts/1. Stage_1/v.7/extension_research/선행구축/Default_Agent_Stage_1/stage1_runtime/fact_ledger_policy.v1.json @@ -0,0 +1,401 @@ +{ + "schema_version": "stage1_fact_ledger_policy.v1", + "status": "ACTIVE", + "purpose": "Part 4 Fact Ledger 의 규칙 선언 정본. 27필드 목록·순서·합성 규칙, 증거력 산정, 게이트 방언 정규화와 구현 범위, 별칭 파생, 보존 등식, 루트 래핑을 코드 밖에 둔다. 코드는 이 선언을 읽어 dispatch 만 한다. registry 가 말하지 않는 값은 만들지 않는다 — 137종 사건 이름을 어디에도 두지 않는다.", + "authority": { + "strategy": "stage_1_part_4_개정전략서.md", + "row_baseline": "선행구축/S_2_GPT_v2/fixtures/legacy_equivalence/run_{1,2}/fact_ledger/Fact_Ledger_base.json (59행 x 27키 실측)", + "gate_sidecars": "domains//gates.json x25 (조립본 바이트 동일 복사 · 127규칙 · registry 121 전건 커버)", + "case_kind_names_forbidden": true + }, + "final_fields_27": [ + "fact_id", + "source_bo_id", + "type", + "date", + "parties", + "object_spec", + "amount", + "action", + "evidence_refs", + "credibility", + "legal_centrality", + "proof_strength", + "legal_effect_roles", + "claim_chain_ref", + "linked_structures", + "must_not_drop_in_claim_types", + "must_consider", + "state_context", + "money_claim_effect", + "commercial_successor_effect", + "actio_roles", + "linked_actio_structures", + "downstream_module_candidates", + "skeleton_validation_codes", + "derived_fact", + "derivation_basis", + "legal_calculation_object" + ], + "new_fields_2": [ + "domain_effects", + "calculation_requests" + ], + "new_fields_note": "29키 = 구 27 + 신규 2. 신규 2키는 required 로 올리지 않는다(D-P4-4 · Stage 2 호환). domain_effects 의 module·role_projection 은 정렬된 배열이다 — 한 도메인이 복수 module 을 선언할 수 있어 스칼라로 두면 정보가 소실된다(D-P4-e).", + "fact_id_format": "F-{idx:03d}", + "fact_id_rule": "BO_ID 사전순 1-기반 연속. 연속성은 F2 가 경성 단언한다.", + "row_cardinality": "BO 1건 = 1행", + "bo_normalization": { + "key_map": { + "BOType": "bo_type", + "Action": "action", + "BO_ID": "bo_id" + }, + "domain_payload_keep_keys": [ + "money_claim_details", + "possession_state", + "lien_state", + "notice_lifecycle", + "asset_alias", + "fractional_effect_events" + ], + "nonblank_only": true, + "note": "구본 _compact_bo(369-374행) 등가 계층. 정규화 없이 원본 대문자 키를 소문자로 찾으면 전건 None 이 된다 — (가) 9필드 전부의 전제." + }, + "field_synthesis_ga_9": { + "note": "(가) BO 유래 9필드 — 골든 59행 전건 값등가 요구. 우변을 바꾸면 정상 구현에서 G-r 이 깨진다.", + "fact_id": "F-{idx:03d} (BO_ID 사전순 1-기반)", + "source_bo_id": "BO_ID 그대로", + "type": "compact.bo_type 단독 (LES types[0] 폴백 금지)", + "date": "first_value(core_field_base.BehaviorTime, bo.date) — bo.date 는 BO 18키에 없어 항상 None. 골든은 BehaviorTime 0/59 라 전건 null", + "parties": "core_field_base 단독 — Performer -> role: actor · Subject -> role: counterparty · 값은 text(party, 160) · source: 'BO.core_field_base'. 구 signal 경로 폐지", + "amount": "text(first_value(bo.amount, core_field_base.Amount), 240) or None — 키 오름차순 JSON(ensure_ascii=False) -> 공백열 단일 공백 정규화 -> 240자 절단 -> 빈 문자열이면 None", + "action": "first_value(compact.action, core_field_base.Action) — 정규화 계층 통과 후의 소문자 키", + "evidence_refs": "BO source_evidence_indexes 단독 — LES 증거는 합집합하지 않는다(합집합하면 v.8 에서 깨진다). LES 증거는 domain_effects 쪽에만 싣는다", + "derived_fact": "action 복제 단독 — 구본 4항 서술 분기는 삭제 확정(deprecation 절)" + }, + "field_synthesis_na_15": { + "note": "(나) LES 유래 15필드 — 골든 등가를 요구하지 않는다(EXPECTED_DELTA-04). v.8 LES 부착에서 결정론으로 재산출한다. 부착 0 이면 아래 기본값이 그대로 남는다.", + "object_spec": { + "rule": "core_field_base.Object 단독 · 없으면 null · 폴백 없음", + "decision": "D-P4-d=(가) — LES 폴백 대상(asset_cluster_ids)이 Part 3 v.8 에서 소멸했으므로 BO 단독 원천으로 확정한다" + }, + "legal_centrality": "부착 있으면 high · 없으면 medium", + "legal_effect_roles": "부착 레코드 role_projection 정렬 합집합 · 기본 []", + "claim_chain_ref": "{claim_group_ids: [], liability_group_ids: [], signal_count: 부착 route signal 수} — 구 그룹 ID 는 v.8 에 존재하지 않으므로 상시 빈 배열", + "linked_structures": "{structure_ids: 정렬 합집합} · 기본 {structure_ids: []}", + "must_not_drop_in_claim_types": "부착 레코드 module 정렬 합집합 · 기본 []", + "must_consider": "1차 = 부착 존재 여부(bool) · 게이트 행 투영이 OR 로 갱신한다 — (나)∩(라) 겸용 필드", + "state_context": "{bo_type, domain_ids: domain_effects 키 정렬} · 기본 {bo_type, domain_ids: []}", + "money_claim_effect": "{enabled: module 'money_claim' 부착 여부, structure_ids: 해당 정렬 목록}", + "commercial_successor_effect": "{enabled: module 'commercial_successor' 부착 여부, structure_ids: 해당 정렬 목록}", + "actio_roles": "module 'actio' 부착 레코드의 role_projection 정렬 합집합 · 기본 []", + "linked_actio_structures": "module 'actio' 부착 레코드의 structure_id 정렬 목록 · 기본 []", + "downstream_module_candidates": "must_not_drop_in_claim_types 와 같은 파생(골든 59/59 동일값 실측 승계)", + "derivation_basis": "부착 있으면 'structure_ids=' + 콤마결합 · 없으면 'bo_only'", + "legal_calculation_object": "부착 type_id ∩ legal_calculation_object_types 의 사전순 최소 · 없으면 null" + }, + "legal_calculation_object_types": [ + "fraudulent_transfer", + "land_use_gain", + "money_claim", + "secured_debt", + "valuation" + ], + "legal_calculation_object_note": "구본 677행 5-원소 집합 승계(D-P4-1 값 이관). calculation_bindings 비공백 도메인으로 대체하면 항등식으로 퇴화한다(라운드 12 실측 — 25도메인 x type_id 56종 전건).", + "evidence_scoring_da_2": { + "note": "(다) credibility·proof_strength — 골든 59행 전건 값등가 요구. 갈래 순서를 바꾸면 재현이 깨진다(문서 초안 4갈래 = 29/59 실측).", + "authority_row_keys": { + "evidence_index": "id 키 6종(evidence_index·id·evidence_id·eid·index·doc_id) 첫 적중", + "title": "first_key(title, Title, name, doc_title)", + "excerpt": "key_facts -> key_dates/key_amounts -> 대표 문자열 폴백", + "priority": "first_key(priority, Priority, weight)", + "authentication": "first_key(authentication, auth, source_authentication)", + "source": "고정 문자열 'evidence_indexed.json'" + }, + "flatten_depth": 3, + "universe_missing_stub": "{evidence_index: eid, missing: true} — stub 도 비어 있지 않은 증거 행으로 계수한다(구본 1136행 승계)", + "text_rule": "행마다 json.dumps(sort_keys=True, ensure_ascii=False) -> 공백열 단일 공백 -> 300자 절단 -> ' '.join 으로 합쳐 1회 대조(합친 뒤 절단이 아니다). sort_keys 에서 title 이 최후미라 절단이 마커를 먼저 지운다 — 절단 여유를 영수증에 적는다", + "markers_documentary": [ + "등기", + "계약", + "판결", + "공문", + "금융", + "송금", + "계좌", + "registry", + "contract", + "judgment" + ], + "branches_in_order": [ + { + "order": 1, + "condition": "증거 행 0건", + "credibility": "low", + "proof_strength": "low" + }, + { + "order": 2, + "condition": "마커 10종 중 하나라도 text 에 존재", + "credibility": "high", + "proof_strength": "documentary" + }, + { + "order": 3, + "condition": "증거 행 2건 이상", + "credibility": "high", + "proof_strength": "high" + }, + { + "order": 4, + "condition": "text 에 meeting 또는 회의 또는 면담", + "credibility": "medium", + "proof_strength": "meeting_only_core" + }, + { + "order": 5, + "condition": "그 외 전부", + "credibility": "medium", + "proof_strength": "medium" + } + ] + }, + "skeleton_checks": { + "order": [ + "EMPTY_DERIVED_FACT", + "NO_EVIDENCE_REFS", + "NO_LINKED_LEGAL_EFFECT_STRUCTURE" + ], + "note": "스켈레톤 3코드를 이 고정 순서로 먼저, 그 뒤 게이트 코드를 gate_id 오름차순으로 append, 중복은 첫 출현 위치 보존 dedupe. G-n(바이트 동일)·G-r 이 순서에 의존한다." + }, + "gate_execution": { + "implemented_condition_keys": [ + "any_review_code_present", + "required_slots_missing_or_conflicting", + "calculation_operand_state_in" + ], + "visualized_condition_keys": { + "boundary_or_overlap_detected": "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW — 정의처(domain_extension.schema · Stage2_handoff_contract)가 배포 트리에 없다(D-P4-c=(가) · source_refs 배포 선언 0회 실측)" + }, + "str_condition_disposition": "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW — 술어 명세를 코드로 옮기는 것은 판정식 창작이다(D-P4-b=(가))", + "orphan_rule_disposition": "GATE_RULE_NOT_IN_REGISTRY", + "unknown_or_missing_policy_default": "fail_open_to_review", + "block_semantics": "blocks_final_drafting: true 는 Stage 1 기록을 막지 않는다. 행은 기록되고 final_writer_status 가 READY_WITH_REVIEW 가 되며 차단 표지는 Stage 2 로 전달된다.", + "row_projection": { + "target_rows": "발화 게이트의 도메인 d 에 대해 R(d) = { row | d ∈ row.domain_effects 키 }", + "codes": "R(d) 전 행의 skeleton_validation_codes 에 합집합 부착(순서 규칙은 skeleton_checks)", + "must_consider": "row_effect.must_consider 보유 규칙 발화 시 R(d) 전 행 OR 갱신(기존 True 미소실)", + "preserve_seed": "on_true.preserve_seed 는 행 필드가 아니라 writer report 게이트별 발화 목록에만 기록", + "empty_target": "R(d) 공집합이면 행에 닿지 않고 writer report 에만 계수(침묵 아님)" + }, + "normalization_axes_17": { + "rule_array_keys": [ + "rules", + "gates" + ], + "emitted_code_keys": [ + "emitted_codes", + "emitted_review_codes", + "review_code" + ], + "effect_containers": [ + "writer_effect", + "row_effect", + "on_true" + ], + "block_flag_paths": [ + "writer_effect.final_drafting_block", + "writer_effect.blocks_final_drafting", + "on_true.blocks_final_drafting" + ], + "status_when_triggered_paths": [ + "writer_effect.status_when_triggered", + "writer_effect.status_on_fire", + "on_true.writer_status" + ], + "evaluation_keys": [ + "evaluation_mode", + "implementation_type", + "evaluation" + ], + "row_effect_keys": [ + "row_effect.must_consider", + "on_true.preserve_seed" + ], + "file_status_keys": [ + "closure_status", + "status" + ], + "input_declaration_keys": [ + "input_fields", + "input_contracts" + ], + "optional_flags": [ + "writes_fact_ledger", + "unknown_or_missing_policy", + "silent_repair_forbidden", + "source_membership_required" + ], + "global_status_rule_note": "사이드카 global_status_rule 3방언은 채택하지 않는다 — 경고로만 기록하고 전역 규칙은 아래 global_status_rule 하나를 쓴다" + }, + "emitted_code_registry_check_surface": "gates[].review_codes (게이트 단위) — 도메인 어휘 면을 쓰면 실측 초과 2건(E18-G03·E20-G03)이 사라진다" + }, + "legacy_alias": { + "source": "routing/extension_payload_key_declarations.v1.json legacy_alias_targets", + "self_declaration_scan": "배포 domains//structure_types.json 전수 — 배열 키 방언(types·structure_types·entries) 정규화 · legacy_source 약칭(B1~B5)을 완전명으로 정규화 · B* 형이 아닌 값(예: 'Part3 fallback')은 선언이 아니다", + "mismatch_disposition": "누락 도메인은 해소 결과에 추가하지 않고 LEGACY_ALIAS_SELF_MISMATCH_REVIEW 로 누락 도메인 ID·영향 항목 수를 계수한다. 자산의 legacy_declaration_mismatches 는 파생 목록이므로 대조용으로만 쓰고 불일치 시 LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW 를 계수한다.", + "abbreviation_map": { + "B1": "B1_Money_Successor", + "B2": "B2_Secured_Registry", + "B3": "B3_Land_Valuation", + "B4": "B4_Actio", + "B5": "B5_Succession_Notice_Lien_Asset_Defense" + } + }, + "p2_handoff_rules": { + "code_key": "source_review_code — issue_type 은 enum 밖 값을 뭉갠다", + "code_pattern": "^[A-Z][A-Z0-9_]{2,127}$", + "not_a_code_disposition": "P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW", + "domain_attribution": "source_domain 을 별칭 해소표로 registry 도메인 ID 로 옮긴 뒤 해당 도메인 게이트에만 공급한다(전역 병합 금지 — 교차 오발화)", + "domain_unresolved_disposition": "P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW — 어느 게이트에도 공급하지 않는다" + }, + "calculation_join": { + "join_key": "signal_id (details.request_id 는 보장되지 않는 키)", + "operand_state": "Part 2 산출을 재산정 없이 승계한다(D-P4-6). deferred 는 registry 선언값이다.", + "calculation_id_missing": "signal_id 로 대체하고 CALCULATION_ID_SUBSTITUTED_REVIEW 계수 — 버리지 않는다", + "dangling_ref": "calculation_id 없는 stub 항목으로 적재(버리면 등식 ④가 정상 실행에서 발화) · CALCULATION_REF_DANGLING_REVIEW 계수", + "law_version_refs": "SG-11 레코드 law_version_refs ∪ details.law_version_refs ∪ 해당 도메인 SG-04 레코드 signal_id — 버전 확정 판단 금지(SG-04 는 참조만)" + }, + "conservation_equations_5": { + "row": "len(rows) == len(BO.json) -> PART4_ROW_CONSERVATION_BROKEN", + "attachment_coverage": "부착 BO 수 + 미부착 BO 수 == len(BO.json) -> PART4_BO_ATTACHMENT_COVERAGE_BROKEN", + "effect": "Σ_bo |domain_effects 키| == Σ_bo |by_bo_id 레코드 distinct domain_id| -> PART4_EFFECT_CONSERVATION_BROKEN", + "calc_row": "Σ_bo |calculation_requests| == Σ_bo |by_bo_id 레코드 distinct calculation_request_refs| -> PART4_CALC_CONSERVATION_BROKEN", + "calc_source": "부착 distinct SG-11 + 미부착 distinct == SG-11 레코드 총수 -> PART4_CALC_SOURCE_CONSERVATION_BROKEN", + "note": "④와 ⑤는 차원이 다르다 — 컴파일러가 SG-11 signal_id 를 도메인 전 레코드에 복사하므로 행 항목 수 ≠ 원천 레코드 수. by_bo_id == {} 는 정상 입력이다(골든 경로 부착 0/59)." + }, + "unattached_review": { + "bo": "FACT_ROW_UNATTACHED_REVIEW", + "structure": "STRUCTURE_UNATTACHED_REVIEW", + "note": "보존 등식은 손실을 세지 흡수를 세지 않는다 — 침묵 금지를 등식 위에 따로 건다" + }, + "exception_pack": { + "surviving_type": "legal_calculation_required", + "review_code": "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "trigger": "구본 866·899-925행 승계 — calculation_required AND amount 후보 2개 이상(bo.amount·core.Amount 양쪽 비공백)이면 pack 적재 + conflict_flags [CALCULATION_BASIS_CONFLICT] · elif amount 또는 object_spec 공백이면 pack 미적재 + CALCULATION_BASIS_INCOMPLETE_REVIEW 검토(if/elif 배타)", + "exception_id_format": "FX-{n:03d}", + "pack_item_char_limit": 2000, + "oversize_disposition": "자동 차단이 아니라 exception_id 유지한 채 review", + "unreachable_in_v8": { + "unresolved_asset_cluster_projection": "asset_cluster 계열이 Part 3 v.8 에서 소멸(P2·P3·LES 스키마·컴파일러 0회)", + "unresolved_structure_role_projection": "registry structure_types 73/73 이 module·role_projection 전건 선언(결손 0)" + }, + "unreachable_disposition": "pack 에 실리면 PART4_UNREACHABLE_EXCEPTION_TYPE 경성 실패" + }, + "adjudication": { + "accepted_status": [ + "READY", + "READY_NO_EXCEPTIONS", + "READY_WITH_BLOCKS" + ], + "allowed_patch_fields": [ + "amount" + ], + "note": "허용 필드 밖 수정은 PART4_PATCH_REJECTED. F1 의 유일 생존 부류가 계산 근거 다중성 판정이므로 패치 가능 필드는 amount 하나다." + }, + "global_status_rule": "blocked item · rejected patch · deterministic review · LES 승계 검토(review_codes 2종 ∪ review_required 플래그) · gate review code 중 하나라도 있으면 READY_WITH_REVIEW, 아니면 READY (동결 계약 승계 — 사이드카 3방언 값은 경고로만 기록)", + "les_inherited_review_codes": [ + "UNREGISTERED_EFFECT_TYPE_REVIEW", + "PART3_REGISTRY_SIDECAR_MISMATCH" + ], + "root_wrapping_table": { + "signals/signal_manifest.json": null, + "routing/domain_activation_manifest.json": "domain_activation_manifest", + "BO.json": "(array)", + "legal_effect_structures.json": null, + "signals/domain_signals/.json": "domain_signal_envelope", + "quality_gates/stage1_part2_review_handoff.json": null, + "evidence_indexed.json": null, + "stage1_tmp/fact_ledger/fact_ledger_candidate_bundle.json": "fact_ledger_candidate_bundle", + "stage1_tmp/fact_ledger/fact_exception_pack.json": "fact_exception_pack", + "stage1_tmp/fact_ledger/fact_adjudication.json": "fact_exception_adjudication", + "Fact_Ledger_base.json": "(array)", + "stage1_tmp/fact_ledger/fact_ledger_writer_report.json": null, + "quality_gates/stage1_part4_review_handoff.json": "stage1_part4_review_handoff", + "validation_assets/routing/part4_receipt.json": null, + "signals/governing_law_version_signals.json": null, + "signals/timeline_notice_condition_signals.json": null, + "signals/asset_right_state_signals.json": null, + "signals/evidence_proof_conflict_signals.json": null, + "signals/calculation_requirements.json": null + }, + "review_codes_soft": [ + "GATE_SPEC_NOT_DEPLOYED_REVIEW", + "GATE_CONDITION_KEY_UNSUPPORTED_REVIEW", + "GATE_PREDICATE_SPEC_UNIMPLEMENTED_REVIEW", + "GATE_RULE_NOT_IN_REGISTRY", + "GATE_EMITTED_CODE_NOT_IN_REGISTRY", + "GATE_BLOCK_FLAG_MISMATCH_REVIEW", + "FACT_ROW_UNATTACHED_REVIEW", + "STRUCTURE_UNATTACHED_REVIEW", + "CALCULATION_REF_DANGLING_REVIEW", + "CALCULATION_ID_SUBSTITUTED_REVIEW", + "CALCULATION_BASIS_INCOMPLETE_REVIEW", + "LEGACY_ALIAS_SELF_MISMATCH_REVIEW", + "LEGACY_ALIAS_DECLARATION_INCOMPLETE_REVIEW", + "P2_HANDOFF_CODE_NOT_A_REVIEW_CODE_REVIEW", + "P2_HANDOFF_DOMAIN_UNRESOLVED_REVIEW", + "EVIDENCE_REF_NOT_IN_EVIDENCE_UNIVERSE", + "EMPTY_DERIVED_FACT", + "NO_EVIDENCE_REFS", + "NO_LINKED_LEGAL_EFFECT_STRUCTURE" + ], + "hard_failure_codes": [ + "PART4_ASSET_DEPLOYMENT_INCOMPLETE", + "MODULE_MIRROR_UNREGISTERED", + "MODULE_MIRROR_HASH_MISMATCH", + "PART4_POLICY_INVALID", + "PART4_LEDGER_SCHEMA_STALE", + "PART4_COMPILER_STALE", + "PART4_SIGNAL_SEAL_FAILED", + "PART4_REGISTRY_INDEX_SEAL_FAILED", + "PART4_LES_TRANSACTION_MISMATCH", + "PART4_ROW_CONSERVATION_BROKEN", + "PART4_BO_ATTACHMENT_COVERAGE_BROKEN", + "PART4_EFFECT_CONSERVATION_BROKEN", + "PART4_CALC_CONSERVATION_BROKEN", + "PART4_CALC_SOURCE_CONSERVATION_BROKEN", + "PART4_COVERAGE_BROKEN", + "PART4_PATCH_REJECTED", + "PART4_BO_CONSERVATION_BROKEN", + "PART4_FACT_ID_SEQUENCE_BROKEN", + "PART4_ADJUDICATION_MISSING", + "PART4_DOMAIN_EFFECT_NOT_ACTIVE", + "PART4_LEDGER_SCHEMA_INVALID", + "PART4_UNREACHABLE_EXCEPTION_TYPE", + "PART4_WRITE_REREAD_MISMATCH" + ], + "deprecation": { + "old_4_fields_alias": "money_claim_effect·commercial_successor_effect·actio_roles·linked_actio_structures 는 v.8 한 버전 동안 domain_effects 로부터의 결정론 별칭이다. v.9 에서 제거 예정(D-P4-a=(가)).", + "derived_fact_4part_branch": "구본의 date·actor·obj·action 4항 서술 분기는 삭제 확정 — object_spec 참조가 끊겨 조건이 영구 거짓이 되는 사문이다.", + "old_signal_3_root_names": "actio_case_signals·case_liability_signals·legal_effect_signals 뿌리 이름을 읽지 않는다(C-7 폐쇄)." + }, + "f1_exception_types": { + "legal_calculation_required": { + "unreachable_in_v8": false + }, + "unresolved_asset_cluster_projection": { + "unreachable_in_v8": true + }, + "unresolved_structure_role_projection": { + "unreachable_in_v8": true + } + }, + "alias_module_vocabulary": { + "money_claim_effect": "money_claim", + "commercial_successor_effect": "commercial_successor", + "actio_fields": "actio", + "note": "구 4필드 별칭이 참조하는 module 어휘. 코드에 리터럴을 두지 않는다(정적 검사 11) — registry structure_types[].module 이 원천이고 이 선언은 그중 별칭 파생이 쓰는 세 값을 고정한다." + } +}