feat(stage1-part3): build Part 3 spec from registry per new strategy; deploy structure assets
- ver_8_yaml_candidates/stage_1_part_3_v.8.yml (52,878 B): two-task stage (Task_C_LE_L0_structure_seed_reducer -> Task_C_LE_L2_final_structure_index_writer, no L1) built solely from registry and Part 1|2 outputs (zero Claude_YAML dependency); fragments P3-L0/L2 v1 block-identical to the integrated file - Default_Agent deployment origin: new structure_index_compiler.py(+mirror), structure_index_policy.v1.json, two sealed schemas (unversioned filenames, pattern-only), 50 sidecar files copied byte-identical from assembly; runtime_manifest 371->426 (delta exactly 55, zero existing-row edits) - interface table 34->45 rows (closes C-7: legacy signal names dropped from Part 3 consumers; closes C-8: client_goal registered, client_meeting consumer added in both duplicated rows), stage_chain Part 3 section, upper strategy chain/§4.2 updated for L1 absence - verified: 3-pass dry run on deployment origin produces legal_effect_structures.json (theta 4, 6 records, SG-13 6/6 attached, BO 2/2 attached via source_domain+Legal_Keywords join, 3 conservation equations from source counts, 3 reverse indexes); 26-domain synthetic theta 56 / records 73 / modules 34, deterministic; regressions 22+2 supplementary all PASS on copied variant trees; interface checker 0 findings; adversarial sub-agent loop 5 findings -> 0 - P3-0a (D-4 approval) treated as granted by the user's execution order; assembly-side sync (builder registration, ledgers, receipt) deferred as one bundle with Part 2 carry-overs - work record stage_1_part_3_개정작업내역서.md; v.7/MEMORY.md entry 23; 확장_최적워크플로우_연구_프롬프트.txt session prompt log Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1474,3 +1474,26 @@ D-4 결정안 승계(Θ=expected_runnable 합집합 · order_key 3키 · by_modu
|
|||||||
2. **회귀 시나리오는 자기 문서의 읽기 규율과 대조해야 한다.** `_verify_asset` 로 읽으라고 지시한 자산의 "내용만 변조" 회귀는 해시 게이트에 먼저 걸려 의미 검사에 도달하지 못한다 — 변조 회귀는 장부 동반/미동반 두 변종이 짝이다.
|
2. **회귀 시나리오는 자기 문서의 읽기 규율과 대조해야 한다.** `_verify_asset` 로 읽으라고 지시한 자산의 "내용만 변조" 회귀는 해시 게이트에 먼저 걸려 의미 검사에 도달하지 못한다 — 변조 회귀는 장부 동반/미동반 두 변종이 짝이다.
|
||||||
3. **용어 하나("활성")가 필수 회귀를 뒤집는다.** 정의역이 둘(`active` ⊊ `expected_runnable`) 있는 시스템에서는 집합 이름을 축약하지 말 것.
|
3. **용어 하나("활성")가 필수 회귀를 뒤집는다.** 정의역이 둘(`active` ⊊ `expected_runnable`) 있는 시스템에서는 집합 이름을 축약하지 말 것.
|
||||||
4. 문서 검증자는 편집 파급 검증에서 가장 세다 — 라운드 2~6 의 지적 전부가 "반영이 만든 새 모순"이었다. 반영 후 재검증을 생략하면 수정이 결함을 재생산한다.
|
4. 문서 검증자는 편집 파급 검증에서 가장 세다 — 라운드 2~6 의 지적 전부가 "반영이 만든 새 모순"이었다. 반영 후 재검증을 생략하면 수정이 결함을 재생산한다.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2026-08-19 ㉓ — Part 3 신전략서 실행 완료: stage_1_part_3_v.8.yml 생성 (예행 3패스 · 회귀 24/24 · 적대 검증 2라운드)
|
||||||
|
|
||||||
|
산출: 통합본 `ver_8_yaml_candidates/stage_1_part_3_v.8.yml`(52,878 B · task 2 · IN→L0→L2→OUT) + 조각 P3-L0/L2 v1, 배포 원본 신설 5종(`structure_index_compiler.py`+미러 · `structure_index_policy.v1.json` · 스키마 2 — 파일명 무판본·패턴만) + 사이드카 50 복사(조립본과 바이트 동일), `runtime_manifest` **371→426**(델타 55·기존 행 수정 0), 선언표 34→**45**(C-7 구 이름 소비자 제거·C-8 폐쇄), stage_chain·상위 전략서 2곳, 내역서 `stage_1_part_3_개정작업내역서.md`. P3-0a 는 사용자의 실행 지시를 승인으로 처리.
|
||||||
|
|
||||||
|
### 핵심 실측
|
||||||
|
- 예행 3패스(asset root = 배포 원본): pass1 6/45 · pass2 3/29 · **pass3 2/5 — `legal_effect_structures.json` 실생성**. Θ 4종·레코드 6((도메인,type_id) 쌍)·SG-13 6/6 부착·**BO 2/2 부착**(source_domain+Legal_Keywords 결정론 조인 — BO 투영 회차가 실은 registry 토큰이 여기서 회수됨)·보존 등식 셋(L2 가 config·SG-13·BO 원본에서 바깥 수 직접 계수)·역인덱스 셋(by_domain_id·by_module·by_bo_id).
|
||||||
|
- 26 도메인 합성: Θ 56 · 구조 73 · module 34 · 머리 4줄(rank1 money_claim×2 비병합) · 결정론. 계산 탐침: signal_id 조인·dangling 검토·미연결 형식화 각 실증.
|
||||||
|
- 회귀 L-a~L-s 22종 + 보충 2종 = 24/24(필수 8종 포함). 변조는 전부 사본 트리 — 배포 원본 무변.
|
||||||
|
- 선언표 검사기 finding 0(3 yaml 전수). 트리 210 파일·매니페스트 실재분 전수 일치·__pycache__ 0.
|
||||||
|
|
||||||
|
### 검증 루프 (지적 5 → 0)
|
||||||
|
1차 MAJOR 2: ① 선언표 `client_meeting` 행이 **두 곳에 중복**(entry_inputs·handoffs)인데 절반만 갱신 — 검사기는 handoffs 만 읽으므로 기계 검증면 미반영 ② 도메인 봉투 부재가 침묵 스킵(실주행에서 감시 도메인 봉투 부재는 상시 경로) → `audit.input_gaps` 기록+L-k 단언. MINOR 3: L2 의 계약 밖 signal_manifest 읽기(→ L0 가 transaction_id 를 bundle 에 부착해 승계) · 합성/탐침 증적 미보존(→ SUPP 2종 영구 기록) · `Stage_1.Part_3` 약칭 잔존. 재검증 **지적 0건**.
|
||||||
|
|
||||||
|
### 교훈
|
||||||
|
1. **중복 선언 행은 전수 갱신 대상이다.** 같은 계약 문서 안에 같은 행이 두 배열에 있으면(entry_inputs·handoffs) 하나만 고친 갱신은 검사기가 읽는 쪽에 따라 무효가 된다 — 갱신 전에 대상 행을 grep 으로 전수 나열할 것.
|
||||||
|
2. **"실재하는 것만 읽는다"는 규칙에는 부재 기록이 한 벌이다.** 예행에서 전부 실재하는 입력은 부재 경로가 한 번도 발화하지 않아 침묵 스킵이 숨는다 — 부재가 상시인 실주행 조건을 회귀(L-k)로 강제해야 드러난다.
|
||||||
|
3. **하류가 상류 봉인값을 쓰려면 재독이 아니라 승계다.** L2 가 signal_manifest 를 다시 읽는 것은 계약 밖 읽기이자 봉인 우회 여지 — L0 가 검증한 값을 bundle 로 나르는 것이 계약에 맞다.
|
||||||
|
4. **검증 스크립트의 부산물도 배포 트리 오염원이다.** sys.path 반입이 만든 __pycache__ 가 트리에 두 번 생겼다 — 실측 스크립트에는 dont_write_bytecode 를 상수로 둘 것.
|
||||||
|
5. 검사기의 가시 범위에 맞춰 코드를 쓴다 — read_json 직호출 상수만 보는 U4 앞에서 간접 호출 래퍼는 "선언은 했으나 실측 불가"를 만든다(L0 read-plumbing 재작성으로 해소).
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s2_module_role_projection.v1",
|
||||||
|
"domain_id": "E-00",
|
||||||
|
"legacy_projection_source": {
|
||||||
|
"path": "S_0_GPT/legacy/part4/structure_projection_contract.json",
|
||||||
|
"sha256": "9e615dda7e26e23fcaae381ed05763d16e4887909400277c9996919e09398398"
|
||||||
|
},
|
||||||
|
"structure_type_to_projection": {
|
||||||
|
"general_legal_effect": {
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default_projection": {
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW"
|
||||||
|
},
|
||||||
|
"closure_status": "CLOSED_WITH_LOCAL_FILES"
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s2_structure_types.v1",
|
||||||
|
"domain_id": "E-00",
|
||||||
|
"legacy_contract": {
|
||||||
|
"canonical_type_set_frozen": true,
|
||||||
|
"source": "1. Stage_1/v.7/extension_research/선행구축/S_0_GPT/legacy/part3/structure_type_contract.json"
|
||||||
|
},
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"legacy_source": "Part3 fallback",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"admission_rule": "registered route only; E-00 unregistered material remains review"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noncanonical_preservation": "nonrouting_labels",
|
||||||
|
"ordering_preserved": true
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"default_projection":null,"domain_id":"E-01","mappings":[{"module":"juristic_act","role_projection":"act_party_object_roles","source":"s6_registered","structure_type":"juristic_act_validity"},{"module":"juristic_act","role_projection":"declarant_counterparty_roles","source":"s6_registered","structure_type":"declaration_defect"},{"module":"juristic_act","role_projection":"canceller_counterparty_third_party_roles","source":"s6_registered","structure_type":"cancellation_effect"},{"module":"agency","role_projection":"principal_agent_counterparty_roles","source":"s6_registered","structure_type":"unauthorized_agency"},{"module":"agency","role_projection":"principal_apparent_agent_relying_party_roles","source":"s6_registered","structure_type":"apparent_agency"},{"module":"juristic_act","role_projection":"condition_beneficiary_obligor_roles","source":"s6_registered","structure_type":"condition_term_effect"}],"missing_mapping_policy":{"emit_review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","silent_fallback_forbidden":true},"schema_version":"stage1_domain_module_role_projection.v1","status":"ACTIVE"}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"admission_rules":{"multiple_types_allowed":true,"nonregistered_type_review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","registered_membership_required":true,"silent_general_legal_effect_fallback_forbidden":true,"source_order_deduplicated":true},"domain_id":"E-01","schema_version":"stage1_domain_structure_types.v1","status":"ACTIVE","structure_types":[{"module":"juristic_act","priority_rank":71,"role_projection":"act_party_object_roles","type_id":"juristic_act_validity"},{"module":"juristic_act","priority_rank":72,"role_projection":"declarant_counterparty_roles","type_id":"declaration_defect"},{"module":"juristic_act","priority_rank":73,"role_projection":"canceller_counterparty_third_party_roles","type_id":"cancellation_effect"},{"module":"agency","priority_rank":74,"role_projection":"principal_agent_counterparty_roles","type_id":"unauthorized_agency"},{"module":"agency","priority_rank":75,"role_projection":"principal_apparent_agent_relying_party_roles","type_id":"apparent_agency"},{"module":"juristic_act","priority_rank":76,"role_projection":"condition_beneficiary_obligor_roles","type_id":"condition_term_effect"}]}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s2_module_role_projection.v1",
|
||||||
|
"domain_id": "E-02",
|
||||||
|
"projections": [
|
||||||
|
{
|
||||||
|
"type_id": "money_claim",
|
||||||
|
"module": "money_claim",
|
||||||
|
"role_projection": "claim_chain_ref"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"closure_status": "CLOSED"
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s2_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-02",
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type_id": "money_claim",
|
||||||
|
"priority_rank": 1,
|
||||||
|
"legacy_source": "B1_Money_Successor",
|
||||||
|
"module": "money_claim",
|
||||||
|
"role_projection": "claim_chain_ref",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"legacy_source": "B1_Money_Successor",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"registered": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fallback_policy": "unregistered_effect_type_review; silent_generalization_forbidden"
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s2_module_role_projection.v1",
|
||||||
|
"domain_id": "E-03",
|
||||||
|
"projections": [
|
||||||
|
{
|
||||||
|
"type_id": "money_claim",
|
||||||
|
"module": "money_claim",
|
||||||
|
"role_projection": "claim_chain_ref"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "commercial_successor",
|
||||||
|
"module": "commercial_successor",
|
||||||
|
"role_projection": "claim_chain_ref"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"closure_status": "CLOSED"
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s2_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-03",
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type_id": "money_claim",
|
||||||
|
"priority_rank": 1,
|
||||||
|
"legacy_source": "B1_Money_Successor",
|
||||||
|
"module": "money_claim",
|
||||||
|
"role_projection": "claim_chain_ref",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "commercial_successor",
|
||||||
|
"priority_rank": 2,
|
||||||
|
"legacy_source": "B1_Money_Successor",
|
||||||
|
"module": "commercial_successor",
|
||||||
|
"role_projection": "claim_chain_ref",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"legacy_source": "B1_Money_Successor",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"registered": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fallback_policy": "unregistered_effect_type_review; silent_generalization_forbidden"
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_module_role_projection.v1",
|
||||||
|
"domain_id": "E-04",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"structure_type": "land_use_gain",
|
||||||
|
"module": "land_use_gain",
|
||||||
|
"role_projection": "legal_calculation_object",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "valuation",
|
||||||
|
"module": "valuation",
|
||||||
|
"role_projection": "legal_calculation_object",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "unjust_enrichment",
|
||||||
|
"module": "unjust_enrichment",
|
||||||
|
"role_projection": "restitution_roles",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_projection": null,
|
||||||
|
"missing_mapping_policy": {
|
||||||
|
"emit_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-04",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"legacy_source_domain": "B3",
|
||||||
|
"structure_types": [
|
||||||
|
{
|
||||||
|
"type_id": "land_use_gain",
|
||||||
|
"priority_rank": 5,
|
||||||
|
"module": "land_use_gain",
|
||||||
|
"role_projection": "legal_calculation_object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "valuation",
|
||||||
|
"priority_rank": 6,
|
||||||
|
"module": "valuation",
|
||||||
|
"role_projection": "legal_calculation_object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "unjust_enrichment",
|
||||||
|
"priority_rank": 12,
|
||||||
|
"module": "unjust_enrichment",
|
||||||
|
"role_projection": "restitution_roles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"admission_rules": {
|
||||||
|
"registered_membership_required": true,
|
||||||
|
"multiple_types_allowed": true,
|
||||||
|
"source_order_deduplicated": true,
|
||||||
|
"nonregistered_type_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_general_legal_effect_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_module_role_projection.v1",
|
||||||
|
"domain_id": "E-05",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"structure_type": "tort_liability",
|
||||||
|
"module": "tort",
|
||||||
|
"role_projection": "tort_liability_roles",
|
||||||
|
"source": "s3_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "joint_tort_liability",
|
||||||
|
"module": "tort",
|
||||||
|
"role_projection": "joint_tort_roles",
|
||||||
|
"source": "s3_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "vicarious_liability",
|
||||||
|
"module": "tort",
|
||||||
|
"role_projection": "employer_employee_roles",
|
||||||
|
"source": "s3_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "structure_liability",
|
||||||
|
"module": "tort",
|
||||||
|
"role_projection": "possessor_owner_roles",
|
||||||
|
"source": "s3_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "tort_damage_extent",
|
||||||
|
"module": "damages",
|
||||||
|
"role_projection": "damage_causation_roles",
|
||||||
|
"source": "s3_registered"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_projection": null,
|
||||||
|
"missing_mapping_policy": {
|
||||||
|
"emit_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-05",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"structure_types": [
|
||||||
|
{
|
||||||
|
"type_id": "tort_liability",
|
||||||
|
"priority_rank": 20,
|
||||||
|
"module": "tort",
|
||||||
|
"role_projection": "tort_liability_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "joint_tort_liability",
|
||||||
|
"priority_rank": 21,
|
||||||
|
"module": "tort",
|
||||||
|
"role_projection": "joint_tort_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "vicarious_liability",
|
||||||
|
"priority_rank": 22,
|
||||||
|
"module": "tort",
|
||||||
|
"role_projection": "employer_employee_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "structure_liability",
|
||||||
|
"priority_rank": 23,
|
||||||
|
"module": "tort",
|
||||||
|
"role_projection": "possessor_owner_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "tort_damage_extent",
|
||||||
|
"priority_rank": 24,
|
||||||
|
"module": "damages",
|
||||||
|
"role_projection": "damage_causation_roles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"admission_rules": {
|
||||||
|
"registered_membership_required": true,
|
||||||
|
"multiple_types_allowed": true,
|
||||||
|
"source_order_deduplicated": true,
|
||||||
|
"nonregistered_type_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_general_legal_effect_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"default_projection":null,"domain_id":"E-06","mappings":[{"module":"professional_liability","role_projection":"professional_client_roles","source":"s6_registered","structure_type":"professional_duty_liability"},{"module":"medical_liability","role_projection":"patient_provider_roles","source":"s6_registered","structure_type":"medical_treatment_fault"},{"module":"medical_liability","role_projection":"patient_explainer_consent_roles","source":"s6_registered","structure_type":"medical_disclosure_duty"},{"module":"product_liability","role_projection":"injured_manufacturer_supplier_roles","source":"s6_registered","structure_type":"product_defect_liability"},{"module":"damages","role_projection":"conduct_damage_alternative_cause_roles","source":"s6_registered","structure_type":"professional_damage_causation"}],"missing_mapping_policy":{"emit_review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","silent_fallback_forbidden":true},"schema_version":"stage1_domain_module_role_projection.v1","status":"ACTIVE"}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"admission_rules":{"multiple_types_allowed":true,"nonregistered_type_review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","registered_membership_required":true,"silent_general_legal_effect_fallback_forbidden":true,"source_order_deduplicated":true},"domain_id":"E-06","schema_version":"stage1_domain_structure_types.v1","status":"ACTIVE","structure_types":[{"module":"professional_liability","priority_rank":71,"role_projection":"professional_client_roles","type_id":"professional_duty_liability"},{"module":"medical_liability","priority_rank":72,"role_projection":"patient_provider_roles","type_id":"medical_treatment_fault"},{"module":"medical_liability","priority_rank":73,"role_projection":"patient_explainer_consent_roles","type_id":"medical_disclosure_duty"},{"module":"product_liability","priority_rank":74,"role_projection":"injured_manufacturer_supplier_roles","type_id":"product_defect_liability"},{"module":"damages","priority_rank":75,"role_projection":"conduct_damage_alternative_cause_roles","type_id":"professional_damage_causation"}]}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s3_module_role_projection.v1",
|
||||||
|
"domain_id": "E-07",
|
||||||
|
"projections": [
|
||||||
|
{
|
||||||
|
"type_id": "construction_payment_claim",
|
||||||
|
"module": "construction_contract",
|
||||||
|
"role_projection": "construction_payment_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "construction_progress_settlement",
|
||||||
|
"module": "construction_contract",
|
||||||
|
"role_projection": "construction_progress_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "construction_defect_remedy",
|
||||||
|
"module": "construction_defect",
|
||||||
|
"role_projection": "construction_defect_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "construction_defect_setoff",
|
||||||
|
"module": "construction_defect",
|
||||||
|
"role_projection": "construction_setoff_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"closure_status": "CLOSED"
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s3_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-07",
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type_id": "construction_payment_claim",
|
||||||
|
"priority_rank": 1,
|
||||||
|
"module": "construction_contract",
|
||||||
|
"role_projection": "construction_payment_roles",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "construction_progress_settlement",
|
||||||
|
"priority_rank": 2,
|
||||||
|
"module": "construction_contract",
|
||||||
|
"role_projection": "construction_progress_roles",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "construction_defect_remedy",
|
||||||
|
"priority_rank": 3,
|
||||||
|
"module": "construction_defect",
|
||||||
|
"role_projection": "construction_defect_roles",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "construction_defect_setoff",
|
||||||
|
"priority_rank": 4,
|
||||||
|
"module": "construction_defect",
|
||||||
|
"role_projection": "construction_setoff_roles",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"registered": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fallback_policy": "unregistered_effect_type_review; silent_generalization_forbidden"
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s3_module_role_projection.v1",
|
||||||
|
"domain_id": "E-08",
|
||||||
|
"projections": [
|
||||||
|
{
|
||||||
|
"type_id": "lease_deposit_return",
|
||||||
|
"module": "lease_contract",
|
||||||
|
"role_projection": "lease_deposit_return_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "lease_rent_payment",
|
||||||
|
"module": "lease_contract",
|
||||||
|
"role_projection": "lease_rent_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "lease_termination",
|
||||||
|
"module": "lease_lifecycle",
|
||||||
|
"role_projection": "lease_termination_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "lease_delivery_concurrent_performance",
|
||||||
|
"module": "lease_lifecycle",
|
||||||
|
"role_projection": "lease_concurrent_performance_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "lease_attached_property_purchase",
|
||||||
|
"module": "lease_contract",
|
||||||
|
"role_projection": "lease_purchase_claim_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"closure_status": "CLOSED"
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s3_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-08",
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type_id": "lease_deposit_return",
|
||||||
|
"priority_rank": 1,
|
||||||
|
"module": "lease_contract",
|
||||||
|
"role_projection": "lease_deposit_return_roles",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "lease_rent_payment",
|
||||||
|
"priority_rank": 2,
|
||||||
|
"module": "lease_contract",
|
||||||
|
"role_projection": "lease_rent_roles",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "lease_termination",
|
||||||
|
"priority_rank": 3,
|
||||||
|
"module": "lease_lifecycle",
|
||||||
|
"role_projection": "lease_termination_roles",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "lease_delivery_concurrent_performance",
|
||||||
|
"priority_rank": 4,
|
||||||
|
"module": "lease_lifecycle",
|
||||||
|
"role_projection": "lease_concurrent_performance_roles",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "lease_attached_property_purchase",
|
||||||
|
"priority_rank": 5,
|
||||||
|
"module": "lease_contract",
|
||||||
|
"role_projection": "lease_purchase_claim_roles",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"registered": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fallback_policy": "unregistered_effect_type_review; silent_generalization_forbidden"
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s2_module_role_projection.v1",
|
||||||
|
"domain_id": "E-09",
|
||||||
|
"projections": [
|
||||||
|
{
|
||||||
|
"type_id": "registry_invalidity",
|
||||||
|
"module": "registry_invalidity",
|
||||||
|
"role_projection": "linked_structures"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"closure_status": "CLOSED"
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s2_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-09",
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type_id": "registry_invalidity",
|
||||||
|
"priority_rank": 4,
|
||||||
|
"legacy_source": "B2_Secured_Registry",
|
||||||
|
"module": "registry_invalidity",
|
||||||
|
"role_projection": "linked_structures",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"legacy_source": "B2_Secured_Registry",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"registered": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fallback_policy": "unregistered_effect_type_review; silent_generalization_forbidden"
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s2_module_role_projection.v1",
|
||||||
|
"domain_id": "E-10",
|
||||||
|
"projections": [
|
||||||
|
{
|
||||||
|
"type_id": "secured_debt",
|
||||||
|
"module": "secured_property",
|
||||||
|
"role_projection": "linked_structures"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "registry_invalidity",
|
||||||
|
"module": "registry_invalidity",
|
||||||
|
"role_projection": "linked_structures"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "succession_notice_lien_asset_defense",
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"closure_status": "CLOSED"
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "s2_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-10",
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type_id": "secured_debt",
|
||||||
|
"priority_rank": 3,
|
||||||
|
"legacy_source": "B2_Secured_Registry",
|
||||||
|
"module": "secured_property",
|
||||||
|
"role_projection": "linked_structures",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "registry_invalidity",
|
||||||
|
"priority_rank": 4,
|
||||||
|
"legacy_source": "B2_Secured_Registry",
|
||||||
|
"module": "registry_invalidity",
|
||||||
|
"role_projection": "linked_structures",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "succession_notice_lien_asset_defense",
|
||||||
|
"priority_rank": 10,
|
||||||
|
"legacy_source": "B2_Secured_Registry",
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures",
|
||||||
|
"registered": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"legacy_source": "B2_Secured_Registry",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"registered": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fallback_policy": "unregistered_effect_type_review; silent_generalization_forbidden"
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_module_role_projection.v1",
|
||||||
|
"domain_id": "E-11",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"structure_type": "possession_vindication",
|
||||||
|
"module": "possession_vindication",
|
||||||
|
"role_projection": "linked_structures",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "land_use_gain",
|
||||||
|
"module": "land_use_gain",
|
||||||
|
"role_projection": "legal_calculation_object",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "succession_notice_lien_asset_defense",
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_projection": null,
|
||||||
|
"missing_mapping_policy": {
|
||||||
|
"emit_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-11",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"legacy_source_domain": "B3",
|
||||||
|
"structure_types": [
|
||||||
|
{
|
||||||
|
"type_id": "possession_vindication",
|
||||||
|
"priority_rank": 5,
|
||||||
|
"module": "possession_vindication",
|
||||||
|
"role_projection": "linked_structures"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "land_use_gain",
|
||||||
|
"priority_rank": 6,
|
||||||
|
"module": "land_use_gain",
|
||||||
|
"role_projection": "legal_calculation_object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "succession_notice_lien_asset_defense",
|
||||||
|
"priority_rank": 10,
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"admission_rules": {
|
||||||
|
"registered_membership_required": true,
|
||||||
|
"multiple_types_allowed": true,
|
||||||
|
"source_order_deduplicated": true,
|
||||||
|
"nonregistered_type_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_general_legal_effect_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"default_projection":null,"domain_id":"E-12","mappings":[{"module":"coownership","role_projection":"coowner_act_consent_roles","source":"s6_registered","structure_type":"coownership_governance"},{"module":"partition","role_projection":"partitioner_coowner_object_roles","source":"s6_registered","structure_type":"coownership_partition"},{"module":"boundary","role_projection":"adjacent_owner_parcel_boundary_roles","source":"s6_registered","structure_type":"boundary_determination"},{"module":"coownership","role_projection":"using_coowner_nonusing_coowner_roles","source":"s6_registered","structure_type":"exclusive_use_settlement"},{"module":"aggregate_building","role_projection":"management_body_unit_owner_roles","source":"s6_registered","structure_type":"aggregate_building_management"}],"missing_mapping_policy":{"emit_review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","silent_fallback_forbidden":true},"schema_version":"stage1_domain_module_role_projection.v1","status":"ACTIVE"}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"admission_rules":{"multiple_types_allowed":true,"nonregistered_type_review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","registered_membership_required":true,"silent_general_legal_effect_fallback_forbidden":true,"source_order_deduplicated":true},"domain_id":"E-12","schema_version":"stage1_domain_structure_types.v1","status":"ACTIVE","structure_types":[{"module":"coownership","priority_rank":71,"role_projection":"coowner_act_consent_roles","type_id":"coownership_governance"},{"module":"partition","priority_rank":72,"role_projection":"partitioner_coowner_object_roles","type_id":"coownership_partition"},{"module":"boundary","priority_rank":73,"role_projection":"adjacent_owner_parcel_boundary_roles","type_id":"boundary_determination"},{"module":"coownership","priority_rank":74,"role_projection":"using_coowner_nonusing_coowner_roles","type_id":"exclusive_use_settlement"},{"module":"aggregate_building","priority_rank":75,"role_projection":"management_body_unit_owner_roles","type_id":"aggregate_building_management"}]}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_module_role_projection.v1",
|
||||||
|
"domain_id": "E-13",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"structure_type": "fraudulent_transfer",
|
||||||
|
"module": "actio",
|
||||||
|
"role_projection": "actio_roles",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "preserved_claim",
|
||||||
|
"module": "actio",
|
||||||
|
"role_projection": "claim_chain_ref",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "actio_remedy_or_cap",
|
||||||
|
"module": "actio",
|
||||||
|
"role_projection": "linked_actio_structures",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_projection": null,
|
||||||
|
"missing_mapping_policy": {
|
||||||
|
"emit_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_structure_types.v1",
|
||||||
|
"domain_id": "E-13",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"legacy_source_domain": "B4",
|
||||||
|
"structure_types": [
|
||||||
|
{
|
||||||
|
"type_id": "fraudulent_transfer",
|
||||||
|
"priority_rank": 7,
|
||||||
|
"module": "actio",
|
||||||
|
"role_projection": "actio_roles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "preserved_claim",
|
||||||
|
"priority_rank": 8,
|
||||||
|
"module": "actio",
|
||||||
|
"role_projection": "claim_chain_ref"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "actio_remedy_or_cap",
|
||||||
|
"priority_rank": 9,
|
||||||
|
"module": "actio",
|
||||||
|
"role_projection": "linked_actio_structures"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"admission_rules": {
|
||||||
|
"registered_membership_required": true,
|
||||||
|
"multiple_types_allowed": true,
|
||||||
|
"source_order_deduplicated": true,
|
||||||
|
"nonregistered_type_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_general_legal_effect_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s3_module_role_projection.v1",
|
||||||
|
"domain_id": "E-14",
|
||||||
|
"structure_type_to_projection": {
|
||||||
|
"execution_linked_claim": {
|
||||||
|
"module": "execution_linked_claims",
|
||||||
|
"role_projection": "linked_execution_claims"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default_projection": {
|
||||||
|
"module": "unregistered_effect_review",
|
||||||
|
"role_projection": "unregistered_effect_roles",
|
||||||
|
"review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW"
|
||||||
|
},
|
||||||
|
"closure_status": "CLOSED_WITH_LOCAL_FILES"
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s3_structure_types.v1",
|
||||||
|
"domain_id": "E-14",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type_id": "execution_linked_claim",
|
||||||
|
"priority_rank": 14,
|
||||||
|
"module": "execution_linked_claims",
|
||||||
|
"role_projection": "linked_execution_claims",
|
||||||
|
"admission_rule": "source-backed SG-13 route and registered active domain required"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"unregistered_effect_policy": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"ordering_integration_required": true
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s2_module_role_projection.v1",
|
||||||
|
"domain_id": "E-15",
|
||||||
|
"legacy_projection_source": {
|
||||||
|
"path": "S_0_GPT/legacy/part4/structure_projection_contract.json",
|
||||||
|
"sha256": "9e615dda7e26e23fcaae381ed05763d16e4887909400277c9996919e09398398"
|
||||||
|
},
|
||||||
|
"structure_type_to_projection": {
|
||||||
|
"succession_notice_lien_asset_defense": {
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures"
|
||||||
|
},
|
||||||
|
"general_legal_effect": {
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default_projection": {
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW"
|
||||||
|
},
|
||||||
|
"closure_status": "CLOSED_WITH_LOCAL_FILES"
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s2_structure_types.v1",
|
||||||
|
"domain_id": "E-15",
|
||||||
|
"legacy_contract": {
|
||||||
|
"canonical_type_set_frozen": true,
|
||||||
|
"source": "1. Stage_1/v.7/extension_research/선행구축/S_0_GPT/legacy/part3/structure_type_contract.json"
|
||||||
|
},
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type_id": "succession_notice_lien_asset_defense",
|
||||||
|
"priority_rank": 10,
|
||||||
|
"legacy_source": "B5",
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures",
|
||||||
|
"admission_rule": "domain payload or SG-13 candidate must be source-backed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"legacy_source": "Part3 fallback",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"admission_rule": "registered route only; E-00 unregistered material remains review"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noncanonical_preservation": "nonrouting_labels",
|
||||||
|
"ordering_preserved": true
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"default_projection":null,"domain_id":"E-16","mappings":[{"module":"negotiable_instrument","role_projection":"issuer_acceptor_holder_roles","source":"s6_registered","structure_type":"bill_primary_liability"},{"module":"negotiable_instrument","role_projection":"drawer_payee_holder_roles","source":"s6_registered","structure_type":"check_issuer_liability"},{"module":"negotiable_instrument","role_projection":"endorser_endorsee_holder_roles","source":"s6_registered","structure_type":"endorsement_holder_status"},{"module":"negotiable_instrument","role_projection":"presenter_drawee_recourse_party_roles","source":"s6_registered","structure_type":"presentment_recourse_liability"},{"module":"negotiable_instrument","role_projection":"grantor_completer_holder_roles","source":"s6_registered","structure_type":"blank_instrument_completion"},{"module":"negotiable_instrument","role_projection":"instrument_debtor_holder_cause_party_roles","source":"s6_registered","structure_type":"cause_relation_defense"}],"missing_mapping_policy":{"emit_review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","silent_fallback_forbidden":true},"schema_version":"stage1_domain_module_role_projection.v1","status":"ACTIVE"}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"admission_rules":{"multiple_types_allowed":true,"nonregistered_type_review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","registered_membership_required":true,"silent_general_legal_effect_fallback_forbidden":true,"source_order_deduplicated":true},"domain_id":"E-16","schema_version":"stage1_domain_structure_types.v1","status":"ACTIVE","structure_types":[{"module":"negotiable_instrument","priority_rank":71,"role_projection":"issuer_acceptor_holder_roles","type_id":"bill_primary_liability"},{"module":"negotiable_instrument","priority_rank":72,"role_projection":"drawer_payee_holder_roles","type_id":"check_issuer_liability"},{"module":"negotiable_instrument","priority_rank":73,"role_projection":"endorser_endorsee_holder_roles","type_id":"endorsement_holder_status"},{"module":"negotiable_instrument","priority_rank":74,"role_projection":"presenter_drawee_recourse_party_roles","type_id":"presentment_recourse_liability"},{"module":"negotiable_instrument","priority_rank":75,"role_projection":"grantor_completer_holder_roles","type_id":"blank_instrument_completion"},{"module":"negotiable_instrument","priority_rank":76,"role_projection":"instrument_debtor_holder_cause_party_roles","type_id":"cause_relation_defense"}]}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s3_module_role_projection.v1",
|
||||||
|
"domain_id": "E-17",
|
||||||
|
"structure_type_to_projection": {
|
||||||
|
"labor_wage_claim": {
|
||||||
|
"module": "labor_wage_claims",
|
||||||
|
"role_projection": "linked_labor_claims"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default_projection": {
|
||||||
|
"module": "unregistered_effect_review",
|
||||||
|
"role_projection": "unregistered_effect_roles",
|
||||||
|
"review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW"
|
||||||
|
},
|
||||||
|
"closure_status": "CLOSED_WITH_LOCAL_FILES"
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s3_structure_types.v1",
|
||||||
|
"domain_id": "E-17",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type_id": "labor_wage_claim",
|
||||||
|
"priority_rank": 17,
|
||||||
|
"module": "labor_wage_claims",
|
||||||
|
"role_projection": "linked_labor_claims",
|
||||||
|
"admission_rule": "source-backed SG-13 route and registered active domain required"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"unregistered_effect_policy": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"ordering_integration_required": true
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"closure_status":"CLOSED_WITH_LOCAL_FILES","default_projection":{"module":"unregistered_effect_review","review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","role_projection":"unregistered_effect_roles"},"domain_id":"E-18","schema_version":"stage1_s6_module_role_projection.v1","structure_type_to_projection":{"org_resolution_status_relation":{"module":"org_resolution_status","role_projection":"resolution_status_roles"}}}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"domain_id":"E-18","entries":[{"admission_rule":"source-backed SG-13 route and registered active domain required","module":"org_resolution_status","priority_rank":18,"role_projection":"resolution_status_roles","type_id":"org_resolution_status_relation"}],"ordering_integration_required":true,"schema_version":"stage1_s6_structure_types.v1","unregistered_effect_policy":"UNREGISTERED_EFFECT_TYPE_REVIEW"}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"closure_status":"CLOSED_WITH_LOCAL_FILES","default_projection":{"module":"unregistered_effect_review","review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","role_projection":"unregistered_effect_roles"},"domain_id":"E-19","schema_version":"stage1_s6_module_role_projection.v1","structure_type_to_projection":{"insurance_claim_relation":{"module":"insurance_claims","role_projection":"insurance_relation_roles"}}}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"domain_id":"E-19","entries":[{"admission_rule":"source-backed SG-13 route and registered active domain required","module":"insurance_claims","priority_rank":19,"role_projection":"insurance_relation_roles","type_id":"insurance_claim_relation"}],"ordering_integration_required":true,"schema_version":"stage1_s6_structure_types.v1","unregistered_effect_policy":"UNREGISTERED_EFFECT_TYPE_REVIEW"}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"closure_status":"CLOSED_WITH_LOCAL_FILES","default_projection":{"module":"unregistered_effect_review","review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","role_projection":"unregistered_effect_roles"},"domain_id":"E-20","schema_version":"stage1_s6_module_role_projection.v1","structure_type_to_projection":{"ip_right_claim_relation":{"module":"ip_claims","role_projection":"ip_right_infringement_roles"}}}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"domain_id":"E-20","entries":[{"admission_rule":"source-backed SG-13 route and registered active domain required","module":"ip_claims","priority_rank":20,"role_projection":"ip_right_infringement_roles","type_id":"ip_right_claim_relation"}],"ordering_integration_required":true,"schema_version":"stage1_s6_structure_types.v1","unregistered_effect_policy":"UNREGISTERED_EFFECT_TYPE_REVIEW"}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"closure_status":"CLOSED_WITH_LOCAL_FILES","default_projection":{"module":"unregistered_effect_review","review_code":"UNREGISTERED_EFFECT_TYPE_REVIEW","role_projection":"unregistered_effect_roles"},"domain_id":"E-21","schema_version":"stage1_s6_module_role_projection.v1","structure_type_to_projection":{"media_personality_claim_relation":{"module":"media_personality_rights","role_projection":"publication_personality_roles"}}}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"domain_id":"E-21","entries":[{"admission_rule":"source-backed SG-13 route and registered active domain required","module":"media_personality_rights","priority_rank":21,"role_projection":"publication_personality_roles","type_id":"media_personality_claim_relation"}],"ordering_integration_required":true,"schema_version":"stage1_s6_structure_types.v1","unregistered_effect_policy":"UNREGISTERED_EFFECT_TYPE_REVIEW"}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s2_module_role_projection.v1",
|
||||||
|
"domain_id": "X1",
|
||||||
|
"legacy_projection_source": {
|
||||||
|
"path": "S_0_GPT/legacy/part4/structure_projection_contract.json",
|
||||||
|
"sha256": "9e615dda7e26e23fcaae381ed05763d16e4887909400277c9996919e09398398"
|
||||||
|
},
|
||||||
|
"structure_type_to_projection": {
|
||||||
|
"succession_notice_lien_asset_defense": {
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures"
|
||||||
|
},
|
||||||
|
"general_legal_effect": {
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default_projection": {
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW"
|
||||||
|
},
|
||||||
|
"closure_status": "CLOSED_WITH_LOCAL_FILES"
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s2_structure_types.v1",
|
||||||
|
"domain_id": "X1",
|
||||||
|
"legacy_contract": {
|
||||||
|
"canonical_type_set_frozen": true,
|
||||||
|
"source": "1. Stage_1/v.7/extension_research/선행구축/S_0_GPT/legacy/part3/structure_type_contract.json"
|
||||||
|
},
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type_id": "succession_notice_lien_asset_defense",
|
||||||
|
"priority_rank": 10,
|
||||||
|
"legacy_source": "B5",
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures",
|
||||||
|
"admission_rule": "domain payload or SG-13 candidate must be source-backed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"legacy_source": "Part3 fallback",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"admission_rule": "registered route only; E-00 unregistered material remains review"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noncanonical_preservation": "nonrouting_labels",
|
||||||
|
"ordering_preserved": true
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_module_role_projection.v1",
|
||||||
|
"domain_id": "X2",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"structure_type": "asset_state",
|
||||||
|
"module": "asset_state",
|
||||||
|
"role_projection": "asset_state_projection",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"structure_type": "valuation",
|
||||||
|
"module": "valuation",
|
||||||
|
"role_projection": "legal_calculation_object",
|
||||||
|
"source": "legacy_preserved_or_additive_registered"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_projection": null,
|
||||||
|
"missing_mapping_policy": {
|
||||||
|
"emit_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_domain_structure_types.v1",
|
||||||
|
"domain_id": "X2",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"legacy_source_domain": "B3",
|
||||||
|
"structure_types": [
|
||||||
|
{
|
||||||
|
"type_id": "asset_state",
|
||||||
|
"priority_rank": 4,
|
||||||
|
"module": "asset_state",
|
||||||
|
"role_projection": "asset_state_projection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "valuation",
|
||||||
|
"priority_rank": 6,
|
||||||
|
"module": "valuation",
|
||||||
|
"role_projection": "legal_calculation_object"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"admission_rules": {
|
||||||
|
"registered_membership_required": true,
|
||||||
|
"multiple_types_allowed": true,
|
||||||
|
"source_order_deduplicated": true,
|
||||||
|
"nonregistered_type_review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"silent_general_legal_effect_fallback_forbidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s2_module_role_projection.v1",
|
||||||
|
"domain_id": "X3",
|
||||||
|
"legacy_projection_source": {
|
||||||
|
"path": "S_0_GPT/legacy/part4/structure_projection_contract.json",
|
||||||
|
"sha256": "9e615dda7e26e23fcaae381ed05763d16e4887909400277c9996919e09398398"
|
||||||
|
},
|
||||||
|
"structure_type_to_projection": {
|
||||||
|
"succession_notice_lien_asset_defense": {
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures"
|
||||||
|
},
|
||||||
|
"general_legal_effect": {
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default_projection": {
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"review_code": "UNREGISTERED_EFFECT_TYPE_REVIEW"
|
||||||
|
},
|
||||||
|
"closure_status": "CLOSED_WITH_LOCAL_FILES"
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_s2_structure_types.v1",
|
||||||
|
"domain_id": "X3",
|
||||||
|
"legacy_contract": {
|
||||||
|
"canonical_type_set_frozen": true,
|
||||||
|
"source": "1. Stage_1/v.7/extension_research/선행구축/S_0_GPT/legacy/part3/structure_type_contract.json"
|
||||||
|
},
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type_id": "succession_notice_lien_asset_defense",
|
||||||
|
"priority_rank": 10,
|
||||||
|
"legacy_source": "B5",
|
||||||
|
"module": "succession_notice_lien_asset_defense",
|
||||||
|
"role_projection": "linked_structures",
|
||||||
|
"admission_rule": "domain payload or SG-13 candidate must be source-backed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type_id": "general_legal_effect",
|
||||||
|
"priority_rank": 11,
|
||||||
|
"legacy_source": "Part3 fallback",
|
||||||
|
"module": "legal_effect",
|
||||||
|
"role_projection": "legal_effect_roles",
|
||||||
|
"admission_rule": "registered route only; E-00 unregistered material remains review"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noncanonical_preservation": "nonrouting_labels",
|
||||||
|
"ordering_preserved": true
|
||||||
|
}
|
||||||
+150
@@ -0,0 +1,150 @@
|
|||||||
|
{
|
||||||
|
"$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": {
|
||||||
|
"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"}
|
||||||
|
}
|
||||||
|
}
|
||||||
+331
@@ -0,0 +1,331 @@
|
|||||||
|
{
|
||||||
|
"$id": "https://schemas.liti-agent.local/stage1/part3/structure_seed_bundle.schema.json",
|
||||||
|
"title": "stage1_structure_seed_bundle.v1",
|
||||||
|
"description": "Part 3 L0 산출 — 구조 seed 묶음. 레코드는 Θ 의 (도메인, type_id) 쌍마다 하나. 열거를 하드코딩하지 않는다 — type_id·도메인 ID·검토 코드는 패턴만 강제한다(도메인 증설 시 스키마 무수정).",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"structure_seed_bundle"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"structure_seed_bundle": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"schema_version",
|
||||||
|
"status",
|
||||||
|
"expected_runnable_domain_ids",
|
||||||
|
"theta",
|
||||||
|
"structure_records",
|
||||||
|
"unattached_routes",
|
||||||
|
"unattached_bo",
|
||||||
|
"counts",
|
||||||
|
"created_by"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {
|
||||||
|
"const": "stage1_structure_seed_bundle.v1"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"enum": [
|
||||||
|
"READY",
|
||||||
|
"READY_WITH_REVIEW"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"registry_version": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unattached_routes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"signal_id",
|
||||||
|
"type_id",
|
||||||
|
"reason"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"signal_id": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type_id": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"reason": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unattached_bo": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"bo_id",
|
||||||
|
"reason"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"bo_id": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"reason": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"counts": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"structure_records",
|
||||||
|
"declared_structure_types_total",
|
||||||
|
"sg13_route_total",
|
||||||
|
"routes_attached",
|
||||||
|
"routes_unattached",
|
||||||
|
"bo_total",
|
||||||
|
"bo_attached",
|
||||||
|
"bo_unattached",
|
||||||
|
"sg11_total",
|
||||||
|
"sg11_linked",
|
||||||
|
"sg11_unlinked"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"structure_records": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"declared_structure_types_total": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"sg13_route_total": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"routes_attached": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"routes_unattached": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"bo_total": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"bo_attached": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"bo_unattached": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"sg11_total": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"sg11_linked": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"sg11_unlinked": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"created_by": {
|
||||||
|
"const": "Task_C_LE_L0_structure_seed_reducer"
|
||||||
|
},
|
||||||
|
"signal_manifest_transaction_id": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^S5TX-[a-f0-9]{20}$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+221
-1
@@ -824,6 +824,206 @@
|
|||||||
"path": "contracts/signals/s5_execution_contract.v2.json",
|
"path": "contracts/signals/s5_execution_contract.v2.json",
|
||||||
"sha256": "311ac8721ec297bc86e54c83f0866d61629bd99f15746f86bbbff32914d35e02"
|
"sha256": "311ac8721ec297bc86e54c83f0866d61629bd99f15746f86bbbff32914d35e02"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-00/module_role_projection.json",
|
||||||
|
"sha256": "278ecdd4ac6c004f9994d6357ef439d089456a5e65bed0f74f49170ba3315932"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-00/structure_types.json",
|
||||||
|
"sha256": "ae4a94fdbb0e2a8e0273a259e38c28774a3f2f24985878286f18a9ae05cbabaa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-01/module_role_projection.json",
|
||||||
|
"sha256": "cb263ca339f7b749a51b57fa12d915fda3644f09e648d15a84202651a23a84fb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-01/structure_types.json",
|
||||||
|
"sha256": "f2c423a0346a347292b3ca33d3a9f33a82d540e83cbb5f79bb8dae7161c68b26"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-02/module_role_projection.json",
|
||||||
|
"sha256": "e21465cf806fb080d75f89526f9c142010c392ee87454a5a19248b9a80eb7c28"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-02/structure_types.json",
|
||||||
|
"sha256": "e470dd3c96f2f9b8bfa6c2c97b05259281005a84b6eea24c38d322abdf16ad8e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-03/module_role_projection.json",
|
||||||
|
"sha256": "f693146dfb3ba2ac5fa6a6221c6b2dc7e32084acb06823b1f46f423a56546704"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-03/structure_types.json",
|
||||||
|
"sha256": "70c579a03c41226e6383fe7b4aa5e60f4b0f5a723db30f6292dd2a0bde4d17bb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-04/module_role_projection.json",
|
||||||
|
"sha256": "4c4bd473a12ad1dfcc8c9392503cd057017385a21287a89cb7ea147f3cf7d2d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-04/structure_types.json",
|
||||||
|
"sha256": "e8b15e9e5a39a488bf9ef660d69c7bc2db832248eb33788ba2ae54a9b5200e73"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-05/module_role_projection.json",
|
||||||
|
"sha256": "7fabfa3893a138dccb4e5bc797444f5f999c0e51ec6b0c8fbe0211eff3f03a6c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-05/structure_types.json",
|
||||||
|
"sha256": "22707fbce849991c099a08885cac929d1ba842aa896945f9921c3214e4c701d0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-06/module_role_projection.json",
|
||||||
|
"sha256": "805b73b2b15cb720b62cd83455de85a67bda434255b69d588eb98f2527f88b71"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-06/structure_types.json",
|
||||||
|
"sha256": "a6520f72813caeaf72d62fd63eca10a1f46fd4fb3ffd97f97f222f37c53e8663"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-07/module_role_projection.json",
|
||||||
|
"sha256": "9920ed415f9768beb517100e97fa7d2eb862ca90ca83f802ea460f8f6bbf2445"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-07/structure_types.json",
|
||||||
|
"sha256": "fd4946e24cacc3bcc74c7fee5ce7fca99d3e5f2339fe3a9d698c54e2626a6c01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-08/module_role_projection.json",
|
||||||
|
"sha256": "4a8c284b5d3bbf8ca224e9f08398e6f563b00cec6de72f5128acc4cd8425eced"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-08/structure_types.json",
|
||||||
|
"sha256": "a7ee6782c6541d0c6f6f6107a1d3615c2e7003a979741f2afc9d5d686a59e51d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-09/module_role_projection.json",
|
||||||
|
"sha256": "ab93656808a832cfd73a6d75ed28d75e50e6cc66c67d59d765fcb8bbf638205f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-09/structure_types.json",
|
||||||
|
"sha256": "c374b8b912679cc765b152c58ecd8320898f947b799e44ca509aa622669d7f4c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-10/module_role_projection.json",
|
||||||
|
"sha256": "80f760d9d8516cc81ebe503a2967418c43e11f8b8410231081111483dfa6477d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-10/structure_types.json",
|
||||||
|
"sha256": "07be97d40e422e14e570574c4e1da875307fe487c39e2ba9862ffd4b1623b748"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-11/module_role_projection.json",
|
||||||
|
"sha256": "0a55ce91b1845d64d085710110aec22bfcea66967ab5fce3a4acf5179faf65f1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-11/structure_types.json",
|
||||||
|
"sha256": "6e565bab389f59866a0eafa8b7b7ff89f90ef2363ced3d9a4240100d3b3c3b90"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-12/module_role_projection.json",
|
||||||
|
"sha256": "207ef7d4a1300c12aa54cb1defa1110a6a9d18850d0fc4e144baebe6ff2b3c27"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-12/structure_types.json",
|
||||||
|
"sha256": "ef97f4b0ed05637165bfb211fc9be0cb3bcf0523e6b5a645c007e8dc2825bea9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-13/module_role_projection.json",
|
||||||
|
"sha256": "412df6800be576d7f789bc7fc2045091eb9c49fd346e442ba42d6f29300f7313"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-13/structure_types.json",
|
||||||
|
"sha256": "cc18073ca443e98a34e139c08d11121ceb7d4ecd366ad3b3d5cb65ddb53e36f7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-14/module_role_projection.json",
|
||||||
|
"sha256": "cead5634615b6b417390b37a948a050ea89cba37485bb79617bd1296c06829a5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-14/structure_types.json",
|
||||||
|
"sha256": "5b0025a720a7b9f1ad7c74ce8d79f0969b282690abc0e3171e28dbf41933153b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-15/module_role_projection.json",
|
||||||
|
"sha256": "873f921bbbc86fc07a0c1ecfcbcacee67b1d9592b2efd7319290a10e8c1ba018"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-15/structure_types.json",
|
||||||
|
"sha256": "784ceabd17c6de6d95a30f52dd5fd4036946fbedc556f2bad536225057a0f5fe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-16/module_role_projection.json",
|
||||||
|
"sha256": "24ad172b32d4942be6f4031c582f928d3c6feb6e442027e3ee02700519744b11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-16/structure_types.json",
|
||||||
|
"sha256": "b0bb30602ec424d35359c1a676ccca2bb2eda4a676997753da44d1dbbcc1f0b3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-17/module_role_projection.json",
|
||||||
|
"sha256": "498e2320ad04a907680b8588ee1b1b9ee7487e131bece96a601979d60796f5a4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-17/structure_types.json",
|
||||||
|
"sha256": "d306dbdf583453d51f643eb8398920efb1bb5bfa82b3a600caa79f45f790b2eb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-18/module_role_projection.json",
|
||||||
|
"sha256": "b0699a793cdc52e6f74e9df3254f403858e83080be9e402bf3c7dae3cd8ffd0e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-18/structure_types.json",
|
||||||
|
"sha256": "ce4eae3a54bfe7ac31f16b657cc0a891580e025f7bfecb1812c015e7fd0c2500"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-19/module_role_projection.json",
|
||||||
|
"sha256": "00950a8c76d4367ccd6dc000476c810df33e49b07fce124545e350530b008b99"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-19/structure_types.json",
|
||||||
|
"sha256": "58b9503558bcab3fb7ae76532659d54694208d813197e97bac148d50795feb22"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-20/module_role_projection.json",
|
||||||
|
"sha256": "7aef9706b50161693005ee61fc576a1562929fdf20830a3187fdda2a94fae8e3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-20/structure_types.json",
|
||||||
|
"sha256": "6766546fd61d3f46f0d0b71bc3c41d5c63a525954b2568d8003c9f5e90234074"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-21/module_role_projection.json",
|
||||||
|
"sha256": "de6b0f29ad8100744a1a4fe8393a44a8a921d604b5244f832256450cb547722a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/E-21/structure_types.json",
|
||||||
|
"sha256": "b027ff04f21cc4a86c8285e2cde6f8fd23e0ff3a3aeb3fa61a9bd10142814e36"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/X1/module_role_projection.json",
|
||||||
|
"sha256": "1bceacd99f408384fec9547262017d8c857dbdb58ae906446c45ce8f1e322920"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/X1/structure_types.json",
|
||||||
|
"sha256": "3344a27beb548c7d0b083b6e442a8f70ca89ce02d22614fda83203d3ff53e7e9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/X2/module_role_projection.json",
|
||||||
|
"sha256": "9282422d06035a78b592c110c197c1afa58e78294566eecd2666f0c61820cd5f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/X2/structure_types.json",
|
||||||
|
"sha256": "f51c0404bab1c5f4506012460ba02faf2cee6c958d49ee353095366defacd2ec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/X3/module_role_projection.json",
|
||||||
|
"sha256": "93d3709c7d01132b82f0445d18eab7e666ea8d7fe2afe9b308bf87af5477be10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "domains/X3/structure_types.json",
|
||||||
|
"sha256": "91f7ef0a971b519bd7fea8fb2efc61770afeb83b3a6ee774f153aea28a9de913"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "domains/_common/common_worker_contract.md",
|
"path": "domains/_common/common_worker_contract.md",
|
||||||
"sha256": "f133c54c5a9353ae14efc4d7a27d9bee18872b6727713a9a107778eef8990fb1"
|
"sha256": "f133c54c5a9353ae14efc4d7a27d9bee18872b6727713a9a107778eef8990fb1"
|
||||||
@@ -1104,6 +1304,14 @@
|
|||||||
"path": "platform/schemas/domain_slice.schema.v2.json",
|
"path": "platform/schemas/domain_slice.schema.v2.json",
|
||||||
"sha256": "212a405088e7cf7ba2c65528a1c716938c946df7fe3bae3256b613051ed31aa3"
|
"sha256": "212a405088e7cf7ba2c65528a1c716938c946df7fe3bae3256b613051ed31aa3"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "platform/schemas/legal_effect_structures.schema.json",
|
||||||
|
"sha256": "e2879c7a6d7f163c0729e8b863c5ef79b1094482eace78e6b8e1c7d69ce201e3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "platform/schemas/structure_seed_bundle.schema.json",
|
||||||
|
"sha256": "b7af9e422b6ac3876cffea39ec4f617eea76a631a57dfdfcd57d3785a83c667a"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "routing/extension_payload_key_declarations.v1.json",
|
"path": "routing/extension_payload_key_declarations.v1.json",
|
||||||
"sha256": "1684e13896bc9dfd0e7aa1a3b01354f58b2fa6ed00908bec6f861bce4b47235c"
|
"sha256": "1684e13896bc9dfd0e7aa1a3b01354f58b2fa6ed00908bec6f861bce4b47235c"
|
||||||
@@ -1460,6 +1668,18 @@
|
|||||||
"path": "stage1_runtime/stage_a_context_builder.txt",
|
"path": "stage1_runtime/stage_a_context_builder.txt",
|
||||||
"sha256": "68d984f9d565034c73721dc97c5aab0f9cdb8a8b46e1bf13eabd8c136ec4bcc0"
|
"sha256": "68d984f9d565034c73721dc97c5aab0f9cdb8a8b46e1bf13eabd8c136ec4bcc0"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "stage1_runtime/structure_index_compiler.py",
|
||||||
|
"sha256": "c400adfb7c58e340a17e67aebcdc4a7cf92c35c422f418451c79ce7c531d201f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "stage1_runtime/structure_index_compiler.txt",
|
||||||
|
"sha256": "c400adfb7c58e340a17e67aebcdc4a7cf92c35c422f418451c79ce7c531d201f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "stage1_runtime/structure_index_policy.v1.json",
|
||||||
|
"sha256": "823a7e734a7bc9433966a5629a5a0b153a067c92d8570072fa14a5c42bb722bd"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "stage1_runtime/worker_output_validator.py",
|
"path": "stage1_runtime/worker_output_validator.py",
|
||||||
"sha256": "e5b46f929082faf621731ffb8f4864ccb9bba461ab23caa56d1bcfe5b073fd9d"
|
"sha256": "e5b46f929082faf621731ffb8f4864ccb9bba461ab23caa56d1bcfe5b073fd9d"
|
||||||
@@ -1486,6 +1706,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"program_release_status": "STAGE1_NOT_RELEASE_READY",
|
"program_release_status": "STAGE1_NOT_RELEASE_READY",
|
||||||
"runtime_artifact_count": 371,
|
"runtime_artifact_count": 426,
|
||||||
"schema_version": "stage1_runtime_manifest.v1"
|
"schema_version": "stage1_runtime_manifest.v1"
|
||||||
}
|
}
|
||||||
|
|||||||
+529
@@ -0,0 +1,529 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""structure_index_compiler — Stage 1 Part 3 구조 seed 조립기.
|
||||||
|
|
||||||
|
정본 근거
|
||||||
|
전략 stage_1_part_3_개정_신전략서.md §2(D-4) · §4.2(L0-1~L0-8)
|
||||||
|
정책 Default_Agent/stage1_runtime/structure_index_policy.v1.json
|
||||||
|
계약 Θ = SG-01.expected_runnable_domain_ids 에 대한 domain_config.structure_types 합집합
|
||||||
|
구조 레코드 = (도메인, type_id) 쌍마다 하나 · 도메인 간 병합 금지
|
||||||
|
전순서 order_key = (priority_rank, registry_index entries 위치, type_id)
|
||||||
|
|
||||||
|
이 모듈은 순수하다 — 파일을 읽지도 쓰지도 않는다. 호출자가 적재한 문서를 받아
|
||||||
|
{structure_seed_bundle, review_items, audit} 를 돌려준다. 시각·난수를 쓰지 않으므로
|
||||||
|
같은 입력이면 같은 출력이다(L-n).
|
||||||
|
|
||||||
|
사건 종류 이름·도메인 목록·유형 열거를 고정하지 않는다(137종 일반성 §8.1).
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
SCHEMA_VERSION = "stage1_structure_seed_bundle.v1"
|
||||||
|
POLICY_SCHEMA_VERSION = "stage1_structure_index_policy.v1"
|
||||||
|
|
||||||
|
# 검토 코드 — registry 선언(UNREGISTERED_EFFECT_TYPE_REVIEW)과 신전략서 §4.2 의 신설분.
|
||||||
|
REVIEW_UNREGISTERED = "UNREGISTERED_EFFECT_TYPE_REVIEW"
|
||||||
|
REVIEW_SLICE_DIVERGENCE = "PART3_SLICE_PROJECTION_DIVERGENCE"
|
||||||
|
REVIEW_SIDECAR_MISMATCH = "PART3_REGISTRY_SIDECAR_MISMATCH"
|
||||||
|
REVIEW_CROSS_DOMAIN = "PART3_EFFECT_TYPE_CROSS_DOMAIN"
|
||||||
|
REVIEW_CALC_DANGLING = "PART3_CALCULATION_REF_DANGLING"
|
||||||
|
REVIEW_BO_UNATTACHED = "PART3_BO_UNATTACHED"
|
||||||
|
REVIEW_BO_UNRESOLVED = "PART3_BO_DOMAIN_UNRESOLVED"
|
||||||
|
|
||||||
|
SG13_ROUTE_RECORD_TYPE = "legal_effect_route"
|
||||||
|
|
||||||
|
|
||||||
|
def _strings(value: Any) -> list[str]:
|
||||||
|
if not isinstance(value, list):
|
||||||
|
return []
|
||||||
|
return [item for item in value if isinstance(item, str) and item]
|
||||||
|
|
||||||
|
|
||||||
|
def _dict(value: Any) -> dict:
|
||||||
|
return value if isinstance(value, dict) else {}
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 사이드카 정규화 리더 — 방언 지도(신전략서 §1.2): 목록 키 3종 · 투영 키 3종 · 정책 자리 4갈래.
|
||||||
|
# 방언을 다 읽지 않은 리더는 결손을 만들어 낸다(구 전략서 §1.5 의 실패 기록) —
|
||||||
|
# 세 방언 전부를 인정하고, 세지 않은 필드를 공통 필드로 승격하지 않는다.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
_LIST_KEYS = ("entries", "structure_types", "types")
|
||||||
|
_PROJECTION_KEYS = ("projections", "mappings", "structure_type_to_projection")
|
||||||
|
_FILE_POLICY_KEYS = ("admission_rules", "unregistered_effect_policy", "fallback_policy")
|
||||||
|
|
||||||
|
|
||||||
|
def read_sidecar_types(doc: Any) -> dict[str, Any]:
|
||||||
|
"""structure_types.json 방언을 정규화한다.
|
||||||
|
|
||||||
|
반환: {"list_key": str|None, "schema_version": str|None,
|
||||||
|
"items": [{"type_id", "admission_rule"?, "registered"?}],
|
||||||
|
"file_policy_key": str|None, "file_policy": dict|None,
|
||||||
|
"legacy_contract_style": bool}
|
||||||
|
"""
|
||||||
|
root = _dict(doc)
|
||||||
|
list_key = None
|
||||||
|
rows: list[Any] = []
|
||||||
|
for key in _LIST_KEYS:
|
||||||
|
if isinstance(root.get(key), list):
|
||||||
|
list_key = key
|
||||||
|
rows = root[key]
|
||||||
|
break
|
||||||
|
items = []
|
||||||
|
for row in rows:
|
||||||
|
row = _dict(row)
|
||||||
|
type_id = row.get("type_id") or row.get("structure_type") or row.get("id")
|
||||||
|
if not isinstance(type_id, str) or not type_id:
|
||||||
|
continue
|
||||||
|
item: dict[str, Any] = {"type_id": type_id}
|
||||||
|
if isinstance(row.get("admission_rule"), str) and row.get("admission_rule"):
|
||||||
|
item["admission_rule"] = row["admission_rule"]
|
||||||
|
if isinstance(row.get("registered"), bool):
|
||||||
|
item["registered"] = row["registered"]
|
||||||
|
items.append(item)
|
||||||
|
file_policy_key = None
|
||||||
|
file_policy = None
|
||||||
|
for key in _FILE_POLICY_KEYS:
|
||||||
|
if root.get(key) is not None:
|
||||||
|
file_policy_key = key
|
||||||
|
file_policy = root[key]
|
||||||
|
break
|
||||||
|
legacy_style = file_policy_key is None and any(
|
||||||
|
key in root for key in ("legacy_contract", "noncanonical_preservation", "ordering_preserved"))
|
||||||
|
return {"list_key": list_key,
|
||||||
|
"schema_version": root.get("schema_version"),
|
||||||
|
"items": items,
|
||||||
|
"file_policy_key": file_policy_key,
|
||||||
|
"file_policy": file_policy,
|
||||||
|
"legacy_contract_style": legacy_style}
|
||||||
|
|
||||||
|
|
||||||
|
def read_sidecar_projection(doc: Any) -> dict[str, Any]:
|
||||||
|
"""module_role_projection.json 방언을 정규화한다.
|
||||||
|
|
||||||
|
반환: {"projection_key": str|None, "mappings": {type_id: {"module", "role_projection"}},
|
||||||
|
"default_projection": dict|None, "missing_mapping_policy": dict|None}
|
||||||
|
"""
|
||||||
|
root = _dict(doc)
|
||||||
|
mappings: dict[str, dict[str, Any]] = {}
|
||||||
|
projection_key = None
|
||||||
|
for key in _PROJECTION_KEYS:
|
||||||
|
value = root.get(key)
|
||||||
|
if isinstance(value, list):
|
||||||
|
projection_key = key
|
||||||
|
for row in value:
|
||||||
|
row = _dict(row)
|
||||||
|
type_id = row.get("structure_type") or row.get("type_id")
|
||||||
|
if isinstance(type_id, str) and type_id:
|
||||||
|
mappings[type_id] = {"module": row.get("module"),
|
||||||
|
"role_projection": row.get("role_projection")}
|
||||||
|
break
|
||||||
|
if isinstance(value, dict):
|
||||||
|
projection_key = key
|
||||||
|
for type_id, row in value.items():
|
||||||
|
row = _dict(row)
|
||||||
|
if isinstance(type_id, str) and type_id:
|
||||||
|
mappings[type_id] = {"module": row.get("module"),
|
||||||
|
"role_projection": row.get("role_projection")}
|
||||||
|
break
|
||||||
|
default_projection = root.get("default_projection")
|
||||||
|
if not isinstance(default_projection, dict):
|
||||||
|
default_projection = None
|
||||||
|
missing_policy = root.get("missing_mapping_policy")
|
||||||
|
if not isinstance(missing_policy, dict):
|
||||||
|
missing_policy = None
|
||||||
|
return {"projection_key": projection_key, "mappings": mappings,
|
||||||
|
"default_projection": default_projection,
|
||||||
|
"missing_mapping_policy": missing_policy}
|
||||||
|
|
||||||
|
|
||||||
|
def admission_policy_for(sidecar_types: dict[str, Any], item: dict[str, Any]) -> str:
|
||||||
|
"""두 단 정의(신전략서 §1.2) — 파일 단위 정책(3키 중 있는 것) → 없으면 항목 admission_rule.
|
||||||
|
|
||||||
|
73/73 이 채워지는 값이다. 어느 단도 없으면 빈 문자열이 아니라 명시 문자열을 남긴다 —
|
||||||
|
없는 것을 있는 것처럼 채우지 않되, 부재 자체는 보이게 한다.
|
||||||
|
"""
|
||||||
|
key = sidecar_types.get("file_policy_key")
|
||||||
|
if key:
|
||||||
|
return "file:%s" % key
|
||||||
|
rule = item.get("admission_rule")
|
||||||
|
if isinstance(rule, str) and rule:
|
||||||
|
return "item:admission_rule"
|
||||||
|
return "absent"
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_source_domains(value: Any, alias_targets: dict[str, Any],
|
||||||
|
known_domain_ids: set[str]) -> list[str]:
|
||||||
|
"""BO provenance.source_domain 을 registry 도메인 ID 목록으로 해소한다(L0-7b ①).
|
||||||
|
|
||||||
|
registry ID 면 [값]. 아니면 별칭표의 목록(다중 타깃 실재 — 단일 primary 개념 없음).
|
||||||
|
별칭표에도 없으면 빈 목록 — 호출자가 PART3_BO_DOMAIN_UNRESOLVED 로 격리한다.
|
||||||
|
"""
|
||||||
|
if not isinstance(value, str) or not value:
|
||||||
|
return []
|
||||||
|
if value in known_domain_ids:
|
||||||
|
return [value]
|
||||||
|
targets = _strings(_dict(alias_targets).get(value))
|
||||||
|
return [d for d in targets if d in known_domain_ids]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 본체
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def compile_structure_seeds(activation_manifest: Any,
|
||||||
|
registry_index: Any,
|
||||||
|
domain_configs: dict[str, Any],
|
||||||
|
domain_sidecars: dict[str, Any],
|
||||||
|
policy: Any,
|
||||||
|
alias_declarations: Any,
|
||||||
|
sg13_records: list[Any],
|
||||||
|
sg05_records: list[Any],
|
||||||
|
sg11_records: list[Any],
|
||||||
|
domain_envelopes: dict[str, Any],
|
||||||
|
bo_records: list[Any],
|
||||||
|
slice_documents: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
"""L0-1 ~ L0-8. 반환 dict 는 theta 와 order_key 를 반드시 싣는다(PART3_COMPILER_STALE 의 대조면)."""
|
||||||
|
policy_root = _dict(policy)
|
||||||
|
if policy_root.get("schema_version") != POLICY_SCHEMA_VERSION:
|
||||||
|
raise ValueError("PART3_POLICY_INVALID:%s" % policy_root.get("schema_version"))
|
||||||
|
|
||||||
|
manifest_root = _dict(activation_manifest).get("domain_activation_manifest",
|
||||||
|
_dict(activation_manifest))
|
||||||
|
expected_runnable = _strings(_dict(manifest_root).get("expected_runnable_domain_ids"))
|
||||||
|
if not expected_runnable:
|
||||||
|
raise ValueError("PART3_EXPECTED_RUNNABLE_EMPTY")
|
||||||
|
|
||||||
|
index_root = _dict(_dict(registry_index).get("domain_registry_index", registry_index))
|
||||||
|
index_entries = [row for row in (index_root.get("entries") or []) if isinstance(row, dict)]
|
||||||
|
index_position = {row.get("domain_id"): position
|
||||||
|
for position, row in enumerate(index_entries)
|
||||||
|
if isinstance(row.get("domain_id"), str)}
|
||||||
|
known_domain_ids = set(index_position)
|
||||||
|
|
||||||
|
review_items: list[dict[str, Any]] = []
|
||||||
|
audit: dict[str, Any] = {"sidecar_dialects": {}, "input_gaps": [],
|
||||||
|
"unlinked_sg11": [], "dangling_calculation_refs": {}}
|
||||||
|
|
||||||
|
def review(code: str, severity: str, domain_id: str | None, detail: dict[str, Any]) -> None:
|
||||||
|
item = {"review_code": code, "severity": severity}
|
||||||
|
if domain_id is not None:
|
||||||
|
item["domain_id"] = domain_id
|
||||||
|
item.update(detail)
|
||||||
|
review_items.append(item)
|
||||||
|
|
||||||
|
# ---------------- L0-1 · Θ 조립 ----------------
|
||||||
|
declared_by_domain: dict[str, list[dict[str, Any]]] = {}
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
if domain_id not in known_domain_ids:
|
||||||
|
raise ValueError("PART3_DOMAIN_NOT_IN_REGISTRY:%s" % domain_id)
|
||||||
|
config_root = _dict(domain_configs.get(domain_id))
|
||||||
|
rows = [row for row in (config_root.get("structure_types") or [])
|
||||||
|
if isinstance(row, dict) and isinstance(row.get("type_id"), str)]
|
||||||
|
declared_by_domain[domain_id] = rows
|
||||||
|
theta = sorted({row["type_id"] for rows in declared_by_domain.values() for row in rows})
|
||||||
|
declaring_domains: dict[str, list[str]] = {}
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
for row in declared_by_domain[domain_id]:
|
||||||
|
declaring_domains.setdefault(row["type_id"], []).append(domain_id)
|
||||||
|
|
||||||
|
# ---------------- L0-2c · 사이드카 교차 검증 (레코드 생성보다 앞 — admission_policy 재료) ----------------
|
||||||
|
sidecar_norm: dict[str, dict[str, Any]] = {}
|
||||||
|
projection_norm: dict[str, dict[str, Any]] = {}
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
pair = _dict(domain_sidecars.get(domain_id))
|
||||||
|
declared_ids = sorted({row["type_id"] for row in declared_by_domain[domain_id]})
|
||||||
|
if not pair:
|
||||||
|
# 부재 규칙(신전략서 §5.5) — 선언 0 도메인의 부재만 정상이다.
|
||||||
|
if declared_ids:
|
||||||
|
raise ValueError("PART3_SIDECAR_ABSENT_FOR_DECLARING_DOMAIN:%s" % domain_id)
|
||||||
|
audit["sidecar_dialects"][domain_id] = {"absent": True, "declared": 0}
|
||||||
|
continue
|
||||||
|
types_doc = read_sidecar_types(pair.get("structure_types"))
|
||||||
|
projection_doc = read_sidecar_projection(pair.get("module_role_projection"))
|
||||||
|
sidecar_norm[domain_id] = types_doc
|
||||||
|
projection_norm[domain_id] = projection_doc
|
||||||
|
audit["sidecar_dialects"][domain_id] = {
|
||||||
|
"list_key": types_doc.get("list_key"),
|
||||||
|
"schema_version": types_doc.get("schema_version"),
|
||||||
|
"projection_key": projection_doc.get("projection_key"),
|
||||||
|
"file_policy_key": types_doc.get("file_policy_key"),
|
||||||
|
"legacy_contract_style": types_doc.get("legacy_contract_style"),
|
||||||
|
"item_count": len(types_doc.get("items") or []),
|
||||||
|
}
|
||||||
|
sidecar_ids = sorted({item["type_id"] for item in types_doc.get("items") or []})
|
||||||
|
if sidecar_ids != declared_ids:
|
||||||
|
review(REVIEW_SIDECAR_MISMATCH, "HARD_WARNING", domain_id, {
|
||||||
|
"detail": "type_set_mismatch",
|
||||||
|
"config_only": sorted(set(declared_ids) - set(sidecar_ids)),
|
||||||
|
"sidecar_only": sorted(set(sidecar_ids) - set(declared_ids)),
|
||||||
|
})
|
||||||
|
for type_id, mapping in sorted((projection_doc.get("mappings") or {}).items()):
|
||||||
|
config_row = next((row for row in declared_by_domain[domain_id]
|
||||||
|
if row["type_id"] == type_id), None)
|
||||||
|
if config_row is None:
|
||||||
|
continue
|
||||||
|
if (mapping.get("module") != config_row.get("module")
|
||||||
|
or mapping.get("role_projection") != config_row.get("role_projection")):
|
||||||
|
review(REVIEW_SIDECAR_MISMATCH, "HARD_WARNING", domain_id, {
|
||||||
|
"detail": "projection_mismatch", "type_id": type_id,
|
||||||
|
"config": {"module": config_row.get("module"),
|
||||||
|
"role_projection": config_row.get("role_projection")},
|
||||||
|
"sidecar": mapping,
|
||||||
|
})
|
||||||
|
|
||||||
|
# ---------------- L0-2 · 구조 레코드 — (도메인, type_id) 쌍마다 하나 ----------------
|
||||||
|
e13_extension_key = None
|
||||||
|
contract = _dict(policy_root.get("extension_key_rule"))
|
||||||
|
e13_extension_key = contract.get("extension_key") if isinstance(
|
||||||
|
contract.get("extension_key"), str) else None
|
||||||
|
e13_domain = contract.get("domain_id") if isinstance(contract.get("domain_id"), str) else None
|
||||||
|
|
||||||
|
records: dict[tuple[str, str], dict[str, Any]] = {}
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
types_doc = sidecar_norm.get(domain_id, {"items": []})
|
||||||
|
item_by_type = {item["type_id"]: item for item in types_doc.get("items") or []}
|
||||||
|
for row in declared_by_domain[domain_id]:
|
||||||
|
type_id = row["type_id"]
|
||||||
|
item = item_by_type.get(type_id, {})
|
||||||
|
record: dict[str, Any] = {
|
||||||
|
"structure_id": "LES-%s-%s" % (domain_id, type_id),
|
||||||
|
"domain_id": domain_id,
|
||||||
|
"type_id": type_id,
|
||||||
|
"priority_rank": row.get("priority_rank"),
|
||||||
|
"order_key": [row.get("priority_rank"), index_position[domain_id], type_id],
|
||||||
|
"module": row.get("module"),
|
||||||
|
"role_projection": row.get("role_projection"),
|
||||||
|
"admission_policy": admission_policy_for(types_doc, item),
|
||||||
|
"routes": [],
|
||||||
|
"route_count": 0,
|
||||||
|
"evidence_refs": [],
|
||||||
|
"meeting_clause_refs": [],
|
||||||
|
"calculation_request_refs": [],
|
||||||
|
"source_bo_ids": [],
|
||||||
|
"review_codes": [],
|
||||||
|
"review_required": False,
|
||||||
|
}
|
||||||
|
if "admission_rule" in item:
|
||||||
|
record["admission_rule"] = item["admission_rule"]
|
||||||
|
if "registered" in item:
|
||||||
|
record["registered_declared"] = item["registered"]
|
||||||
|
if domain_id == e13_domain and e13_extension_key:
|
||||||
|
record["extension_key"] = e13_extension_key
|
||||||
|
if domain_id == "E-00":
|
||||||
|
# E-00 특칙 — 잔여 수집기 유래 구조는 검토 필수(신전략서 §4.2).
|
||||||
|
record["review_required"] = True
|
||||||
|
records[(domain_id, type_id)] = record
|
||||||
|
|
||||||
|
# ---------------- L0-2b · 슬라이스 투영 정합 ----------------
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
slice_doc = slice_documents.get(domain_id)
|
||||||
|
if slice_doc is None:
|
||||||
|
audit["input_gaps"].append({"kind": "slice_absent", "domain_id": domain_id})
|
||||||
|
continue
|
||||||
|
slice_root = _dict(_dict(slice_doc).get("stage_b_domain_slice", slice_doc))
|
||||||
|
declarations = _dict(slice_root.get("domain_declarations"))
|
||||||
|
slice_ids = sorted(_strings(declarations.get("structure_type_ids")))
|
||||||
|
config_ids = sorted({row["type_id"] for row in declared_by_domain[domain_id]})
|
||||||
|
if slice_ids != config_ids:
|
||||||
|
review(REVIEW_SLICE_DIVERGENCE, "HARD_WARNING", domain_id, {
|
||||||
|
"slice_only": sorted(set(slice_ids) - set(config_ids)),
|
||||||
|
"config_only": sorted(set(config_ids) - set(slice_ids)),
|
||||||
|
})
|
||||||
|
|
||||||
|
# ---------------- L0-3 · SG-13 조인 + L0-4 · 도메인 소속 ----------------
|
||||||
|
routes = [_dict(row) for row in sg13_records
|
||||||
|
if _dict(row).get("record_type") == SG13_ROUTE_RECORD_TYPE]
|
||||||
|
attached_route_count = 0
|
||||||
|
unattached_routes: list[dict[str, Any]] = []
|
||||||
|
for route in routes:
|
||||||
|
details = _dict(route.get("details"))
|
||||||
|
type_id = details.get("type_id")
|
||||||
|
route_domains = _strings(route.get("domain_ids"))
|
||||||
|
route_domain = route_domains[0] if route_domains else None
|
||||||
|
registered = details.get("registered")
|
||||||
|
route_ref = {
|
||||||
|
"signal_id": route.get("signal_id"),
|
||||||
|
"source_seed_id": details.get("source_seed_id"),
|
||||||
|
"registered": registered if isinstance(registered, bool) else None,
|
||||||
|
"status": route.get("status"),
|
||||||
|
}
|
||||||
|
if not isinstance(type_id, str) or type_id not in theta:
|
||||||
|
# Θ 밖 — 버리지 않는다. default_projection 이 있으면 검토 항목에 실어 함께 나른다.
|
||||||
|
projection = None
|
||||||
|
if route_domain and route_domain in projection_norm:
|
||||||
|
projection = projection_norm[route_domain].get("default_projection")
|
||||||
|
review(REVIEW_UNREGISTERED, "HARD_WARNING", route_domain, {
|
||||||
|
"type_id": type_id, "signal_id": route.get("signal_id"),
|
||||||
|
"default_projection": projection,
|
||||||
|
})
|
||||||
|
unattached_routes.append({"signal_id": route.get("signal_id"),
|
||||||
|
"type_id": type_id, "reason": "type_outside_theta"})
|
||||||
|
continue
|
||||||
|
target_keys = []
|
||||||
|
if route_domain and (route_domain, type_id) in records:
|
||||||
|
target_keys = [(route_domain, type_id)]
|
||||||
|
else:
|
||||||
|
# Θ 안이지만 자기 도메인이 선언하지 않은 유형 — 선언 도메인 전부에 붙이고 검토(L0-4).
|
||||||
|
target_keys = [(d, type_id) for d in declaring_domains.get(type_id, [])
|
||||||
|
if (d, type_id) in records]
|
||||||
|
if target_keys:
|
||||||
|
review(REVIEW_CROSS_DOMAIN, "HARD_WARNING", route_domain, {
|
||||||
|
"type_id": type_id, "signal_id": route.get("signal_id"),
|
||||||
|
"attached_to": ["%s:%s" % key for key in target_keys],
|
||||||
|
})
|
||||||
|
if not target_keys:
|
||||||
|
unattached_routes.append({"signal_id": route.get("signal_id"),
|
||||||
|
"type_id": type_id, "reason": "no_declaring_domain"})
|
||||||
|
review(REVIEW_UNREGISTERED, "HARD_WARNING", route_domain, {
|
||||||
|
"type_id": type_id, "signal_id": route.get("signal_id"),
|
||||||
|
"default_projection": None,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
attached_route_count += 1
|
||||||
|
for key in target_keys:
|
||||||
|
record = records[key]
|
||||||
|
record["routes"].append(dict(route_ref))
|
||||||
|
for field in ("evidence_refs", "meeting_clause_refs"):
|
||||||
|
merged = set(record[field]) | set(_strings(route.get(field)))
|
||||||
|
record[field] = sorted(merged)
|
||||||
|
if registered is False:
|
||||||
|
# 붙은 채로 검토 꼬리표를 단다(L-m2 — 분할은 부착 여부로만 가른다).
|
||||||
|
if REVIEW_UNREGISTERED not in record["review_codes"]:
|
||||||
|
record["review_codes"].append(REVIEW_UNREGISTERED)
|
||||||
|
record["review_required"] = True
|
||||||
|
review(REVIEW_UNREGISTERED, "SOFT_WARNING", key[0], {
|
||||||
|
"type_id": type_id, "signal_id": route.get("signal_id"),
|
||||||
|
"detail": "registered_false_route_attached",
|
||||||
|
})
|
||||||
|
declared_flag = record.get("registered_declared")
|
||||||
|
if isinstance(declared_flag, bool) and isinstance(registered, bool) \
|
||||||
|
and declared_flag != registered:
|
||||||
|
# types 방언의 registered ↔ SG-13 registered 대조(L0-2c 둘째 갈래).
|
||||||
|
if REVIEW_SIDECAR_MISMATCH not in record["review_codes"]:
|
||||||
|
record["review_codes"].append(REVIEW_SIDECAR_MISMATCH)
|
||||||
|
record["review_required"] = True
|
||||||
|
review(REVIEW_SIDECAR_MISMATCH, "HARD_WARNING", key[0], {
|
||||||
|
"detail": "registered_flag_mismatch", "type_id": type_id,
|
||||||
|
"sidecar": declared_flag, "route": registered,
|
||||||
|
})
|
||||||
|
|
||||||
|
# ---------------- L0-5 · module 부여는 L0-2 에서 config 원문으로 끝났다 ----------------
|
||||||
|
|
||||||
|
# ---------------- L0-6 · 계산 조인 — SG-11 signal_id ↔ 도메인 봉투 calculation_refs ----------------
|
||||||
|
# 봉투 부재는 침묵 스킵이 아니라 영수증 계수다(I-5 — expected_runnable 중 실재하는 것만 읽되
|
||||||
|
# 부재를 기록한다). 실주행에서 감시 도메인 봉투가 없는 것은 상시 경로다.
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
if domain_id not in domain_envelopes:
|
||||||
|
audit["input_gaps"].append({"kind": "domain_envelope_absent",
|
||||||
|
"domain_id": domain_id})
|
||||||
|
sg11_rows = [_dict(row) for row in sg11_records]
|
||||||
|
sg11_ids = {row.get("signal_id") for row in sg11_rows if isinstance(row.get("signal_id"), str)}
|
||||||
|
linked_sg11: set[str] = set()
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
envelope = _dict(_dict(domain_envelopes.get(domain_id)).get("domain_signal_envelope",
|
||||||
|
domain_envelopes.get(domain_id)))
|
||||||
|
refs = _strings(_dict(envelope).get("calculation_refs"))
|
||||||
|
if not refs:
|
||||||
|
continue
|
||||||
|
matched = sorted(set(refs) & sg11_ids)
|
||||||
|
dangling = sorted(set(refs) - sg11_ids)
|
||||||
|
if dangling:
|
||||||
|
audit["dangling_calculation_refs"][domain_id] = dangling
|
||||||
|
review(REVIEW_CALC_DANGLING, "HARD_WARNING", domain_id, {"dangling": dangling})
|
||||||
|
if matched:
|
||||||
|
linked_sg11.update(matched)
|
||||||
|
for (record_domain, _type_id), record in records.items():
|
||||||
|
if record_domain == domain_id:
|
||||||
|
merged = set(record["calculation_request_refs"]) | set(matched)
|
||||||
|
record["calculation_request_refs"] = sorted(merged)
|
||||||
|
# 봉투 참조에 연결되지 않은 SG-11(합성 id 포함)은 구조에 싣지 않되 형식화 기록(신전략서 L0-6).
|
||||||
|
audit["unlinked_sg11"] = sorted(sg11_ids - linked_sg11)
|
||||||
|
|
||||||
|
# ---------------- L0-7 · SG-05 근거 refs (도메인 단위 합집합) ----------------
|
||||||
|
for row in sg05_records:
|
||||||
|
row = _dict(row)
|
||||||
|
for domain_id in _strings(row.get("domain_ids")):
|
||||||
|
for (record_domain, _type_id), record in records.items():
|
||||||
|
if record_domain == domain_id:
|
||||||
|
for field in ("evidence_refs", "meeting_clause_refs"):
|
||||||
|
merged = set(record[field]) | set(_strings(row.get(field)))
|
||||||
|
record[field] = sorted(merged)
|
||||||
|
|
||||||
|
# ---------------- L0-7b · BO 부착 조인 ----------------
|
||||||
|
alias_targets = _dict(_dict(alias_declarations).get("legacy_alias_targets"))
|
||||||
|
bo_rows = [_dict(row) for row in bo_records]
|
||||||
|
bo_attached: list[str] = []
|
||||||
|
bo_unattached: list[dict[str, Any]] = []
|
||||||
|
for bo in bo_rows:
|
||||||
|
bo_id = bo.get("BO_ID") or bo.get("id")
|
||||||
|
provenance = _dict(bo.get("provenance"))
|
||||||
|
source_domain = provenance.get("source_domain")
|
||||||
|
resolved = resolve_source_domains(source_domain, alias_targets, known_domain_ids)
|
||||||
|
if not resolved:
|
||||||
|
review(REVIEW_BO_UNRESOLVED, "HARD_WARNING", None, {
|
||||||
|
"bo_id": bo_id, "source_domain": source_domain,
|
||||||
|
})
|
||||||
|
bo_unattached.append({"bo_id": bo_id, "reason": "source_domain_unresolved"})
|
||||||
|
continue
|
||||||
|
keywords = _strings(bo.get("Legal_Keywords"))
|
||||||
|
attached_here = False
|
||||||
|
for domain_id in resolved:
|
||||||
|
for type_id in keywords:
|
||||||
|
record = records.get((domain_id, type_id))
|
||||||
|
if record is not None:
|
||||||
|
attached_here = True
|
||||||
|
if isinstance(bo_id, str) and bo_id not in record["source_bo_ids"]:
|
||||||
|
record["source_bo_ids"].append(bo_id)
|
||||||
|
record["source_bo_ids"].sort()
|
||||||
|
if attached_here:
|
||||||
|
bo_attached.append(bo_id if isinstance(bo_id, str) else "")
|
||||||
|
else:
|
||||||
|
# 부착 실패는 침묵 금지(L0-7b ③).
|
||||||
|
review(REVIEW_BO_UNATTACHED, "HARD_WARNING", None, {
|
||||||
|
"bo_id": bo_id, "source_domain": source_domain,
|
||||||
|
"legal_keywords": keywords, "resolved_domains": resolved,
|
||||||
|
})
|
||||||
|
bo_unattached.append({"bo_id": bo_id, "reason": "no_matching_structure"})
|
||||||
|
|
||||||
|
# ---------------- 정렬 · 검토 재료 반영 · 산출 ----------------
|
||||||
|
ordered = sorted(records.values(),
|
||||||
|
key=lambda record: (record["order_key"][0] if isinstance(
|
||||||
|
record["order_key"][0], int) else 999,
|
||||||
|
record["order_key"][1], record["order_key"][2]))
|
||||||
|
for record in ordered:
|
||||||
|
record["route_count"] = len(record["routes"])
|
||||||
|
record["routes"].sort(key=lambda ref: (str(ref.get("signal_id")),
|
||||||
|
str(ref.get("source_seed_id"))))
|
||||||
|
record["review_codes"].sort()
|
||||||
|
if record["review_codes"]:
|
||||||
|
record["review_required"] = True
|
||||||
|
|
||||||
|
status = "READY_WITH_REVIEW" if review_items else "READY"
|
||||||
|
bundle = {
|
||||||
|
"schema_version": SCHEMA_VERSION,
|
||||||
|
"status": status,
|
||||||
|
"registry_version": index_root.get("registry_version"),
|
||||||
|
"expected_runnable_domain_ids": list(expected_runnable),
|
||||||
|
"theta": theta,
|
||||||
|
"structure_records": ordered,
|
||||||
|
"unattached_routes": sorted(unattached_routes,
|
||||||
|
key=lambda row: (str(row.get("signal_id")),
|
||||||
|
str(row.get("type_id")))),
|
||||||
|
"unattached_bo": sorted(bo_unattached, key=lambda row: str(row.get("bo_id"))),
|
||||||
|
"counts": {
|
||||||
|
"structure_records": len(ordered),
|
||||||
|
"declared_structure_types_total": sum(
|
||||||
|
len(rows) for rows in declared_by_domain.values()),
|
||||||
|
"sg13_route_total": len(routes),
|
||||||
|
"routes_attached": attached_route_count,
|
||||||
|
"routes_unattached": len(unattached_routes),
|
||||||
|
"bo_total": len(bo_rows),
|
||||||
|
"bo_attached": len(bo_attached),
|
||||||
|
"bo_unattached": len(bo_unattached),
|
||||||
|
"sg11_total": len(sg11_rows),
|
||||||
|
"sg11_linked": len(linked_sg11),
|
||||||
|
"sg11_unlinked": len(audit["unlinked_sg11"]),
|
||||||
|
},
|
||||||
|
"created_by": "Task_C_LE_L0_structure_seed_reducer",
|
||||||
|
}
|
||||||
|
return {"structure_seed_bundle": bundle, "review_items": review_items, "audit": audit}
|
||||||
+529
@@ -0,0 +1,529 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""structure_index_compiler — Stage 1 Part 3 구조 seed 조립기.
|
||||||
|
|
||||||
|
정본 근거
|
||||||
|
전략 stage_1_part_3_개정_신전략서.md §2(D-4) · §4.2(L0-1~L0-8)
|
||||||
|
정책 Default_Agent/stage1_runtime/structure_index_policy.v1.json
|
||||||
|
계약 Θ = SG-01.expected_runnable_domain_ids 에 대한 domain_config.structure_types 합집합
|
||||||
|
구조 레코드 = (도메인, type_id) 쌍마다 하나 · 도메인 간 병합 금지
|
||||||
|
전순서 order_key = (priority_rank, registry_index entries 위치, type_id)
|
||||||
|
|
||||||
|
이 모듈은 순수하다 — 파일을 읽지도 쓰지도 않는다. 호출자가 적재한 문서를 받아
|
||||||
|
{structure_seed_bundle, review_items, audit} 를 돌려준다. 시각·난수를 쓰지 않으므로
|
||||||
|
같은 입력이면 같은 출력이다(L-n).
|
||||||
|
|
||||||
|
사건 종류 이름·도메인 목록·유형 열거를 고정하지 않는다(137종 일반성 §8.1).
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
SCHEMA_VERSION = "stage1_structure_seed_bundle.v1"
|
||||||
|
POLICY_SCHEMA_VERSION = "stage1_structure_index_policy.v1"
|
||||||
|
|
||||||
|
# 검토 코드 — registry 선언(UNREGISTERED_EFFECT_TYPE_REVIEW)과 신전략서 §4.2 의 신설분.
|
||||||
|
REVIEW_UNREGISTERED = "UNREGISTERED_EFFECT_TYPE_REVIEW"
|
||||||
|
REVIEW_SLICE_DIVERGENCE = "PART3_SLICE_PROJECTION_DIVERGENCE"
|
||||||
|
REVIEW_SIDECAR_MISMATCH = "PART3_REGISTRY_SIDECAR_MISMATCH"
|
||||||
|
REVIEW_CROSS_DOMAIN = "PART3_EFFECT_TYPE_CROSS_DOMAIN"
|
||||||
|
REVIEW_CALC_DANGLING = "PART3_CALCULATION_REF_DANGLING"
|
||||||
|
REVIEW_BO_UNATTACHED = "PART3_BO_UNATTACHED"
|
||||||
|
REVIEW_BO_UNRESOLVED = "PART3_BO_DOMAIN_UNRESOLVED"
|
||||||
|
|
||||||
|
SG13_ROUTE_RECORD_TYPE = "legal_effect_route"
|
||||||
|
|
||||||
|
|
||||||
|
def _strings(value: Any) -> list[str]:
|
||||||
|
if not isinstance(value, list):
|
||||||
|
return []
|
||||||
|
return [item for item in value if isinstance(item, str) and item]
|
||||||
|
|
||||||
|
|
||||||
|
def _dict(value: Any) -> dict:
|
||||||
|
return value if isinstance(value, dict) else {}
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 사이드카 정규화 리더 — 방언 지도(신전략서 §1.2): 목록 키 3종 · 투영 키 3종 · 정책 자리 4갈래.
|
||||||
|
# 방언을 다 읽지 않은 리더는 결손을 만들어 낸다(구 전략서 §1.5 의 실패 기록) —
|
||||||
|
# 세 방언 전부를 인정하고, 세지 않은 필드를 공통 필드로 승격하지 않는다.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
_LIST_KEYS = ("entries", "structure_types", "types")
|
||||||
|
_PROJECTION_KEYS = ("projections", "mappings", "structure_type_to_projection")
|
||||||
|
_FILE_POLICY_KEYS = ("admission_rules", "unregistered_effect_policy", "fallback_policy")
|
||||||
|
|
||||||
|
|
||||||
|
def read_sidecar_types(doc: Any) -> dict[str, Any]:
|
||||||
|
"""structure_types.json 방언을 정규화한다.
|
||||||
|
|
||||||
|
반환: {"list_key": str|None, "schema_version": str|None,
|
||||||
|
"items": [{"type_id", "admission_rule"?, "registered"?}],
|
||||||
|
"file_policy_key": str|None, "file_policy": dict|None,
|
||||||
|
"legacy_contract_style": bool}
|
||||||
|
"""
|
||||||
|
root = _dict(doc)
|
||||||
|
list_key = None
|
||||||
|
rows: list[Any] = []
|
||||||
|
for key in _LIST_KEYS:
|
||||||
|
if isinstance(root.get(key), list):
|
||||||
|
list_key = key
|
||||||
|
rows = root[key]
|
||||||
|
break
|
||||||
|
items = []
|
||||||
|
for row in rows:
|
||||||
|
row = _dict(row)
|
||||||
|
type_id = row.get("type_id") or row.get("structure_type") or row.get("id")
|
||||||
|
if not isinstance(type_id, str) or not type_id:
|
||||||
|
continue
|
||||||
|
item: dict[str, Any] = {"type_id": type_id}
|
||||||
|
if isinstance(row.get("admission_rule"), str) and row.get("admission_rule"):
|
||||||
|
item["admission_rule"] = row["admission_rule"]
|
||||||
|
if isinstance(row.get("registered"), bool):
|
||||||
|
item["registered"] = row["registered"]
|
||||||
|
items.append(item)
|
||||||
|
file_policy_key = None
|
||||||
|
file_policy = None
|
||||||
|
for key in _FILE_POLICY_KEYS:
|
||||||
|
if root.get(key) is not None:
|
||||||
|
file_policy_key = key
|
||||||
|
file_policy = root[key]
|
||||||
|
break
|
||||||
|
legacy_style = file_policy_key is None and any(
|
||||||
|
key in root for key in ("legacy_contract", "noncanonical_preservation", "ordering_preserved"))
|
||||||
|
return {"list_key": list_key,
|
||||||
|
"schema_version": root.get("schema_version"),
|
||||||
|
"items": items,
|
||||||
|
"file_policy_key": file_policy_key,
|
||||||
|
"file_policy": file_policy,
|
||||||
|
"legacy_contract_style": legacy_style}
|
||||||
|
|
||||||
|
|
||||||
|
def read_sidecar_projection(doc: Any) -> dict[str, Any]:
|
||||||
|
"""module_role_projection.json 방언을 정규화한다.
|
||||||
|
|
||||||
|
반환: {"projection_key": str|None, "mappings": {type_id: {"module", "role_projection"}},
|
||||||
|
"default_projection": dict|None, "missing_mapping_policy": dict|None}
|
||||||
|
"""
|
||||||
|
root = _dict(doc)
|
||||||
|
mappings: dict[str, dict[str, Any]] = {}
|
||||||
|
projection_key = None
|
||||||
|
for key in _PROJECTION_KEYS:
|
||||||
|
value = root.get(key)
|
||||||
|
if isinstance(value, list):
|
||||||
|
projection_key = key
|
||||||
|
for row in value:
|
||||||
|
row = _dict(row)
|
||||||
|
type_id = row.get("structure_type") or row.get("type_id")
|
||||||
|
if isinstance(type_id, str) and type_id:
|
||||||
|
mappings[type_id] = {"module": row.get("module"),
|
||||||
|
"role_projection": row.get("role_projection")}
|
||||||
|
break
|
||||||
|
if isinstance(value, dict):
|
||||||
|
projection_key = key
|
||||||
|
for type_id, row in value.items():
|
||||||
|
row = _dict(row)
|
||||||
|
if isinstance(type_id, str) and type_id:
|
||||||
|
mappings[type_id] = {"module": row.get("module"),
|
||||||
|
"role_projection": row.get("role_projection")}
|
||||||
|
break
|
||||||
|
default_projection = root.get("default_projection")
|
||||||
|
if not isinstance(default_projection, dict):
|
||||||
|
default_projection = None
|
||||||
|
missing_policy = root.get("missing_mapping_policy")
|
||||||
|
if not isinstance(missing_policy, dict):
|
||||||
|
missing_policy = None
|
||||||
|
return {"projection_key": projection_key, "mappings": mappings,
|
||||||
|
"default_projection": default_projection,
|
||||||
|
"missing_mapping_policy": missing_policy}
|
||||||
|
|
||||||
|
|
||||||
|
def admission_policy_for(sidecar_types: dict[str, Any], item: dict[str, Any]) -> str:
|
||||||
|
"""두 단 정의(신전략서 §1.2) — 파일 단위 정책(3키 중 있는 것) → 없으면 항목 admission_rule.
|
||||||
|
|
||||||
|
73/73 이 채워지는 값이다. 어느 단도 없으면 빈 문자열이 아니라 명시 문자열을 남긴다 —
|
||||||
|
없는 것을 있는 것처럼 채우지 않되, 부재 자체는 보이게 한다.
|
||||||
|
"""
|
||||||
|
key = sidecar_types.get("file_policy_key")
|
||||||
|
if key:
|
||||||
|
return "file:%s" % key
|
||||||
|
rule = item.get("admission_rule")
|
||||||
|
if isinstance(rule, str) and rule:
|
||||||
|
return "item:admission_rule"
|
||||||
|
return "absent"
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_source_domains(value: Any, alias_targets: dict[str, Any],
|
||||||
|
known_domain_ids: set[str]) -> list[str]:
|
||||||
|
"""BO provenance.source_domain 을 registry 도메인 ID 목록으로 해소한다(L0-7b ①).
|
||||||
|
|
||||||
|
registry ID 면 [값]. 아니면 별칭표의 목록(다중 타깃 실재 — 단일 primary 개념 없음).
|
||||||
|
별칭표에도 없으면 빈 목록 — 호출자가 PART3_BO_DOMAIN_UNRESOLVED 로 격리한다.
|
||||||
|
"""
|
||||||
|
if not isinstance(value, str) or not value:
|
||||||
|
return []
|
||||||
|
if value in known_domain_ids:
|
||||||
|
return [value]
|
||||||
|
targets = _strings(_dict(alias_targets).get(value))
|
||||||
|
return [d for d in targets if d in known_domain_ids]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 본체
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def compile_structure_seeds(activation_manifest: Any,
|
||||||
|
registry_index: Any,
|
||||||
|
domain_configs: dict[str, Any],
|
||||||
|
domain_sidecars: dict[str, Any],
|
||||||
|
policy: Any,
|
||||||
|
alias_declarations: Any,
|
||||||
|
sg13_records: list[Any],
|
||||||
|
sg05_records: list[Any],
|
||||||
|
sg11_records: list[Any],
|
||||||
|
domain_envelopes: dict[str, Any],
|
||||||
|
bo_records: list[Any],
|
||||||
|
slice_documents: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
"""L0-1 ~ L0-8. 반환 dict 는 theta 와 order_key 를 반드시 싣는다(PART3_COMPILER_STALE 의 대조면)."""
|
||||||
|
policy_root = _dict(policy)
|
||||||
|
if policy_root.get("schema_version") != POLICY_SCHEMA_VERSION:
|
||||||
|
raise ValueError("PART3_POLICY_INVALID:%s" % policy_root.get("schema_version"))
|
||||||
|
|
||||||
|
manifest_root = _dict(activation_manifest).get("domain_activation_manifest",
|
||||||
|
_dict(activation_manifest))
|
||||||
|
expected_runnable = _strings(_dict(manifest_root).get("expected_runnable_domain_ids"))
|
||||||
|
if not expected_runnable:
|
||||||
|
raise ValueError("PART3_EXPECTED_RUNNABLE_EMPTY")
|
||||||
|
|
||||||
|
index_root = _dict(_dict(registry_index).get("domain_registry_index", registry_index))
|
||||||
|
index_entries = [row for row in (index_root.get("entries") or []) if isinstance(row, dict)]
|
||||||
|
index_position = {row.get("domain_id"): position
|
||||||
|
for position, row in enumerate(index_entries)
|
||||||
|
if isinstance(row.get("domain_id"), str)}
|
||||||
|
known_domain_ids = set(index_position)
|
||||||
|
|
||||||
|
review_items: list[dict[str, Any]] = []
|
||||||
|
audit: dict[str, Any] = {"sidecar_dialects": {}, "input_gaps": [],
|
||||||
|
"unlinked_sg11": [], "dangling_calculation_refs": {}}
|
||||||
|
|
||||||
|
def review(code: str, severity: str, domain_id: str | None, detail: dict[str, Any]) -> None:
|
||||||
|
item = {"review_code": code, "severity": severity}
|
||||||
|
if domain_id is not None:
|
||||||
|
item["domain_id"] = domain_id
|
||||||
|
item.update(detail)
|
||||||
|
review_items.append(item)
|
||||||
|
|
||||||
|
# ---------------- L0-1 · Θ 조립 ----------------
|
||||||
|
declared_by_domain: dict[str, list[dict[str, Any]]] = {}
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
if domain_id not in known_domain_ids:
|
||||||
|
raise ValueError("PART3_DOMAIN_NOT_IN_REGISTRY:%s" % domain_id)
|
||||||
|
config_root = _dict(domain_configs.get(domain_id))
|
||||||
|
rows = [row for row in (config_root.get("structure_types") or [])
|
||||||
|
if isinstance(row, dict) and isinstance(row.get("type_id"), str)]
|
||||||
|
declared_by_domain[domain_id] = rows
|
||||||
|
theta = sorted({row["type_id"] for rows in declared_by_domain.values() for row in rows})
|
||||||
|
declaring_domains: dict[str, list[str]] = {}
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
for row in declared_by_domain[domain_id]:
|
||||||
|
declaring_domains.setdefault(row["type_id"], []).append(domain_id)
|
||||||
|
|
||||||
|
# ---------------- L0-2c · 사이드카 교차 검증 (레코드 생성보다 앞 — admission_policy 재료) ----------------
|
||||||
|
sidecar_norm: dict[str, dict[str, Any]] = {}
|
||||||
|
projection_norm: dict[str, dict[str, Any]] = {}
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
pair = _dict(domain_sidecars.get(domain_id))
|
||||||
|
declared_ids = sorted({row["type_id"] for row in declared_by_domain[domain_id]})
|
||||||
|
if not pair:
|
||||||
|
# 부재 규칙(신전략서 §5.5) — 선언 0 도메인의 부재만 정상이다.
|
||||||
|
if declared_ids:
|
||||||
|
raise ValueError("PART3_SIDECAR_ABSENT_FOR_DECLARING_DOMAIN:%s" % domain_id)
|
||||||
|
audit["sidecar_dialects"][domain_id] = {"absent": True, "declared": 0}
|
||||||
|
continue
|
||||||
|
types_doc = read_sidecar_types(pair.get("structure_types"))
|
||||||
|
projection_doc = read_sidecar_projection(pair.get("module_role_projection"))
|
||||||
|
sidecar_norm[domain_id] = types_doc
|
||||||
|
projection_norm[domain_id] = projection_doc
|
||||||
|
audit["sidecar_dialects"][domain_id] = {
|
||||||
|
"list_key": types_doc.get("list_key"),
|
||||||
|
"schema_version": types_doc.get("schema_version"),
|
||||||
|
"projection_key": projection_doc.get("projection_key"),
|
||||||
|
"file_policy_key": types_doc.get("file_policy_key"),
|
||||||
|
"legacy_contract_style": types_doc.get("legacy_contract_style"),
|
||||||
|
"item_count": len(types_doc.get("items") or []),
|
||||||
|
}
|
||||||
|
sidecar_ids = sorted({item["type_id"] for item in types_doc.get("items") or []})
|
||||||
|
if sidecar_ids != declared_ids:
|
||||||
|
review(REVIEW_SIDECAR_MISMATCH, "HARD_WARNING", domain_id, {
|
||||||
|
"detail": "type_set_mismatch",
|
||||||
|
"config_only": sorted(set(declared_ids) - set(sidecar_ids)),
|
||||||
|
"sidecar_only": sorted(set(sidecar_ids) - set(declared_ids)),
|
||||||
|
})
|
||||||
|
for type_id, mapping in sorted((projection_doc.get("mappings") or {}).items()):
|
||||||
|
config_row = next((row for row in declared_by_domain[domain_id]
|
||||||
|
if row["type_id"] == type_id), None)
|
||||||
|
if config_row is None:
|
||||||
|
continue
|
||||||
|
if (mapping.get("module") != config_row.get("module")
|
||||||
|
or mapping.get("role_projection") != config_row.get("role_projection")):
|
||||||
|
review(REVIEW_SIDECAR_MISMATCH, "HARD_WARNING", domain_id, {
|
||||||
|
"detail": "projection_mismatch", "type_id": type_id,
|
||||||
|
"config": {"module": config_row.get("module"),
|
||||||
|
"role_projection": config_row.get("role_projection")},
|
||||||
|
"sidecar": mapping,
|
||||||
|
})
|
||||||
|
|
||||||
|
# ---------------- L0-2 · 구조 레코드 — (도메인, type_id) 쌍마다 하나 ----------------
|
||||||
|
e13_extension_key = None
|
||||||
|
contract = _dict(policy_root.get("extension_key_rule"))
|
||||||
|
e13_extension_key = contract.get("extension_key") if isinstance(
|
||||||
|
contract.get("extension_key"), str) else None
|
||||||
|
e13_domain = contract.get("domain_id") if isinstance(contract.get("domain_id"), str) else None
|
||||||
|
|
||||||
|
records: dict[tuple[str, str], dict[str, Any]] = {}
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
types_doc = sidecar_norm.get(domain_id, {"items": []})
|
||||||
|
item_by_type = {item["type_id"]: item for item in types_doc.get("items") or []}
|
||||||
|
for row in declared_by_domain[domain_id]:
|
||||||
|
type_id = row["type_id"]
|
||||||
|
item = item_by_type.get(type_id, {})
|
||||||
|
record: dict[str, Any] = {
|
||||||
|
"structure_id": "LES-%s-%s" % (domain_id, type_id),
|
||||||
|
"domain_id": domain_id,
|
||||||
|
"type_id": type_id,
|
||||||
|
"priority_rank": row.get("priority_rank"),
|
||||||
|
"order_key": [row.get("priority_rank"), index_position[domain_id], type_id],
|
||||||
|
"module": row.get("module"),
|
||||||
|
"role_projection": row.get("role_projection"),
|
||||||
|
"admission_policy": admission_policy_for(types_doc, item),
|
||||||
|
"routes": [],
|
||||||
|
"route_count": 0,
|
||||||
|
"evidence_refs": [],
|
||||||
|
"meeting_clause_refs": [],
|
||||||
|
"calculation_request_refs": [],
|
||||||
|
"source_bo_ids": [],
|
||||||
|
"review_codes": [],
|
||||||
|
"review_required": False,
|
||||||
|
}
|
||||||
|
if "admission_rule" in item:
|
||||||
|
record["admission_rule"] = item["admission_rule"]
|
||||||
|
if "registered" in item:
|
||||||
|
record["registered_declared"] = item["registered"]
|
||||||
|
if domain_id == e13_domain and e13_extension_key:
|
||||||
|
record["extension_key"] = e13_extension_key
|
||||||
|
if domain_id == "E-00":
|
||||||
|
# E-00 특칙 — 잔여 수집기 유래 구조는 검토 필수(신전략서 §4.2).
|
||||||
|
record["review_required"] = True
|
||||||
|
records[(domain_id, type_id)] = record
|
||||||
|
|
||||||
|
# ---------------- L0-2b · 슬라이스 투영 정합 ----------------
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
slice_doc = slice_documents.get(domain_id)
|
||||||
|
if slice_doc is None:
|
||||||
|
audit["input_gaps"].append({"kind": "slice_absent", "domain_id": domain_id})
|
||||||
|
continue
|
||||||
|
slice_root = _dict(_dict(slice_doc).get("stage_b_domain_slice", slice_doc))
|
||||||
|
declarations = _dict(slice_root.get("domain_declarations"))
|
||||||
|
slice_ids = sorted(_strings(declarations.get("structure_type_ids")))
|
||||||
|
config_ids = sorted({row["type_id"] for row in declared_by_domain[domain_id]})
|
||||||
|
if slice_ids != config_ids:
|
||||||
|
review(REVIEW_SLICE_DIVERGENCE, "HARD_WARNING", domain_id, {
|
||||||
|
"slice_only": sorted(set(slice_ids) - set(config_ids)),
|
||||||
|
"config_only": sorted(set(config_ids) - set(slice_ids)),
|
||||||
|
})
|
||||||
|
|
||||||
|
# ---------------- L0-3 · SG-13 조인 + L0-4 · 도메인 소속 ----------------
|
||||||
|
routes = [_dict(row) for row in sg13_records
|
||||||
|
if _dict(row).get("record_type") == SG13_ROUTE_RECORD_TYPE]
|
||||||
|
attached_route_count = 0
|
||||||
|
unattached_routes: list[dict[str, Any]] = []
|
||||||
|
for route in routes:
|
||||||
|
details = _dict(route.get("details"))
|
||||||
|
type_id = details.get("type_id")
|
||||||
|
route_domains = _strings(route.get("domain_ids"))
|
||||||
|
route_domain = route_domains[0] if route_domains else None
|
||||||
|
registered = details.get("registered")
|
||||||
|
route_ref = {
|
||||||
|
"signal_id": route.get("signal_id"),
|
||||||
|
"source_seed_id": details.get("source_seed_id"),
|
||||||
|
"registered": registered if isinstance(registered, bool) else None,
|
||||||
|
"status": route.get("status"),
|
||||||
|
}
|
||||||
|
if not isinstance(type_id, str) or type_id not in theta:
|
||||||
|
# Θ 밖 — 버리지 않는다. default_projection 이 있으면 검토 항목에 실어 함께 나른다.
|
||||||
|
projection = None
|
||||||
|
if route_domain and route_domain in projection_norm:
|
||||||
|
projection = projection_norm[route_domain].get("default_projection")
|
||||||
|
review(REVIEW_UNREGISTERED, "HARD_WARNING", route_domain, {
|
||||||
|
"type_id": type_id, "signal_id": route.get("signal_id"),
|
||||||
|
"default_projection": projection,
|
||||||
|
})
|
||||||
|
unattached_routes.append({"signal_id": route.get("signal_id"),
|
||||||
|
"type_id": type_id, "reason": "type_outside_theta"})
|
||||||
|
continue
|
||||||
|
target_keys = []
|
||||||
|
if route_domain and (route_domain, type_id) in records:
|
||||||
|
target_keys = [(route_domain, type_id)]
|
||||||
|
else:
|
||||||
|
# Θ 안이지만 자기 도메인이 선언하지 않은 유형 — 선언 도메인 전부에 붙이고 검토(L0-4).
|
||||||
|
target_keys = [(d, type_id) for d in declaring_domains.get(type_id, [])
|
||||||
|
if (d, type_id) in records]
|
||||||
|
if target_keys:
|
||||||
|
review(REVIEW_CROSS_DOMAIN, "HARD_WARNING", route_domain, {
|
||||||
|
"type_id": type_id, "signal_id": route.get("signal_id"),
|
||||||
|
"attached_to": ["%s:%s" % key for key in target_keys],
|
||||||
|
})
|
||||||
|
if not target_keys:
|
||||||
|
unattached_routes.append({"signal_id": route.get("signal_id"),
|
||||||
|
"type_id": type_id, "reason": "no_declaring_domain"})
|
||||||
|
review(REVIEW_UNREGISTERED, "HARD_WARNING", route_domain, {
|
||||||
|
"type_id": type_id, "signal_id": route.get("signal_id"),
|
||||||
|
"default_projection": None,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
attached_route_count += 1
|
||||||
|
for key in target_keys:
|
||||||
|
record = records[key]
|
||||||
|
record["routes"].append(dict(route_ref))
|
||||||
|
for field in ("evidence_refs", "meeting_clause_refs"):
|
||||||
|
merged = set(record[field]) | set(_strings(route.get(field)))
|
||||||
|
record[field] = sorted(merged)
|
||||||
|
if registered is False:
|
||||||
|
# 붙은 채로 검토 꼬리표를 단다(L-m2 — 분할은 부착 여부로만 가른다).
|
||||||
|
if REVIEW_UNREGISTERED not in record["review_codes"]:
|
||||||
|
record["review_codes"].append(REVIEW_UNREGISTERED)
|
||||||
|
record["review_required"] = True
|
||||||
|
review(REVIEW_UNREGISTERED, "SOFT_WARNING", key[0], {
|
||||||
|
"type_id": type_id, "signal_id": route.get("signal_id"),
|
||||||
|
"detail": "registered_false_route_attached",
|
||||||
|
})
|
||||||
|
declared_flag = record.get("registered_declared")
|
||||||
|
if isinstance(declared_flag, bool) and isinstance(registered, bool) \
|
||||||
|
and declared_flag != registered:
|
||||||
|
# types 방언의 registered ↔ SG-13 registered 대조(L0-2c 둘째 갈래).
|
||||||
|
if REVIEW_SIDECAR_MISMATCH not in record["review_codes"]:
|
||||||
|
record["review_codes"].append(REVIEW_SIDECAR_MISMATCH)
|
||||||
|
record["review_required"] = True
|
||||||
|
review(REVIEW_SIDECAR_MISMATCH, "HARD_WARNING", key[0], {
|
||||||
|
"detail": "registered_flag_mismatch", "type_id": type_id,
|
||||||
|
"sidecar": declared_flag, "route": registered,
|
||||||
|
})
|
||||||
|
|
||||||
|
# ---------------- L0-5 · module 부여는 L0-2 에서 config 원문으로 끝났다 ----------------
|
||||||
|
|
||||||
|
# ---------------- L0-6 · 계산 조인 — SG-11 signal_id ↔ 도메인 봉투 calculation_refs ----------------
|
||||||
|
# 봉투 부재는 침묵 스킵이 아니라 영수증 계수다(I-5 — expected_runnable 중 실재하는 것만 읽되
|
||||||
|
# 부재를 기록한다). 실주행에서 감시 도메인 봉투가 없는 것은 상시 경로다.
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
if domain_id not in domain_envelopes:
|
||||||
|
audit["input_gaps"].append({"kind": "domain_envelope_absent",
|
||||||
|
"domain_id": domain_id})
|
||||||
|
sg11_rows = [_dict(row) for row in sg11_records]
|
||||||
|
sg11_ids = {row.get("signal_id") for row in sg11_rows if isinstance(row.get("signal_id"), str)}
|
||||||
|
linked_sg11: set[str] = set()
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
envelope = _dict(_dict(domain_envelopes.get(domain_id)).get("domain_signal_envelope",
|
||||||
|
domain_envelopes.get(domain_id)))
|
||||||
|
refs = _strings(_dict(envelope).get("calculation_refs"))
|
||||||
|
if not refs:
|
||||||
|
continue
|
||||||
|
matched = sorted(set(refs) & sg11_ids)
|
||||||
|
dangling = sorted(set(refs) - sg11_ids)
|
||||||
|
if dangling:
|
||||||
|
audit["dangling_calculation_refs"][domain_id] = dangling
|
||||||
|
review(REVIEW_CALC_DANGLING, "HARD_WARNING", domain_id, {"dangling": dangling})
|
||||||
|
if matched:
|
||||||
|
linked_sg11.update(matched)
|
||||||
|
for (record_domain, _type_id), record in records.items():
|
||||||
|
if record_domain == domain_id:
|
||||||
|
merged = set(record["calculation_request_refs"]) | set(matched)
|
||||||
|
record["calculation_request_refs"] = sorted(merged)
|
||||||
|
# 봉투 참조에 연결되지 않은 SG-11(합성 id 포함)은 구조에 싣지 않되 형식화 기록(신전략서 L0-6).
|
||||||
|
audit["unlinked_sg11"] = sorted(sg11_ids - linked_sg11)
|
||||||
|
|
||||||
|
# ---------------- L0-7 · SG-05 근거 refs (도메인 단위 합집합) ----------------
|
||||||
|
for row in sg05_records:
|
||||||
|
row = _dict(row)
|
||||||
|
for domain_id in _strings(row.get("domain_ids")):
|
||||||
|
for (record_domain, _type_id), record in records.items():
|
||||||
|
if record_domain == domain_id:
|
||||||
|
for field in ("evidence_refs", "meeting_clause_refs"):
|
||||||
|
merged = set(record[field]) | set(_strings(row.get(field)))
|
||||||
|
record[field] = sorted(merged)
|
||||||
|
|
||||||
|
# ---------------- L0-7b · BO 부착 조인 ----------------
|
||||||
|
alias_targets = _dict(_dict(alias_declarations).get("legacy_alias_targets"))
|
||||||
|
bo_rows = [_dict(row) for row in bo_records]
|
||||||
|
bo_attached: list[str] = []
|
||||||
|
bo_unattached: list[dict[str, Any]] = []
|
||||||
|
for bo in bo_rows:
|
||||||
|
bo_id = bo.get("BO_ID") or bo.get("id")
|
||||||
|
provenance = _dict(bo.get("provenance"))
|
||||||
|
source_domain = provenance.get("source_domain")
|
||||||
|
resolved = resolve_source_domains(source_domain, alias_targets, known_domain_ids)
|
||||||
|
if not resolved:
|
||||||
|
review(REVIEW_BO_UNRESOLVED, "HARD_WARNING", None, {
|
||||||
|
"bo_id": bo_id, "source_domain": source_domain,
|
||||||
|
})
|
||||||
|
bo_unattached.append({"bo_id": bo_id, "reason": "source_domain_unresolved"})
|
||||||
|
continue
|
||||||
|
keywords = _strings(bo.get("Legal_Keywords"))
|
||||||
|
attached_here = False
|
||||||
|
for domain_id in resolved:
|
||||||
|
for type_id in keywords:
|
||||||
|
record = records.get((domain_id, type_id))
|
||||||
|
if record is not None:
|
||||||
|
attached_here = True
|
||||||
|
if isinstance(bo_id, str) and bo_id not in record["source_bo_ids"]:
|
||||||
|
record["source_bo_ids"].append(bo_id)
|
||||||
|
record["source_bo_ids"].sort()
|
||||||
|
if attached_here:
|
||||||
|
bo_attached.append(bo_id if isinstance(bo_id, str) else "")
|
||||||
|
else:
|
||||||
|
# 부착 실패는 침묵 금지(L0-7b ③).
|
||||||
|
review(REVIEW_BO_UNATTACHED, "HARD_WARNING", None, {
|
||||||
|
"bo_id": bo_id, "source_domain": source_domain,
|
||||||
|
"legal_keywords": keywords, "resolved_domains": resolved,
|
||||||
|
})
|
||||||
|
bo_unattached.append({"bo_id": bo_id, "reason": "no_matching_structure"})
|
||||||
|
|
||||||
|
# ---------------- 정렬 · 검토 재료 반영 · 산출 ----------------
|
||||||
|
ordered = sorted(records.values(),
|
||||||
|
key=lambda record: (record["order_key"][0] if isinstance(
|
||||||
|
record["order_key"][0], int) else 999,
|
||||||
|
record["order_key"][1], record["order_key"][2]))
|
||||||
|
for record in ordered:
|
||||||
|
record["route_count"] = len(record["routes"])
|
||||||
|
record["routes"].sort(key=lambda ref: (str(ref.get("signal_id")),
|
||||||
|
str(ref.get("source_seed_id"))))
|
||||||
|
record["review_codes"].sort()
|
||||||
|
if record["review_codes"]:
|
||||||
|
record["review_required"] = True
|
||||||
|
|
||||||
|
status = "READY_WITH_REVIEW" if review_items else "READY"
|
||||||
|
bundle = {
|
||||||
|
"schema_version": SCHEMA_VERSION,
|
||||||
|
"status": status,
|
||||||
|
"registry_version": index_root.get("registry_version"),
|
||||||
|
"expected_runnable_domain_ids": list(expected_runnable),
|
||||||
|
"theta": theta,
|
||||||
|
"structure_records": ordered,
|
||||||
|
"unattached_routes": sorted(unattached_routes,
|
||||||
|
key=lambda row: (str(row.get("signal_id")),
|
||||||
|
str(row.get("type_id")))),
|
||||||
|
"unattached_bo": sorted(bo_unattached, key=lambda row: str(row.get("bo_id"))),
|
||||||
|
"counts": {
|
||||||
|
"structure_records": len(ordered),
|
||||||
|
"declared_structure_types_total": sum(
|
||||||
|
len(rows) for rows in declared_by_domain.values()),
|
||||||
|
"sg13_route_total": len(routes),
|
||||||
|
"routes_attached": attached_route_count,
|
||||||
|
"routes_unattached": len(unattached_routes),
|
||||||
|
"bo_total": len(bo_rows),
|
||||||
|
"bo_attached": len(bo_attached),
|
||||||
|
"bo_unattached": len(bo_unattached),
|
||||||
|
"sg11_total": len(sg11_rows),
|
||||||
|
"sg11_linked": len(linked_sg11),
|
||||||
|
"sg11_unlinked": len(audit["unlinked_sg11"]),
|
||||||
|
},
|
||||||
|
"created_by": "Task_C_LE_L0_structure_seed_reducer",
|
||||||
|
}
|
||||||
|
return {"structure_seed_bundle": bundle, "review_items": review_items, "audit": audit}
|
||||||
+98
@@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "stage1_structure_index_policy.v1",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"purpose": "Part 3 구조 seed 조립·색인 규칙을 코드 밖에 선언한다. 값의 정본은 registry(domain_config.structure_types)이고, registry 가 말하지 않는 값은 만들지 않고 검토로 올린다. 선언의 목적문은 적대 검증의 오라클이다.",
|
||||||
|
"authority": {
|
||||||
|
"theta_source": "SG-01 domain_activation_manifest.expected_runnable_domain_ids",
|
||||||
|
"structure_type_source": "Default_Agent/domains/<id>/domain_config.json#structure_types",
|
||||||
|
"sidecar_source": "Default_Agent/domains/<id>/structure_types.json · module_role_projection.json",
|
||||||
|
"alias_source": "Default_Agent/routing/extension_payload_key_declarations.v1.json#legacy_alias_targets",
|
||||||
|
"case_kind_names_forbidden": true
|
||||||
|
},
|
||||||
|
"theta_definition": {
|
||||||
|
"formula": "Θ(사건) = ⋃ { domain_config(d).structure_types[].type_id | d ∈ expected_runnable_domain_ids }",
|
||||||
|
"note": "active_domain_ids 가 아니다(D-3 넓은 정의). 좁히면 감시·상시 도메인의 효과가 전부 검토로 쓸려 들어가고 커버리지 등식은 그래도 통과한다 — 조용히 틀리는 경로다."
|
||||||
|
},
|
||||||
|
"record_identity": {
|
||||||
|
"rule": "구조 레코드는 Θ 의 (도메인, type_id) 쌍마다 정확히 하나다. 도메인 간에 유형을 합치지 않는다.",
|
||||||
|
"structure_id": "LES-<domain_id>-<type_id>",
|
||||||
|
"note": "SG-13 route·BO 부착은 레코드 위에 붙는 근거이지 레코드 생성 주체가 아니다. 근거 없는 유형은 '선언됐으나 근거 없음'으로 보이고, 선언 0 도메인(EC-00)은 자연히 0 이다."
|
||||||
|
},
|
||||||
|
"total_order": {
|
||||||
|
"order_key": [
|
||||||
|
"그 도메인이 선언한 priority_rank",
|
||||||
|
"domains/_registry_index.json entries[] 의 도메인 위치",
|
||||||
|
"type_id"
|
||||||
|
],
|
||||||
|
"note": "min(rank) 병합 금지 — 도메인 내부 rank 는 26/26 전부 엄격 오름차순이라 이 3키가 각 도메인의 선언 순서를 보존한다."
|
||||||
|
},
|
||||||
|
"admission_policy": {
|
||||||
|
"definition": "두 단 — 그 도메인 사이드카의 파일 단위 정책 키(admission_rules · unregistered_effect_policy · fallback_policy 중 있는 것, 값 'file:<키>') → 없으면 항목 단위 admission_rule(값 'item:admission_rule') → 어느 단도 없으면 'absent'.",
|
||||||
|
"note": "admission_rule 은 entries 방언 13건에만 있는 선택 필드다 — 게이트 기준으로 쓰지 않는다. 원본 보존을 위해 있는 것만 admission_rule 필드로 함께 싣는다."
|
||||||
|
},
|
||||||
|
"fallback_policy": {
|
||||||
|
"silent_general_legal_effect_fallback_forbidden": true,
|
||||||
|
"rule": "빈 입력은 structure_count 0 으로 기록한다. Θ 밖·registered:false 유형은 버리지도 일반화하지도 않고 UNREGISTERED_EFFECT_TYPE_REVIEW 를 붙인다. 사이드카 default_projection 이 있으면 그 투영을 검토 항목에 실어 나르되 검토 코드를 반드시 동반한다."
|
||||||
|
},
|
||||||
|
"attachment_partition": {
|
||||||
|
"rule": "route 의 분할은 부착 여부로만 가른다 — 붙은(어떤 레코드에 근거로 실림) / 미부착(어느 레코드에도 닿지 못함). 검토 여부는 분할과 무관하다. registered:false 라도 type_id 가 Θ 안·선언 도메인이 있으면 붙은 채로 검토 꼬리표를 단다(이중 계상 금지)."
|
||||||
|
},
|
||||||
|
"bo_attachment": {
|
||||||
|
"alias_resolution": "provenance.source_domain 이 registry 도메인 ID 면 [그 값], 아니면 legacy_alias_targets[값] 목록(다중 타깃 실재 — 단일 primary 개념 없음), 어디에도 없으면 PART3_BO_DOMAIN_UNRESOLVED 검토로 격리.",
|
||||||
|
"join": "해소된 모든 도메인 d 에 대해 t ∈ BO.Legal_Keywords 인 구조 레코드 (d, t) 에 source_bo_ids 로 부착을 시도한다.",
|
||||||
|
"silence_forbidden": "Legal_Keywords 가 비었거나 어느 (d, t) 에도 닿지 않는 BO 는 PART3_BO_UNATTACHED 검토로 싣는다.",
|
||||||
|
"purpose": "원본 review_code 와 마찬가지로 — BO 연결을 잃지 않기 위해서다. SG 층의 source_bo_ids 가 비어 있어도 이 조인이 BO↔구조 연결을 세운다."
|
||||||
|
},
|
||||||
|
"sg11_join": {
|
||||||
|
"key": "SG-11 레코드의 signal_id (worker request_id 승계값 — details.request_id 는 보장되지 않는 키라 쓰지 않는다)",
|
||||||
|
"counterpart": "domain_signal_envelope.calculation_refs",
|
||||||
|
"unlinked_rule": "봉투 참조에 연결되지 않은 SG-11 레코드는 구조에 싣지 않되 영수증에 미연결 계수로 형식화 기록한다.",
|
||||||
|
"dangling_rule": "봉투에 있는데 SG-11 에 없는 참조는 PART3_CALCULATION_REF_DANGLING 검토."
|
||||||
|
},
|
||||||
|
"extension_key_rule": {
|
||||||
|
"domain_id": "E-13",
|
||||||
|
"extension_key": "actio_case_signals.v2",
|
||||||
|
"note": "별도 매핑표를 만들지 않는다 — s5_execution_contract.v2.json 의 e13_extension_domains 선언을 그대로 싣는다."
|
||||||
|
},
|
||||||
|
"conservation_equations": {
|
||||||
|
"declaration": "구조 레코드 수 == Σ(expected_runnable 도메인의 structure_types 개수) — 어긋나면 PART3_STRUCTURE_COUNT_BROKEN",
|
||||||
|
"route": "붙은 route 수 + 미부착 route 수 == SG-13 legal_effect_route 총수 — 어긋나면 PART3_ROUTE_CONSERVATION_BROKEN",
|
||||||
|
"bo": "부착 BO 수 + 미부착 BO 수 == BO.json 레코드 총수 — 어긋나면 PART3_BO_CONSERVATION_BROKEN",
|
||||||
|
"note": "세 등식 다 L2 가 원본 파일(domain_config · legal_effect_routes · BO.json)에서 직접 센 바깥 수와 비교한다. bundle 에 실린 수와 비교하면 항등식으로 퇴행한다."
|
||||||
|
},
|
||||||
|
"structure_index": {
|
||||||
|
"indexes": [
|
||||||
|
"by_domain_id",
|
||||||
|
"by_module",
|
||||||
|
"by_bo_id"
|
||||||
|
],
|
||||||
|
"by_claim_form_forbidden": "claim-form 어휘가 registry 에 없고 (module, role_projection) 은 type_id 와 일대일이라 색인이 되지 못한다. 필요하면 registry 에 어휘를 새로 선언하는 일이며 Part 3 범위 밖이다."
|
||||||
|
},
|
||||||
|
"review_codes": [
|
||||||
|
"UNREGISTERED_EFFECT_TYPE_REVIEW",
|
||||||
|
"PART3_SLICE_PROJECTION_DIVERGENCE",
|
||||||
|
"PART3_REGISTRY_SIDECAR_MISMATCH",
|
||||||
|
"PART3_EFFECT_TYPE_CROSS_DOMAIN",
|
||||||
|
"PART3_CALCULATION_REF_DANGLING",
|
||||||
|
"PART3_BO_UNATTACHED",
|
||||||
|
"PART3_BO_DOMAIN_UNRESOLVED"
|
||||||
|
],
|
||||||
|
"hard_error_codes": [
|
||||||
|
"PART3_ASSET_DEPLOYMENT_INCOMPLETE",
|
||||||
|
"PART3_POLICY_INVALID",
|
||||||
|
"PART3_STRUCTURE_SCHEMA_STALE",
|
||||||
|
"PART3_COMPILER_STALE",
|
||||||
|
"PART3_INDEX_SCHEMA_STALE",
|
||||||
|
"PART3_SIGNAL_SEAL_FAILED",
|
||||||
|
"PART3_REGISTRY_INDEX_SEAL_FAILED",
|
||||||
|
"PART3_DOMAIN_NOT_IN_REGISTRY",
|
||||||
|
"PART3_STRUCTURE_COUNT_BROKEN",
|
||||||
|
"PART3_ROUTE_CONSERVATION_BROKEN",
|
||||||
|
"PART3_BO_CONSERVATION_BROKEN",
|
||||||
|
"PART3_FINAL_SCHEMA_VALIDATION_FAILED",
|
||||||
|
"PART3_EXPECTED_RUNNABLE_EMPTY",
|
||||||
|
"PART3_SIDECAR_ABSENT_FOR_DECLARING_DOMAIN"
|
||||||
|
],
|
||||||
|
"e00_rule": "E-00(잔여 수집기)에서 온 구조 레코드는 review_required true 를 강제한다.",
|
||||||
|
"sidecar_absence_rule": "structure_types 선언이 0 인 도메인(오늘 EC-00 하나)의 사이드카 부재는 정상 — 스킵하고 영수증에 기록한다. 선언이 있는 도메인의 부재·미등재는 경성 실패다."
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
# Stage 1 — Part 3 개정 작업 내역서
|
||||||
|
|
||||||
|
- 문서 판본: v1 (2026-08-19)
|
||||||
|
- 지시 원문: `stage_1_part_3_개정_신전략서.md` (§3 입력 계약 · §4 작업 명세 · §5 자산 · §6 실행 단계 · §7 검증 · §10 완료 판정)
|
||||||
|
- 판정 기준(완수): 실행 완주가 아니라 **의도한 결과물이 올바른 방식으로 생성 + 소작업 간 upstream–downstream consistency 유지**.
|
||||||
|
- P3-0a(D-4 승인): 사용자의 "신전략서를 실행하라"는 지시를 승인으로 처리하고 착수했다.
|
||||||
|
- 결과: **§4·§5 처방 전건 반영 + 회귀 22종 및 보충 실측 2종 전건 통과 + 적대적 sub-agent 검증 2라운드(1차 지적 5건 전부 반영 · 재검증 "지적 0건")** — Part 3 는 예행 전 사슬(3패스 11단계)에서 완수 기준을 충족했다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 요약
|
||||||
|
|
||||||
|
Part 3 작업명세서를 **registry 와 Part 1·2 실산출 계약만으로 새로 지었다**(구 Part 3 v2 무참조 — 제약 1). task 는 둘이다: `Task_C_LE_L0_structure_seed_reducer`(Θ 조립 → 구조 레코드 → SG-13·SG-11·BO 조인 → 검토) → `Task_C_LE_L2_final_structure_index_writer`(보존 등식 셋 → 역인덱스 셋 → 최종 기록). **L1 은 두지 않았다** — LLM 이 판정할 예외 부류가 없다(신전략서 §4.1). 조립·판정 로직은 신설 모듈 `structure_index_compiler` 에 있고 YAML 은 반입·게이트·봉인·기록만 한다.
|
||||||
|
|
||||||
|
가장 중요한 실측: **예행 전 사슬에서 `legal_effect_structures.json` 이 실제로 선다** — Θ 4종 · 구조 레코드 6건(레코드는 (도메인, type_id) 쌍마다 하나) · SG-13 route 6/6 부착 · **BO 2/2 부착**(`provenance.source_domain` + `Legal_Keywords` 결정론 조인 — SG 층의 `source_bo_ids` 가 비어 있어도 BO↔구조 연결이 섬) · 보존 등식 셋 전부 성립(바깥 수는 L2 가 원본에서 직접 계수) · 역인덱스 셋(`by_domain_id` · `by_module` · `by_bo_id`). 26 도메인 합성 실측은 **Θ 56종 · 구조 73건 · module 34종 · 결정론**이다.
|
||||||
|
|
||||||
|
## 1. 작업 분류 (method 1)
|
||||||
|
|
||||||
|
**병렬(독립) 수행분** — 서로 파일이 겹치지 않음: 정책 자산 작성 · 스키마 2종 작성 · 컴파일러 모듈 작성 · 사이드카 50 복사.
|
||||||
|
**순차 수행분** — 상하류 의존: 매니페스트 등재(자산 확정 후, 원자적 1회) → L0/L2 yaml 조각 → 통합본 병합 → 선언표·stage_chain·상위 전략서 → 예행 3패스 → 회귀 → 검증 루프 → 내역서.
|
||||||
|
|
||||||
|
## 2. 산출물 목록
|
||||||
|
|
||||||
|
| 파일 | 상태 |
|
||||||
|
| --- | --- |
|
||||||
|
| `ver_8_yaml_candidates/stage_1_part_3_v.8.yml` | **신규** (52,878 B · task 2 · `IN→L0→L2→OUT` · 선언 1벌) |
|
||||||
|
| `ver_8_yaml_candidates/P3-L0_structure_seed_reducer_v1.yml` · `P3-L2_final_structure_index_writer_v1.yml` | **신규** 조각 (통합본과 블록 동일) |
|
||||||
|
| `Default_Agent/stage1_runtime/structure_index_compiler.py` + `.txt` | **신규** (순수 모듈 — I/O·시각·난수 없음. 방언 3·3 정규화 리더 · Θ · 전순서 · 조인 · 검토) |
|
||||||
|
| `Default_Agent/stage1_runtime/structure_index_policy.v1.json` | **신규** (`stage1_structure_index_policy.v1` — Θ 정의 · 3키 전순서 · admission 2단 · 부착 분할 · BO 조인 · SG-11 키 · 등식 셋 · 코드 목록. 검증 오라클) |
|
||||||
|
| `Default_Agent/platform/schemas/structure_seed_bundle.schema.json` · `legal_effect_structures.schema.json` | **신규** (파일명 무판본 · 열거 하드코딩 없이 패턴 3종만) |
|
||||||
|
| `Default_Agent/domains/<id>/structure_types.json` · `module_role_projection.json` ×25 | **복사 배포** (조립본과 50/50 바이트 동일) |
|
||||||
|
| `Default_Agent/runtime_manifest.json` | 갱신 (**371 → 426** · 델타 정확히 55 · 기존 행 수정 0 · 왕복 보존) |
|
||||||
|
| `선행구축/.../handoffs/stage1_part_interface.v1.json` | 갱신 (신규 11줄 + 기존 갱신 — `handoff_count` 34→**45** · C-7 구 이름 3종 Part 3 소비자 제거 · C-8 `client_goal` 등재·`client_meeting` 소비자 · compact 직렬화 보존) |
|
||||||
|
| `선행구축/.../handoffs/stage1_stage_chain.v1.json` | 갱신 (Part 3 구간 — yaml·requires·produces·이관 완료 note) |
|
||||||
|
| `stage_1_update_strategy.md` | 갱신 두 곳 (§1 사슬 `S0→L0→L2` · §4.2 L1 부재 주석) |
|
||||||
|
| 예행 증거 | `/tmp/dry_p3/` (3패스 산출 전부) · 회귀 결과 scratchpad `part3_regression_results.json` |
|
||||||
|
|
||||||
|
배포 트리 최종 210 파일(155+55) · 매니페스트 등재-실물(트리 실재 149건) 전수 해시 일치 · `__pycache__` 0.
|
||||||
|
|
||||||
|
## 3. 검증 (method 3)
|
||||||
|
|
||||||
|
**정적** — yaml parse PASS · 선언 1벌 · AST 미정의 이름 0 · 개별↔통합 블록 동일 · 비주석 금지어(Claude_YAML·구 signal 이름·case_kind matrix·조립본 경로) 0 · code-executor parameters 5키 · 선언표 검사기 **finding 0**(3 yaml 전수).
|
||||||
|
|
||||||
|
**예행 3패스** (asset root = 배포 원본 `extension_research/Default_Agent` · 실행 루트 신선): pass1 PASS 6단계/45건 · seed 대역 3건 · pass2 PASS 3단계/29건 · **pass3 PASS 2단계/5건**. 산출 실측 — 최종 status READY · 봉인 승계(`S5TX-…`) · 레코드 6 · 게이트 3 전부 pass · handoff FINALIZED · 영수증(게이트 10 자산/실측·봉인 둘·조인 계수·SG-02/07 공백 형식화·사이드카 방언 지도·BO sha·전순서표).
|
||||||
|
|
||||||
|
**회귀 22종(L-a~L-s) + 보충 2종 = 24/24 PASS.** 필수 8종(L-b·b2·c·d2·k·m2·p·q) 포함 전건 기대대로 발화. 변조는 전부 사본 트리/실행 루트에서 수행(배포 원본 무변). 보충: SUPP-synth26(Θ56·73·34·머리 4줄·결정론) · SUPP-calc-probe(정상 조인·dangling 검토·미연결 형식화).
|
||||||
|
|
||||||
|
**적대적 sub-agent 2라운드**: 1차 지적 5건(MAJOR 2 — ① 선언표 `handoffs` 배열의 `client_meeting` 행 미갱신(중복 행 절반만 반영) ② 도메인 봉투 부재의 침묵 스킵(영수증 계수 부재) / MINOR 3 — L2 의 계약 밖 `signal_manifest` 읽기 · 합성/탐침 증적 미보존 · `Stage_1.Part_3` 약칭 잔존) — **전부 반영**(봉투 부재는 `audit.input_gaps` 기록 + L-k 단언, transaction_id 는 L0 가 bundle 에 부착해 L2 가 승계). 재검증: **"지적 0건"**(반영 5 + 파급 12 = 17건 실측 전건 통과).
|
||||||
|
|
||||||
|
## 4. 한계 (정직 선언)
|
||||||
|
|
||||||
|
- 예행은 LLM 대역 기반이다 — 실체 도메인 활성·실서버 실행은 범위 밖(행동 검증 **미측정** 명시, 신전략서 §7.1).
|
||||||
|
- 예행 Θ 는 4종에 그치므로 26 도메인 합성·계산 조인 탐침으로 따로 실측했다(§7.3 처방 그대로).
|
||||||
|
- P3-2 의 "정책·스키마 자기 검증" 별도 기록은 남기지 않았다 — 예행에서 두 스키마가 실주입 검증됐고(L0·L2 닫힌 스키마 통과) 회귀 L-b/L-b2 가 스키마 능력 검사의 발화를 실증한다.
|
||||||
|
|
||||||
|
## 5. 이월 (조립본 동기화 회차 — 신전략서 §6.1)
|
||||||
|
|
||||||
|
① Part 3 신설 자산 5종(+미러)·사이드카 등재의 조립본 반영 + 빌더 `PART3_ASSEMBLY_AUTHORED` 등록 + 사이드카 sweep 규칙(파일 반영과 등록은 한 벌) ② `release_manifest`·`source_copy_map` 재생성 ③ 신선 빌드의 Part 3 자산 재현 확인 ④ `dry_run_receipt` 정본화(Part 2 M-* + Part 3 L-* 합본) ⑤ `effect_projection.schema_ref` dangling 해소(registry 소유자). Part 2 의 D-1~D-6 과 한 벌로 처리한다. Part 3 실행 정합성 무영향은 배포 원본 단독 예행 전 사슬로 실증돼 있다.
|
||||||
+2
-2
@@ -173,7 +173,7 @@ signal 파일명은 다음과 같다. SG-01 `domain_activation_manifest.json`, S
|
|||||||
└─▶ Fact_Ledger_base.json + writer report ──▶ Stage 2
|
└─▶ Fact_Ledger_base.json + writer report ──▶ Stage 2
|
||||||
|
|
||||||
의존: T0→{T1,D0} · {T5,T6,T8}→D0 · D0→T9 · T9→A0 · fanout_plan→B*→R0→R1→F0→S0
|
의존: T0→{T1,D0} · {T5,T6,T8}→D0 · D0→T9 · T9→A0 · fanout_plan→B*→R0→R1→F0→S0
|
||||||
S0→L0→L1→L2→P4-F0→F1→F2
|
S0→L0→L2→P4-F0→F1→F2 (L1 은 두지 않는다 — stage_1_part_3_개정_신전략서.md §4.1)
|
||||||
```
|
```
|
||||||
|
|
||||||
LLM 호출 구조: Part 1은 스크리너 1 + Task_A 1 + 매퍼 2N + 조건부 판정(대개 즉시 종료). Part 2는 worker M개 + 조건부 판정. Part 3·4는 조건부 판정만. 순증 비용은 스크리너 1회와 활성 도메인 증가분이며, 상시 도메인과 감시 도메인은 낮은 등급 모델로 돈다.
|
LLM 호출 구조: Part 1은 스크리너 1 + Task_A 1 + 매퍼 2N + 조건부 판정(대개 즉시 종료). Part 2는 worker M개 + 조건부 판정. Part 3·4는 조건부 판정만. 순증 비용은 스크리너 1회와 활성 도메인 증가분이며, 상시 도메인과 감시 도메인은 낮은 등급 모델로 돈다.
|
||||||
@@ -287,7 +287,7 @@ signal마다 받아들이는 조건이 registry에 선언되어 있으므로 그
|
|||||||
|
|
||||||
### 4.2 P3-L1 예외 판정
|
### 4.2 P3-L1 예외 판정
|
||||||
|
|
||||||
무변경이다. 허용 결정 목록과 후보값 부분집합 규율을 유지하고, 예외 묶음에 동적 열거 목록이 동봉될 뿐이다.
|
(개정 실행에서 L1 은 두지 않는 것으로 확정됐다 — LLM 이 판정할 예외 부류가 없다. 되돌림 경로와 근거는 `stage_1_part_3_개정_신전략서.md` §4.1. 아래 원문은 기록으로 남긴다.) 무변경이다. 허용 결정 목록과 후보값 부분집합 규율을 유지하고, 예외 묶음에 동적 열거 목록이 동봉될 뿐이다.
|
||||||
|
|
||||||
### 4.3 P3-L2 최종 색인 기록 [개정]
|
### 4.3 P3-L2 최종 색인 기록 [개정]
|
||||||
|
|
||||||
|
|||||||
+567
@@ -0,0 +1,567 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# P3-L0 / Task_C_LE_L0_structure_seed_reducer — v1 (신설)
|
||||||
|
#
|
||||||
|
# 정본 근거 : stage_1_part_3_개정_신전략서.md §4.2 (L0-1 ~ L0-8) · §3 (I-1 ~ I-14) · §5
|
||||||
|
# 계약 : Θ = SG-01.expected_runnable_domain_ids 의 domain_config.structure_types 합집합
|
||||||
|
# 구조 레코드 = (도메인, type_id) 쌍마다 하나 · order_key 3키 전순서
|
||||||
|
# 조립·판정 로직은 stage1_runtime/structure_index_compiler 모듈에 있다.
|
||||||
|
# 이 task 는 자산 반입 → 게이트 → 봉인 → 적재 → 모듈 호출 → 기록만 한다.
|
||||||
|
# 제약 1 : v.7/Claude_YAML 의 어떤 파일도 읽지 않는다. 구 Part 3 에 의존하지 않는다.
|
||||||
|
# 137종 : 사건 이름·도메인 목록·유형 열거를 코드에 두지 않는다.
|
||||||
|
# =============================================================================
|
||||||
|
- task_name: Task_C_LE_L0_structure_seed_reducer
|
||||||
|
mcp: code-executor
|
||||||
|
tool_name: run_code
|
||||||
|
parameters:
|
||||||
|
language: python
|
||||||
|
requirements: "httpx"
|
||||||
|
network: "agent-network"
|
||||||
|
timeout: 240
|
||||||
|
code: |-
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Task_C_LE_L0_structure_seed_reducer (v1)
|
||||||
|
# 0) 배포 게이트 -> 1) 모듈 반입 -> 2) 정책 반입 -> 3) 의미 검사 둘 ->
|
||||||
|
# 4) 진입 봉인 둘 -> 5) 입력 적재 -> 6) compile_structure_seeds ->
|
||||||
|
# 7) bundle · review handoff · 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 2 A0 검증 완료본 복사.
|
||||||
|
# clientInfo 에 {{__user_hash__}} / {{__workspace_hash__}} 를 반드시 넣는다.
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
TASK_NAME = "Task_C_LE_L0_structure_seed_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 공유(신전략서 §4.2 도입부. 새 뿌리를 만들지 않는다).
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
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",
|
||||||
|
}
|
||||||
|
MODULES = ["runtime_common", "schema_subset_validator", "registry_loader",
|
||||||
|
"structure_index_compiler"]
|
||||||
|
RUNTIME_MANIFEST = "Default_Agent/runtime_manifest.json"
|
||||||
|
RUNTIME_MANIFEST_SCHEMA = "stage1_runtime_manifest.v1"
|
||||||
|
|
||||||
|
# 정적 자산 (I-9 ~ I-14)
|
||||||
|
REGISTRY_INDEX = "Default_Agent/domains/_registry_index.json"
|
||||||
|
POLICY = "Default_Agent/stage1_runtime/structure_index_policy.v1.json"
|
||||||
|
POLICY_SCHEMA_VERSION = "stage1_structure_index_policy.v1"
|
||||||
|
ALIAS_DECLARATIONS = "Default_Agent/routing/extension_payload_key_declarations.v1.json"
|
||||||
|
SEED_SCHEMA = "Default_Agent/platform/schemas/structure_seed_bundle.schema.json"
|
||||||
|
INDEX_SCHEMA = "Default_Agent/platform/schemas/legal_effect_structures.schema.json"
|
||||||
|
|
||||||
|
# 사건 산출물 (I-1 ~ I-8) — Part 1·2 가 낸 것만 읽는다.
|
||||||
|
SIGNAL_MANIFEST = "signals/signal_manifest.json"
|
||||||
|
SG13_PATH = "signals/legal_effect_routes.json"
|
||||||
|
SG05_PATH = "signals/legal_relation_lifecycle_signals.json"
|
||||||
|
SG11_PATH = "signals/calculation_requirements.json"
|
||||||
|
SG02_PATH = "signals/procedural_posture_relief_signals.json"
|
||||||
|
SG07_PATH = "signals/asset_right_state_signals.json"
|
||||||
|
DOMAIN_SIGNAL_DIR = "signals/domain_signals"
|
||||||
|
ACTIVATION_MANIFEST = "routing/domain_activation_manifest.json"
|
||||||
|
BO_PATH = "BO.json"
|
||||||
|
SLICE_DIR = "runtime/domain_slices"
|
||||||
|
|
||||||
|
# 산출
|
||||||
|
BUNDLE_PATH = "stage1_tmp/task_le/structure_seed_bundle.json"
|
||||||
|
HANDOFF_PATH = "quality_gates/stage1_part3_review_handoff.json"
|
||||||
|
RECEIPT_PATH = "validation_assets/routing/part3_receipt.json"
|
||||||
|
|
||||||
|
# F-2 상당 — 배포 게이트가 한 벌로 검사하는 필수 자산 10 리터럴(신전략서 §5.5).
|
||||||
|
# 사이드카 50 은 여기 넣지 않는다 — expected_runnable 도메인 것만 읽는 시점에
|
||||||
|
# _verify_asset 으로 지킨다. domains/_registry_index.json 은 매니페스트 등재
|
||||||
|
# 대상이 아니므로 SG-01 봉인이 지킨다(I-11 · §1.10 규율).
|
||||||
|
PART3_REQUIRED_ASSETS = (
|
||||||
|
SEED_SCHEMA,
|
||||||
|
INDEX_SCHEMA,
|
||||||
|
POLICY,
|
||||||
|
ALIAS_DECLARATIONS,
|
||||||
|
"Default_Agent/signals/signal_registry.v2.json",
|
||||||
|
"Default_Agent/contracts/signals/s5_execution_contract.v2.json",
|
||||||
|
"Default_Agent/stage1_runtime/structure_index_compiler.txt",
|
||||||
|
"Default_Agent/stage1_runtime/runtime_common.txt",
|
||||||
|
"Default_Agent/stage1_runtime/schema_subset_validator.txt",
|
||||||
|
"Default_Agent/stage1_runtime/registry_loader.txt",
|
||||||
|
)
|
||||||
|
PART3_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 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": "PART3_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(PART3_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": "PART3_ASSET_DEPLOYMENT_INCOMPLETE",
|
||||||
|
"message": "배포 원본에 Part 3 자산이 한 벌로 반영되지 않았다.",
|
||||||
|
"unregistered": unregistered, "hash_mismatch": mismatch,
|
||||||
|
"unreadable": unreadable, "remedy": PART3_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": "PART3_ASSET_DEPLOYMENT_INCOMPLETE", "path": logical,
|
||||||
|
"detail": detail, "remedy": PART3_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 R-b/R-g2 교훈).
|
||||||
|
# 각 검사는 자기가 쓰는 산출물의 스키마만 본다. by_module 은 L2 몫이다.
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
def assert_seed_schema_capability(seed_schema):
|
||||||
|
bundle_props = (((seed_schema.get("properties") or {})
|
||||||
|
.get("structure_seed_bundle") or {}).get("properties") or {})
|
||||||
|
record_props = ((((bundle_props.get("structure_records") or {})
|
||||||
|
.get("items") or {}).get("properties") or {}))
|
||||||
|
missing = []
|
||||||
|
if "theta" not in bundle_props:
|
||||||
|
missing.append("theta")
|
||||||
|
if "admission_policy" not in record_props:
|
||||||
|
missing.append("structure_records[].admission_policy")
|
||||||
|
if missing:
|
||||||
|
raise RuntimeError(json.dumps({
|
||||||
|
"reason_code": "PART3_STRUCTURE_SCHEMA_STALE",
|
||||||
|
"message": "seed 스키마가 Part 3 계약 필드를 모른다. 배포 원본의 스키마가 개정 전 판본이다.",
|
||||||
|
"missing": missing, "remedy": PART3_DEPLOYMENT_REMEDY,
|
||||||
|
}, ensure_ascii=False))
|
||||||
|
|
||||||
|
|
||||||
|
def assert_compiler_capability(result):
|
||||||
|
bundle = (result or {}).get("structure_seed_bundle") or {}
|
||||||
|
records = bundle.get("structure_records")
|
||||||
|
bad = ("theta" not in bundle
|
||||||
|
or not isinstance(records, list)
|
||||||
|
or any("order_key" not in record for record in records
|
||||||
|
if isinstance(record, dict)))
|
||||||
|
if bad:
|
||||||
|
raise RuntimeError(json.dumps({
|
||||||
|
"reason_code": "PART3_COMPILER_STALE",
|
||||||
|
"message": "컴파일러 산출에 theta·order_key 가 실려 나오지 않는다. "
|
||||||
|
"배포 원본의 structure_index_compiler 가 개정 전 판본이다.",
|
||||||
|
"remedy": PART3_DEPLOYMENT_REMEDY,
|
||||||
|
}, ensure_ascii=False))
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 4) 진입 봉인 둘 (신전략서 §4.2-5)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
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": "PART3_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/ 접두사가 없다(I-1 주의). 붙여서 대조한다.
|
||||||
|
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": "PART3_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, "root": root}
|
||||||
|
|
||||||
|
|
||||||
|
def verify_registry_index_seal(activation_root, index_raw):
|
||||||
|
declared = activation_root.get("registry_index_sha256")
|
||||||
|
actual = sha_text(index_raw)
|
||||||
|
if declared != actual:
|
||||||
|
# 전순서 둘째 키가 이 색인의 배열 위치에 걸려 있다. 매니페스트 등재 대상이
|
||||||
|
# 아니므로 이 봉인만이 색인 교체를 잡는다(L-d2).
|
||||||
|
raise RuntimeError(json.dumps({
|
||||||
|
"reason_code": "PART3_REGISTRY_INDEX_SEAL_FAILED",
|
||||||
|
"declared": declared, "actual": actual}, ensure_ascii=False))
|
||||||
|
return actual
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
gate_manifest = assert_deployment()
|
||||||
|
staged = materialize_modules()
|
||||||
|
import structure_index_compiler
|
||||||
|
|
||||||
|
# 2) 정책 반입 — 판본 불일치는 PART3_POLICY_INVALID.
|
||||||
|
policy = json.loads(_verify_asset(POLICY))
|
||||||
|
if policy.get("schema_version") != POLICY_SCHEMA_VERSION:
|
||||||
|
raise RuntimeError(json.dumps({
|
||||||
|
"reason_code": "PART3_POLICY_INVALID",
|
||||||
|
"expected": POLICY_SCHEMA_VERSION,
|
||||||
|
"actual": policy.get("schema_version")}, ensure_ascii=False))
|
||||||
|
|
||||||
|
# 3) 의미 검사 (입력 쪽)
|
||||||
|
seed_schema = json.loads(_verify_asset(SEED_SCHEMA))
|
||||||
|
assert_seed_schema_capability(seed_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)
|
||||||
|
|
||||||
|
expected_runnable = [d for d in (activation_root.get("expected_runnable_domain_ids") or [])
|
||||||
|
if isinstance(d, str)]
|
||||||
|
|
||||||
|
# 5) 입력 적재
|
||||||
|
# I-9 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 = {}
|
||||||
|
domain_sidecars = {}
|
||||||
|
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": "PART3_REGISTRY_INDEX_SEAL_FAILED",
|
||||||
|
"detail": "config_sha256", "domain_id": domain_id}, ensure_ascii=False))
|
||||||
|
config = json.loads(raw)
|
||||||
|
domain_configs[domain_id] = config
|
||||||
|
config_root = config.get("domain_config", config)
|
||||||
|
declared = [row for row in (config_root.get("structure_types") or [])
|
||||||
|
if isinstance(row, dict)]
|
||||||
|
if declared:
|
||||||
|
# 사이드카 부재 규칙(§5.5) — 선언 있는 도메인의 부재·미등재는 경성 실패.
|
||||||
|
domain_sidecars[domain_id] = {
|
||||||
|
"structure_types": json.loads(_verify_asset(
|
||||||
|
"Default_Agent/domains/%s/structure_types.json" % domain_id)),
|
||||||
|
"module_role_projection": json.loads(_verify_asset(
|
||||||
|
"Default_Agent/domains/%s/module_role_projection.json" % domain_id)),
|
||||||
|
}
|
||||||
|
|
||||||
|
alias_declarations = json.loads(_verify_asset(ALIAS_DECLARATIONS))
|
||||||
|
|
||||||
|
def records_of(doc):
|
||||||
|
if not isinstance(doc, dict):
|
||||||
|
return []
|
||||||
|
return [row for row in (doc.get("records") or []) if isinstance(row, dict)]
|
||||||
|
|
||||||
|
# 경로 상수를 read_json 에 직접 넘긴다 — 선언표 검사기(U4)가 읽기를 코드에서
|
||||||
|
# 확인할 수 있는 형태다. 간접 호출로 감싸면 소비 선언이 실측 불가가 된다.
|
||||||
|
sg13_records = records_of(read_json(SG13_PATH))
|
||||||
|
sg05_records = records_of(read_json(SG05_PATH))
|
||||||
|
sg11_records = records_of(read_json(SG11_PATH))
|
||||||
|
# 읽되 비어 있음을 정상 처리(§3) — 조인 코드를 두지 않고 형식화된 사실로 남긴다.
|
||||||
|
sg02_rows = records_of(read_json(SG02_PATH))
|
||||||
|
sg07_rows = records_of(read_json(SG07_PATH))
|
||||||
|
empty_read_set = [
|
||||||
|
{"signal_id": "SG-02", "transform": "passthrough", "producer_count": 0,
|
||||||
|
"record_count": len(sg02_rows),
|
||||||
|
"reason": "closed schema declares no claim-form vocabulary"},
|
||||||
|
{"signal_id": "SG-07", "transform": "passthrough", "producer_count": 0,
|
||||||
|
"record_count": len(sg07_rows),
|
||||||
|
"reason": "closed schema declares no claim-form vocabulary"},
|
||||||
|
]
|
||||||
|
|
||||||
|
domain_envelopes = {}
|
||||||
|
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
|
||||||
|
|
||||||
|
bo_doc = try_read_json(BO_PATH)
|
||||||
|
bo_records = bo_doc if isinstance(bo_doc, list) else []
|
||||||
|
bo_raw = read_raw(BO_PATH) if bo_doc is not None else ""
|
||||||
|
|
||||||
|
slice_documents = {}
|
||||||
|
for domain_id in expected_runnable:
|
||||||
|
doc = try_read_json("%s/%s.json" % (SLICE_DIR, domain_id))
|
||||||
|
if doc is not None:
|
||||||
|
slice_documents[domain_id] = doc
|
||||||
|
|
||||||
|
# 6) 모듈 호출 — 판정 규칙을 이 파일에 쓰지 않는다.
|
||||||
|
try:
|
||||||
|
result = structure_index_compiler.compile_structure_seeds(
|
||||||
|
activation_doc, registry_index, domain_configs, domain_sidecars,
|
||||||
|
policy, alias_declarations, sg13_records, sg05_records, sg11_records,
|
||||||
|
domain_envelopes, bo_records, slice_documents)
|
||||||
|
except TypeError as exc:
|
||||||
|
raise RuntimeError(json.dumps({
|
||||||
|
"reason_code": "PART3_COMPILER_STALE", "detail": "signature_mismatch",
|
||||||
|
"signature_error": str(exc)[:200],
|
||||||
|
"remedy": PART3_DEPLOYMENT_REMEDY}, ensure_ascii=False))
|
||||||
|
assert_compiler_capability(result)
|
||||||
|
|
||||||
|
bundle = result["structure_seed_bundle"]
|
||||||
|
review_items = result["review_items"]
|
||||||
|
audit = result["audit"]
|
||||||
|
# 진입 봉인의 거래 식별자를 bundle 에 부착한다 — L2 는 signal_manifest 를
|
||||||
|
# 다시 읽지 않고(§4.3 입력 계약 밖) 이 값을 승계한다.
|
||||||
|
bundle["signal_manifest_transaction_id"] = seal["transaction_id"]
|
||||||
|
|
||||||
|
# 자기 산출을 닫힌 스키마로 검증한다.
|
||||||
|
import schema_subset_validator
|
||||||
|
schema_errors = schema_subset_validator.validate(
|
||||||
|
{"structure_seed_bundle": bundle}, seed_schema)
|
||||||
|
if schema_errors:
|
||||||
|
raise RuntimeError(json.dumps({
|
||||||
|
"reason_code": "PART3_FINAL_SCHEMA_VALIDATION_FAILED",
|
||||||
|
"stage": "L0", "errors": schema_errors[:10]}, ensure_ascii=False))
|
||||||
|
|
||||||
|
# 7) 기록 — bundle · handoff(OPEN) · receipt(실측값. 상수 PASS 금지).
|
||||||
|
write_doc(BUNDLE_PATH, canonical({"structure_seed_bundle": bundle}))
|
||||||
|
write_doc(HANDOFF_PATH, canonical({"stage1_part3_review_handoff": {
|
||||||
|
"schema_version": "stage1_part3_review_handoff.v1",
|
||||||
|
"status": "OPEN",
|
||||||
|
"review_items": review_items,
|
||||||
|
"counts": {"review_items": len(review_items)},
|
||||||
|
"created_by": TASK_NAME,
|
||||||
|
}}))
|
||||||
|
order_table = ["%s|%s|%s|%s|%s" % (
|
||||||
|
record.get("priority_rank"), record.get("domain_id"),
|
||||||
|
record.get("type_id"), record.get("module"),
|
||||||
|
record.get("admission_policy"))
|
||||||
|
for record in bundle.get("structure_records") or []]
|
||||||
|
write_doc(RECEIPT_PATH, canonical({
|
||||||
|
"schema_version": "stage1_part3_receipt.v1",
|
||||||
|
"stage": "P3-L0",
|
||||||
|
"deployment_gate": {
|
||||||
|
"checked_count": len(set(list(MODULE_MIRRORS.values())
|
||||||
|
+ list(PART3_REQUIRED_ASSETS))),
|
||||||
|
"runtime_manifest_sha256": sha_text(read_raw(RUNTIME_MANIFEST)),
|
||||||
|
"runtime_artifact_count": gate_manifest.get("runtime_artifact_count"),
|
||||||
|
"schema_capability_checked": ["theta", "structure_records[].admission_policy"],
|
||||||
|
"compiler_output_checked": ["theta", "structure_records[].order_key"],
|
||||||
|
"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),
|
||||||
|
},
|
||||||
|
"join_counts": bundle.get("counts"),
|
||||||
|
"empty_read_set_signals": empty_read_set,
|
||||||
|
"sidecar_dialects": audit.get("sidecar_dialects"),
|
||||||
|
"unlinked_sg11": audit.get("unlinked_sg11"),
|
||||||
|
"dangling_calculation_refs": audit.get("dangling_calculation_refs"),
|
||||||
|
"input_gaps": audit.get("input_gaps"),
|
||||||
|
"bo_input": {"sha256": sha_text(bo_raw) if bo_raw else None,
|
||||||
|
"record_count": len(bo_records),
|
||||||
|
"note": "BO.json 은 무봉인 입력이다 — signal 5종은 I-1 봉인, "
|
||||||
|
"registry 는 SG-01 봉인이 지키는 비대칭을 관찰로 남긴다."},
|
||||||
|
"resolved_order_table": order_table,
|
||||||
|
"created_by": TASK_NAME,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return {"status": bundle.get("status"),
|
||||||
|
"modules": staged,
|
||||||
|
"expected_runnable_domain_ids": expected_runnable,
|
||||||
|
"theta_count": len(bundle.get("theta") or []),
|
||||||
|
"structure_record_count": len(bundle.get("structure_records") or []),
|
||||||
|
"review_item_count": len(review_items),
|
||||||
|
"counts": bundle.get("counts"),
|
||||||
|
"errors": ERRORS, "warnings": WARNINGS}
|
||||||
|
|
||||||
|
|
||||||
|
_sink = io.StringIO()
|
||||||
|
with contextlib.redirect_stdout(_sink):
|
||||||
|
_init()
|
||||||
|
RESULT = main()
|
||||||
|
print(json.dumps(RESULT, ensure_ascii=False))
|
||||||
+420
@@ -0,0 +1,420 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# P3-L2 / Task_C_LE_L2_final_structure_index_writer — v1 (신설)
|
||||||
|
#
|
||||||
|
# 정본 근거 : stage_1_part_3_개정_신전략서.md §4.3 · §2(D-4)
|
||||||
|
# 계약 : 구조 레코드 = Θ 의 (도메인, type_id) 쌍마다 하나 — 레코드 생성 주체는
|
||||||
|
# registry 선언이지 SG-13·BO 가 아니다.
|
||||||
|
# 보존 등식 셋(선언·근거·BO) — 바깥 수는 이 task 가 원본 파일에서 직접 센다
|
||||||
|
# (bundle 수와 비교하면 항등식으로 퇴행한다).
|
||||||
|
# 역인덱스 셋 by_domain_id · by_module · by_bo_id. by_claim_form 금지(D-4 라).
|
||||||
|
# 판정 파일은 없다 — L1 부재. F0 의 조건부 규칙을 흉내 내지 않는다(§4.1).
|
||||||
|
# =============================================================================
|
||||||
|
- task_name: Task_C_LE_L2_final_structure_index_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_LE_L2_final_structure_index_writer (v1)
|
||||||
|
# 1) 게이트·정책·의미 검사 -> 2) bundle·handoff·원본 적재 ->
|
||||||
|
# 3) 보존 등식 셋(바깥 수 직접 계수) -> 4) 역인덱스 셋 ->
|
||||||
|
# 5) 닫힌 스키마 검증 -> 6) 기록·재읽기 -> 7) handoff FINALIZED
|
||||||
|
import contextlib
|
||||||
|
import hashlib
|
||||||
|
import io
|
||||||
|
import itertools
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
TASK_NAME = "Task_C_LE_L2_final_structure_index_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 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/structure_index_policy.v1.json"
|
||||||
|
POLICY_SCHEMA_VERSION = "stage1_structure_index_policy.v1"
|
||||||
|
INDEX_SCHEMA = "Default_Agent/platform/schemas/legal_effect_structures.schema.json"
|
||||||
|
REGISTRY_INDEX = "Default_Agent/domains/_registry_index.json"
|
||||||
|
|
||||||
|
BUNDLE_PATH = "stage1_tmp/task_le/structure_seed_bundle.json"
|
||||||
|
HANDOFF_PATH = "quality_gates/stage1_part3_review_handoff.json"
|
||||||
|
ACTIVATION_MANIFEST = "routing/domain_activation_manifest.json"
|
||||||
|
SG13_PATH = "signals/legal_effect_routes.json"
|
||||||
|
BO_PATH = "BO.json"
|
||||||
|
TARGET_PATH = "legal_effect_structures.json"
|
||||||
|
|
||||||
|
PART3_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": "PART3_ASSET_DEPLOYMENT_INCOMPLETE", "path": logical,
|
||||||
|
"detail": detail, "remedy": PART3_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_index_schema_capability(index_schema):
|
||||||
|
# 의미 검사(자기 스키마) — by_module · by_bo_id 를 아는가(PART3_INDEX_SCHEMA_STALE).
|
||||||
|
index_props = (((index_schema.get("properties") or {})
|
||||||
|
.get("structure_index") or {}).get("properties") or {})
|
||||||
|
missing = [key for key in ("by_module", "by_bo_id") if key not in index_props]
|
||||||
|
if missing:
|
||||||
|
raise RuntimeError(json.dumps({
|
||||||
|
"reason_code": "PART3_INDEX_SCHEMA_STALE",
|
||||||
|
"message": "색인 스키마가 by_module·by_bo_id 를 모른다. "
|
||||||
|
"배포 원본의 legal_effect_structures.schema.json 이 개정 전 판본이다.",
|
||||||
|
"missing": missing, "remedy": PART3_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": "PART3_POLICY_INVALID",
|
||||||
|
"expected": POLICY_SCHEMA_VERSION,
|
||||||
|
"actual": policy.get("schema_version")}, ensure_ascii=False))
|
||||||
|
index_schema = json.loads(_verify_asset(INDEX_SCHEMA))
|
||||||
|
assert_index_schema_capability(index_schema)
|
||||||
|
|
||||||
|
# 입력은 전부 파일 계약. 판정 파일은 없다(L1 부재 — §4.1).
|
||||||
|
bundle_doc = read_json(BUNDLE_PATH)
|
||||||
|
bundle = bundle_doc.get("structure_seed_bundle", bundle_doc)
|
||||||
|
records = [row for row in (bundle.get("structure_records") or [])
|
||||||
|
if isinstance(row, dict)]
|
||||||
|
handoff_doc = read_json(HANDOFF_PATH)
|
||||||
|
handoff = handoff_doc.get("stage1_part3_review_handoff", handoff_doc)
|
||||||
|
activation_doc = read_json(ACTIVATION_MANIFEST)
|
||||||
|
activation_root = activation_doc.get("domain_activation_manifest", activation_doc)
|
||||||
|
expected_runnable = [d for d in (activation_root.get("expected_runnable_domain_ids") or [])
|
||||||
|
if isinstance(d, str)]
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 보존 등식 셋 — 바깥 수를 원본에서 직접 센다.
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# (1) 선언 보존 — Σ(expected_runnable 도메인의 structure_types 개수).
|
||||||
|
# domain_config 무결성은 SG-01 봉인 -> _registry_index -> config_sha256 사슬 재사용.
|
||||||
|
index_raw = read_raw(REGISTRY_INDEX)
|
||||||
|
if activation_root.get("registry_index_sha256") != sha_text(index_raw):
|
||||||
|
raise RuntimeError(json.dumps({
|
||||||
|
"reason_code": "PART3_REGISTRY_INDEX_SEAL_FAILED",
|
||||||
|
"stage": "L2"}, ensure_ascii=False))
|
||||||
|
registry_index = json.loads(index_raw)
|
||||||
|
index_root = registry_index.get("domain_registry_index", registry_index)
|
||||||
|
config_sha = {row.get("domain_id"): row.get("config_sha256")
|
||||||
|
for row in (index_root.get("entries") or []) if isinstance(row, dict)}
|
||||||
|
declared_total = 0
|
||||||
|
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": "PART3_REGISTRY_INDEX_SEAL_FAILED",
|
||||||
|
"detail": "config_sha256", "domain_id": domain_id}, ensure_ascii=False))
|
||||||
|
config = json.loads(raw)
|
||||||
|
config_root = config.get("domain_config", config)
|
||||||
|
declared_total += len([row for row in (config_root.get("structure_types") or [])
|
||||||
|
if isinstance(row, dict)])
|
||||||
|
declaration_pass = (len(records) == declared_total)
|
||||||
|
|
||||||
|
# (2) 근거 보존 — SG-13 원본에서 legal_effect_route 총수를 직접 센다.
|
||||||
|
# 붙은 route 는 레코드에서 재계수한다(bundle counts 를 믿지 않는다).
|
||||||
|
sg13_doc = read_json(SG13_PATH)
|
||||||
|
sg13_total = len([row for row in (sg13_doc.get("records") or [])
|
||||||
|
if isinstance(row, dict)
|
||||||
|
and row.get("record_type") == "legal_effect_route"])
|
||||||
|
attached_signal_ids = set()
|
||||||
|
for record in records:
|
||||||
|
for ref in record.get("routes") or []:
|
||||||
|
if isinstance(ref, dict) and isinstance(ref.get("signal_id"), str):
|
||||||
|
attached_signal_ids.add(ref["signal_id"])
|
||||||
|
routes_unattached = len(bundle.get("unattached_routes") or [])
|
||||||
|
route_pass = (len(attached_signal_ids) + routes_unattached == sg13_total)
|
||||||
|
|
||||||
|
# (3) BO 보존 — BO.json 원본에서 총수를 직접 센다.
|
||||||
|
bo_doc = read_json(BO_PATH)
|
||||||
|
bo_total = len(bo_doc) if isinstance(bo_doc, list) else 0
|
||||||
|
attached_bo = set()
|
||||||
|
for record in records:
|
||||||
|
for bo_id in record.get("source_bo_ids") or []:
|
||||||
|
if isinstance(bo_id, str):
|
||||||
|
attached_bo.add(bo_id)
|
||||||
|
bo_unattached = len(bundle.get("unattached_bo") or [])
|
||||||
|
bo_pass = (len(attached_bo) + bo_unattached == bo_total)
|
||||||
|
|
||||||
|
failures = []
|
||||||
|
if not declaration_pass:
|
||||||
|
failures.append({"reason_code": "PART3_STRUCTURE_COUNT_BROKEN",
|
||||||
|
"records": len(records), "declared_total": declared_total})
|
||||||
|
if not route_pass:
|
||||||
|
failures.append({"reason_code": "PART3_ROUTE_CONSERVATION_BROKEN",
|
||||||
|
"attached": len(attached_signal_ids),
|
||||||
|
"unattached": routes_unattached, "sg13_total": sg13_total})
|
||||||
|
if not bo_pass:
|
||||||
|
failures.append({"reason_code": "PART3_BO_CONSERVATION_BROKEN",
|
||||||
|
"attached": len(attached_bo),
|
||||||
|
"unattached": bo_unattached, "bo_total": bo_total})
|
||||||
|
if failures:
|
||||||
|
raise RuntimeError(json.dumps({"conservation_failures": failures},
|
||||||
|
ensure_ascii=False))
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 역인덱스 셋 — ID 목록만 담는다. 본문을 복제하지 않는다.
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
by_domain_id = {}
|
||||||
|
by_module = {}
|
||||||
|
by_bo_id = {}
|
||||||
|
for record in records:
|
||||||
|
structure_id = record.get("structure_id")
|
||||||
|
by_domain_id.setdefault(record.get("domain_id"), []).append(structure_id)
|
||||||
|
if record.get("module"):
|
||||||
|
by_module.setdefault(record["module"], []).append(structure_id)
|
||||||
|
for bo_id in record.get("source_bo_ids") or []:
|
||||||
|
by_bo_id.setdefault(bo_id, []).append(structure_id)
|
||||||
|
for index in (by_domain_id, by_module, by_bo_id):
|
||||||
|
for key in index:
|
||||||
|
index[key] = sorted(set(index[key]))
|
||||||
|
|
||||||
|
# 검토 재산정 — handoff 항목을 코드·심각도로 묶는다. review 는 근거 없이 줄지 않는다.
|
||||||
|
review_queue = []
|
||||||
|
queue_keys = {}
|
||||||
|
for item in handoff.get("review_items") or []:
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
continue
|
||||||
|
key = (item.get("review_code"), item.get("severity"))
|
||||||
|
if key in queue_keys:
|
||||||
|
queue_keys[key]["count"] += 1
|
||||||
|
else:
|
||||||
|
row = {"review_code": key[0], "severity": key[1] or "SOFT_WARNING",
|
||||||
|
"count": 1}
|
||||||
|
queue_keys[key] = row
|
||||||
|
review_queue.append(row)
|
||||||
|
review_queue.sort(key=lambda row: (row["review_code"], row["severity"]))
|
||||||
|
status = "READY_WITH_REVIEW" if (review_queue or any(
|
||||||
|
record.get("review_required") for record in records)) else "READY"
|
||||||
|
|
||||||
|
final_doc = {
|
||||||
|
"schema_version": "stage1_legal_effect_structures.v1",
|
||||||
|
"status": status,
|
||||||
|
"registry_version": bundle.get("registry_version"),
|
||||||
|
"registry_index_sha256": sha_text(index_raw),
|
||||||
|
"activation_manifest_sha256": sha_text(read_raw(ACTIVATION_MANIFEST)),
|
||||||
|
"signal_manifest_transaction_id": bundle.get("signal_manifest_transaction_id"),
|
||||||
|
"expected_runnable_domain_ids": expected_runnable,
|
||||||
|
"theta": bundle.get("theta") or [],
|
||||||
|
"structure_records": records,
|
||||||
|
"structure_index": {"by_domain_id": by_domain_id,
|
||||||
|
"by_module": by_module,
|
||||||
|
"by_bo_id": by_bo_id},
|
||||||
|
"quality_gate": {
|
||||||
|
"declaration_conservation": {
|
||||||
|
"structure_records": len(records),
|
||||||
|
"declared_total_from_config": declared_total,
|
||||||
|
"conservation_pass": declaration_pass,
|
||||||
|
},
|
||||||
|
"route_conservation": {
|
||||||
|
"routes_attached": len(attached_signal_ids),
|
||||||
|
"routes_unattached": routes_unattached,
|
||||||
|
"sg13_route_total_from_source": sg13_total,
|
||||||
|
"conservation_pass": route_pass,
|
||||||
|
},
|
||||||
|
"bo_conservation": {
|
||||||
|
"bo_attached": len(attached_bo),
|
||||||
|
"bo_unattached": bo_unattached,
|
||||||
|
"bo_total_from_source": bo_total,
|
||||||
|
"conservation_pass": bo_pass,
|
||||||
|
},
|
||||||
|
"review_queue": review_queue,
|
||||||
|
},
|
||||||
|
"created_by": TASK_NAME,
|
||||||
|
}
|
||||||
|
|
||||||
|
# 닫힌 스키마 검증 -> 기록 -> 재읽기.
|
||||||
|
schema_errors = schema_subset_validator.validate(final_doc, index_schema)
|
||||||
|
if schema_errors:
|
||||||
|
raise RuntimeError(json.dumps({
|
||||||
|
"reason_code": "PART3_FINAL_SCHEMA_VALIDATION_FAILED",
|
||||||
|
"stage": "L2", "errors": schema_errors[:10]}, ensure_ascii=False))
|
||||||
|
text = canonical(final_doc)
|
||||||
|
write_doc(TARGET_PATH, text)
|
||||||
|
reread = read_raw(TARGET_PATH)
|
||||||
|
if sha_text(reread) != sha_text(text):
|
||||||
|
raise RuntimeError("PART3_WRITE_REREAD_MISMATCH")
|
||||||
|
|
||||||
|
# handoff FINALIZED 갱신(공동 기록자). 항목을 줄이지 않는다.
|
||||||
|
handoff["status"] = "FINALIZED"
|
||||||
|
handoff["finalized_by"] = TASK_NAME
|
||||||
|
write_doc(HANDOFF_PATH, canonical({"stage1_part3_review_handoff": handoff}))
|
||||||
|
|
||||||
|
return {"status": status, "modules": staged,
|
||||||
|
"structure_record_count": len(records),
|
||||||
|
"theta_count": len(bundle.get("theta") or []),
|
||||||
|
"index_bucket_counts": {"by_domain_id": len(by_domain_id),
|
||||||
|
"by_module": len(by_module),
|
||||||
|
"by_bo_id": len(by_bo_id)},
|
||||||
|
"quality_gate_pass": {"declaration": declaration_pass,
|
||||||
|
"route": route_pass, "bo": bo_pass},
|
||||||
|
"review_queue_count": len(review_queue)}
|
||||||
|
|
||||||
|
|
||||||
|
_sink = io.StringIO()
|
||||||
|
with contextlib.redirect_stdout(_sink):
|
||||||
|
_init()
|
||||||
|
RESULT = main()
|
||||||
|
print(json.dumps(RESULT, ensure_ascii=False))
|
||||||
+1037
File diff suppressed because it is too large
Load Diff
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
{"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 집합으로의 이관은 Part 3 개정의 몫이다.","order":3,"part":"Stage_1.Part_3","produces":[],"requires":["BO.json","signals/signal_manifest.json"],"yaml":"Stage_1_Part_3_Claude_v2.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":[],"requires":["BO.json","signals/signal_manifest.json"],"yaml":"Stage_1_Part_4_Claude_v3.yml"}],"status":"ACTIVE"}
|
||||||
|
|||||||
+27
-242
@@ -2110,10 +2110,6 @@ MEMORY.md 파일에는 지금까지의 작업 내역들이 압축적으로 요
|
|||||||
--> 이 기준을 prompt에 명시해야 한다.
|
--> 이 기준을 prompt에 명시해야 한다.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
┌────────────────────────────────────────────┐
|
┌────────────────────────────────────────────┐
|
||||||
│ │
|
│ │
|
||||||
│ 1st Execution of Update Strategy of Part 3 │
|
│ 1st Execution of Update Strategy of Part 3 │
|
||||||
@@ -2125,21 +2121,21 @@ MEMORY.md 파일에는 지금까지의 작업 내역들이 압축적으로 요
|
|||||||
</goal>
|
</goal>
|
||||||
|
|
||||||
<context>
|
<context>
|
||||||
지금까지 stage 1 -- part 1|2 개정작업을 진행했다.
|
지금까지 stage 1 -- part 1|2 개정작업을 진행하여 yaml 작업명세서 `stage_1_part_1_v.8.yml`와 `stage_1_part_2_v.8.yml`를 생성했다.
|
||||||
`stage_1_part_1_v.8.yml`, `stage_1_part_2_v.8.yml`를 검증하여 두 작업명세서가 모두 의도한 작업 과정을 실행하는 것을 확인했다.
|
두 yaml 작업명세서는 검증을 통해 모두 의도한 작업 과정을 실행함을 확인했다.
|
||||||
|
|
||||||
이제 `stage_1_part_3_개정_신전략서.md`가 제시하는 방식을 실행하여 part 3 작업의 궁극적 목적(목표/목적물)을 '최소 작업으로 최고 품질로 달성'하는 원칙을 기반으로 두고 최적 워크플로우를 구성하여 작업명세서(yaml & related assets)를 개정 생성하고자 한다.
|
이제 `stage_1_part_3_개정_신전략서.md`가 제시하는 방식을 실행하여 part 3 작업의 궁극적 목적(목표/목적물)을 '최소 작업으로 최고 품질로 달성'하는 원칙을 달성하는 part 3 작업명세서(yaml & related assets)를 생성하고자 한다.
|
||||||
</context>
|
</context>
|
||||||
|
|
||||||
<method>
|
<method>
|
||||||
1. `stage_1_part_3_개정_신전략서.md`의 작업 내용들을 독립적으로 동시 실행가능한 task들과, 직렬로 sequential 방식으로 실행해야 할 task들로 분류한다.
|
1. `stage_1_part_3_개정_신전략서.md`의 작업 내용들을 독립적으로 동시 실행가능한 task들과, 직렬로 sequential 방식으로 실행해야 할 task들로 분류한다.
|
||||||
2. 1의 결과를 바탕으로 독립 동시 실행 가능한 task들은 병렬로 동시 실행하고, sequential 방식으로 실행해야 하는 task들은 upstream-downstream 작업 consistency를 유지하면서 실행한다.
|
2. 1의 결과를 바탕으로 독립 동시 실행 가능한 task들은 병렬로 동시 실행하고, sequential 방식으로 실행해야 하는 task들은 upstream-downstream 작업 consistency를 유지하면서 실행하여 part 3의 yaml 작업명세서를 작성한다.
|
||||||
3. 2번 작업을 수행한 후 sub-agent를 띄워서 2번 작업이 `stage_1_part_3_개정_신전략서.md`가 지시하는 사항들을 100% 정확하게 준수하여 작업을 `완수`했는지 검증한다. Sub-agent가 100% 검증 통과라고 판정할 때까지, 2번 작업을 반복한다.
|
3. 2번 작업을 수행한 후 sub-agent를 띄워서 2번 작업이 `stage_1_part_3_개정_신전략서.md`가 지시하는 사항들을 100% 정확하게 준수하여 작업을 수행했는지 검증한다. Sub-agent가 100% 검증 통과라고 판정할 때까지, 2번 작업(yaml 작성 & 검증)을 반복한다.
|
||||||
4. 3번 작업이 완료되면, 작업 결과물 중 저장해야 할 자산들(assets)을 `extension_research/Default_Agent` 폴더에 배포 위치를 정확히 지켜서 저장하고, part 3 작업명세서를 `extension_research/ver_8_yaml_candidates/stage_1_part_3_v.8.yml`로 생성한다.
|
4. 3번 작업이 완료되면, 작업 결과물 중 저장해야 할 자산들(assets)을 `extension_research/Default_Agent` 폴더에 배포 위치를 정확히 지켜서 저장하고, part 3 작업명세서를 `extension_research/ver_8_yaml_candidates/stage_1_part_3_v.8.yml`로 생성한다.
|
||||||
|
|
||||||
<constraints>
|
<constraints>
|
||||||
1. 작업 시, 기존 yaml 작업명세서('v.7/Claude_YAML'의 yaml 파일들)와 관련한 dependency는 완전히 제거한다. 즉, 기존 yaml 작업 명세서가 지시하는 내용에 전혀 의존하지 않도록 한다.
|
1. 작업 시, 기존 yaml 작업명세서('v.7/Claude_YAML'의 yaml 파일들)와 관련한 dependency는 완전히 제거한다. 즉, 기존 yaml 작업 명세서가 지시하는 내용에 전혀 의존하지 않도록 한다.
|
||||||
2. Stage 1 -- part 1|2 작업이 사용하는 자산들의 명칭과 배포 위치 그리고 포맷은 `extension_research/Default_Agent` 폴더에 저장되어 있으며, 앞으로 Part 3 개정 작업을 통해 생성될 Stage 1 (part 상관없이) 실행 시 사용될 모든 자산(md, .py, .txt, .json)들은 이 폴더에 정확한 배포 위치 서브 폴더에 저장한다.
|
2. Stage 1 -- part 1|2 작업이 사용하는 자산들의 명칭과 배포 위치 그리고 포맷은 `extension_research/Default_Agent` 폴더에 저장되어 있으며, 앞으로 개정 생성될 Part 3 yaml 작업명세서가 실행되어 생성될 모든 자산(md, .py, .txt, .json)들은 이 폴더의 정확한 배포 위치(서브 폴더)에 저장한다.
|
||||||
3. Liti-agent가 stage # yaml 작업명세서를 실행하여 작업을 `완수`했다는 표현을 사용할 때, `완수`의 기준은 단순히 yaml 작업명세서를 끝까지 실행 완주할 수 있다는 것이 아니다. Yaml 작업명세서를 실행했을 때, 원래 의도한 결과물들이 올바른 방식으로 생성되어야 하고, 소작업(sub-task)들의 upstream-downstream 간에 consistency(넘겨받은 파일을 올바로 읽고, handoff로 넘겨주는 파일도 정해진 포맷에 맞게 넘겨줌)를 유지하여 작업이 끝까지 이어진 경우를 작업이 `완수`되었다고 평가한다.
|
3. Liti-agent가 stage # yaml 작업명세서를 실행하여 작업을 `완수`했다는 표현을 사용할 때, `완수`의 기준은 단순히 yaml 작업명세서를 끝까지 실행 완주할 수 있다는 것이 아니다. Yaml 작업명세서를 실행했을 때, 원래 의도한 결과물들이 올바른 방식으로 생성되어야 하고, 소작업(sub-task)들의 upstream-downstream 간에 consistency(넘겨받은 파일을 올바로 읽고, handoff로 넘겨주는 파일도 정해진 포맷에 맞게 넘겨줌)를 유지하여 작업이 끝까지 이어진 경우를 작업이 `완수`되었다고 평가한다.
|
||||||
4. Stage 1 전체 작업에 대한 아래 내용을 절대 규칙으로 삼는다.
|
4. Stage 1 전체 작업에 대한 아래 내용을 절대 규칙으로 삼는다.
|
||||||
<absolute_principle_for_whole_stage_1>
|
<absolute_principle_for_whole_stage_1>
|
||||||
@@ -2156,6 +2152,15 @@ MEMORY.md 파일에는 지금까지의 작업 내역들이 압축적으로 요
|
|||||||
- yaml 작성을 할 때는 <v.7/SKILL.md>의 규칙을 엄격하게 준수하며, python code를 작성할 때는 <v.7/extension_research/test_code_executor.ipynb>가 제시하는 Python code 작성 규칙을 엄수한다.
|
- yaml 작성을 할 때는 <v.7/SKILL.md>의 규칙을 엄격하게 준수하며, python code를 작성할 때는 <v.7/extension_research/test_code_executor.ipynb>가 제시하는 Python code 작성 규칙을 엄수한다.
|
||||||
</global_constraints>
|
</global_constraints>
|
||||||
|
|
||||||
|
==================================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--> stage 1 -- part 3 개정 작업 최적 워크플로우 탐구 --> 작업 명세서 생성
|
||||||
|
--> stage 1 -- part 3 개정 --> 남은 작업 check --> 추가작업 명세서 생성 및 작업 수행 --> part 3 개정 마무리
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2188,102 +2193,6 @@ MEMORY.md 파일에는 지금까지의 작업 내역들이 압축적으로 요
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
┌────────────────────────────────────────────┐
|
|
||||||
│ │
|
|
||||||
│ 1st Execution of Update Strategy of Part 3 │
|
|
||||||
│ │
|
|
||||||
└────────────────────────────────────────────┘
|
|
||||||
|
|
||||||
<goal>
|
|
||||||
`stage_1_part_3_개정방안전략서.md`를 실행하여 stage 1 -- part 3 작업명세서를 개정한다.
|
|
||||||
</goal>
|
|
||||||
|
|
||||||
<context>
|
|
||||||
지금까지 stage 1 -- part 1|2 개정작업을 진행했다.
|
|
||||||
`stage_1_part_1_v.8.yml`, `stage_1_part_2_v.8.yml`를 검증하여 두 작업명세서가 모두 의도한 작업 과정을 실행하는 것을 확인했다.
|
|
||||||
|
|
||||||
이제 `stage_1_part_3_개정방안전략서.md`가 제시하는 방식을 실행하여 part 3 작업명세서(yaml & related assets)를 개정하고자 한다.
|
|
||||||
</context>
|
|
||||||
|
|
||||||
<method>
|
|
||||||
1. 구 part 3 yaml('v.7/Claude_YAML/Stage_1_Part_3_Claude_v2.yml')를 확인한다.
|
|
||||||
2. `stage_1_part_3_개정방안전략서.md`가 제시하는 개정 방법을 읽고, 독립 실행 가능한 task들과 sequential 작업이 필요한 task들로 분류한다.
|
|
||||||
3. 2번 과정에서 분류한 task들에 대해서 독립 실행 가능한 task들은 병렬로 작업을 수행하고, sequential 작업이 필요한 task들은 upstream-downstream consistency를 유지하면서 작업을 수행한다. 작업 수행 시 아래 제시한 <constraints>를 준수한다.
|
|
||||||
4. 3번 작업 과정에서 얻은 결과물들은 기본적으로 `v.7/extension_research/ver_8_yaml_candidates` 폴더에 '정확한 배포 위치'로 저장하는 것을 원칙으로 한다. 작업 수행 과정에서 기존 stage 1 -- part 3의 개별 작업용 yaml 파일들, md 파일들, python code (.py|.txt)들을 수정하거나 재작성하면 기존 파일들은 outdated 폴더로 옮기고, 수정 및 재작성한 파일은 기존 파일들을 대체하는 방식으로 정확한 배포 위치에 생성한다. 신규 작성한 자산이 있다면 그 역시 정확한 배포 위치에 저장한다.
|
|
||||||
5. 작업을 완료하면 독립적으로 sub-agent를 띄워서 `stage_1_part_3_개정방안전략서.md`가 제시하는 개정 방법에 따라 작업이 100% 수행되었는지 검증한다. 만일 검증을 통과하지 못한 작업 내용이 존재하면 그 작업 내역만을 incremental 수정한다. 이 과정은 sub-agent의 검증을 통과할 때까지 반복한다.
|
|
||||||
6. 5번 검증까지 통과하여 작업을 완료하면 작업 내역서를 문서로 작성하여 'v.7/extension_research/stage_1_part_3_1차개정작업.md'로 생성한다.
|
|
||||||
|
|
||||||
<constraints>
|
|
||||||
1. 작업 시, 기존 yaml 작업명세서('v.7/Claude_YAML'의 yaml 파일들)와 관련한 dependency가 없도록 한다. 즉, 기존 yaml 작업 명세서가 지시하는 내용에 절대 의존하면 안된다. 새로 작성하는 yaml이나 자산들은 기존 작업명세서에서 지시하는 내용과는 별개로 독립적인 결과물이어야 한다.
|
|
||||||
2. Stage 1 -- part 1|2 작업이 사용하는 자산들의 명칭과 배포 위치 그리고 포맷들을 정확히 활용한다.
|
|
||||||
3. Stage 1 전체 작업에 대한 아래의 절대 규칙을 'conerstone'으로 삼는다.
|
|
||||||
<absolute_principle_for_whole_stage_1>
|
|
||||||
- Liti-agent는 대한민국 민사소송 사건종류 137종을 모두 다룰 수 있도록 개발되어야 한다.
|
|
||||||
- Stage 1 에서는 Liti-agent가 137종 중 그 어떤 사건을 다루더라도 `레지스트리 기반` 방식으로 사건 종류의 이름이 아니라 증거 구성요소·요건 슬롯·단서로 구조화된 정보를 확보하는 것을 목표로 한다. 이를 위해 사용할 자산들이 '법리 도메인, 계산 도메인, signal'이며 이에 대한 정의는 `v.7/extension_research/ensemble_v2.md`에 제시되어 있다.
|
|
||||||
</absolute_principle_for_whole_stage_1>
|
|
||||||
</constraints>
|
|
||||||
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<global_constraints>
|
|
||||||
- `v.7/CLAUDE.md`가 제시하는 Opus 5 행동 규칙을 준수한다.
|
|
||||||
- 작업 완료 시 작업 내역을 압축 요약하여 `v.7/MEMORY.md`에 추가 기입한다.
|
|
||||||
- yaml 작성을 할 때는 <v.7/SKILL.md>의 규칙을 엄격하게 준수하며, python code를 작성할 때는 <v.7/extension_research/test_code_executor.ipynb>가 제시하는 Python code 작성 규칙을 엄수한다.
|
|
||||||
</global_constraints>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--> stage 1 -- part 3 개정 --> 남은 작업 check --> 추가작업 명세서 생성 및 작업 수행 --> part 3 개정 마무리
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[다음]
|
|
||||||
- stage 1 -- part 2에서 더 개정할 작업이 남아있는가?
|
|
||||||
- if no -> 오늘 stage 1 -- part 2 개정 작업 결과물을 읽어들여서 `stage_1_part_1_and_2_updated_yaml_analysis.md`를 incremental update하여 `stage_1_part_1_and_2_updated_yaml_analysis.md`로 생성 (기존 파일은 `stage_1_part_1_and_2_updated_yaml_analysis_old.md`로 파일명을 바꿔서 outdated 폴더로 이송)
|
|
||||||
- `stage_1_part_1_and_2_updated_yaml_analysis.md` 내용을 기반으로 part 3 최적 개정 방식 탐구
|
|
||||||
- 기존 stage_1_update_strategy.md가 제시한 part 3 개정 작업 내용 및 DAG 형식 참조
|
|
||||||
|
|
||||||
|
|
||||||
Part 3 — 법률효과 구조
|
|
||||||
P3-L0 -> P3-L1 -> P3-L2
|
|
||||||
§4. Part 3 세부 워크플로우
|
|
||||||
- §4.1 P3-L0 구조 seed 축약 [개정]
|
|
||||||
- §4.2 P3-L1 예외 판정
|
|
||||||
- §4.3 P3-L2 최종 색인 기록 [개정]
|
|
||||||
|
|
||||||
최적 개정 작업 전략서 작성
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
┌─────────────────────────────┐
|
|
||||||
│ Must Task │
|
|
||||||
└─────────────────────────────┘
|
|
||||||
MEMORY.md에 기록된 {{실측이 리포트와 갈린 곳 · 문서가 처음 기록한 것}}에 제시된 내용을 참조한 리포트는 파일명 마지막에 '_outdated'를 붙여서 남기고, {{실측이 리포트와 갈린 곳 · 문서가 처음 기록한 것}} 내용을 반영하여 기존 리포트에 반영하여 새 리포트를 동일한 파일명으로 생성하라.
|
|
||||||
|
|
||||||
|
|
||||||
=> 리포트에 반영하여, 리포트 개정 요망
|
|
||||||
|
|
||||||
|
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
## 개정 작업에서 지켜야 할 절대적인 기준(cornerstone):
|
## 개정 작업에서 지켜야 할 절대적인 기준(cornerstone):
|
||||||
@@ -2314,13 +2223,6 @@ Opus 5에 명시적으로 검증 작업을 시킬 때, 검증 100% 통과할 때
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[2] [1]의 md 문서를 기준으로
|
|
||||||
--> stage 1 -- part 3 개정 작업 최적 워크플로우 탐구 --> 작업 명세서 생성
|
|
||||||
--> stage 1 -- part 3 개정 --> 남은 작업 check --> 추가작업 명세서 생성 및 작업 수행 --> part 3 개정 마무리
|
|
||||||
|
|
||||||
|
|
||||||
[3]
|
[3]
|
||||||
Stage 1 -- part 1, 2, 3 개정 작업 요약
|
Stage 1 -- part 1, 2, 3 개정 작업 요약
|
||||||
기존 stage 1 -- part 1, 2 개정 작업 요약본에 stage 1 -- part 3 개정 작업본을 추가 입력하여 업데이트
|
기존 stage 1 -- part 1, 2 개정 작업 요약본에 stage 1 -- part 3 개정 작업본을 추가 입력하여 업데이트
|
||||||
@@ -2342,42 +2244,6 @@ Stage 1 -- part 1, 2, 3 개정 작업 요약
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Part 2 작업 검증
|
Part 2 작업 검증
|
||||||
Part 3 개정 작업 리서치
|
Part 3 개정 작업 리서치
|
||||||
Part 3 개정 yaml 작성
|
Part 3 개정 yaml 작성
|
||||||
@@ -2387,6 +2253,13 @@ Part 4 개정 yaml 작성
|
|||||||
--> 안종선이 step by step으로 실행하고, 결과물을 내가 체크하여 수정, 추후 다시 실행
|
--> 안종선이 step by step으로 실행하고, 결과물을 내가 체크하여 수정, 추후 다시 실행
|
||||||
--> stage 1 4개 yaml 파일들이 문제없이 실행되도록 완료
|
--> stage 1 4개 yaml 파일들이 문제없이 실행되도록 완료
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
┌────────────────────────────────────────────┐
|
||||||
|
│ Sketch of How to Update Stage 2 │
|
||||||
|
└────────────────────────────────────────────┘
|
||||||
|
|
||||||
[Stage 1 작업 내역들 + DAG + stage 2 작업 목표 + 기존 stage 2 yaml] 종합하여 stage 2 최적 워크플로우 탐구
|
[Stage 1 작업 내역들 + DAG + stage 2 작업 목표 + 기존 stage 2 yaml] 종합하여 stage 2 최적 워크플로우 탐구
|
||||||
-->
|
-->
|
||||||
큰 덩어리:
|
큰 덩어리:
|
||||||
@@ -2412,10 +2285,14 @@ Part 4 개정 yaml 작성
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
`stage_1_part_2_optimal_update_strategy.md`를 수행한다.
|
|
||||||
|
|
||||||
|
|
||||||
==========================================================================
|
==========================================================================
|
||||||
|
┌────────────────────────────────────────────┐
|
||||||
|
│ Structure of Prompt │
|
||||||
|
└────────────────────────────────────────────┘
|
||||||
|
|
||||||
<goal>
|
<goal>
|
||||||
|
|
||||||
</goal>
|
</goal>
|
||||||
@@ -2463,98 +2340,6 @@ Part 4 개정 yaml 작성
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[질문]
|
|
||||||
1. `registry_component_ids`를 `evidence_indexed` 내부의 항목으로 표현되도록 `evidence_indexed` json schema에 포함시키면 되나?
|
|
||||||
만일 답이 '예'라면, 'part_2_update_addition.md'의 "§1. `registry_component_ids`는 파일이 아니다"에 제시된 내용을 반영하여 작업을 실행하면 `registry_component_ids`를 `evidence_indexed` 내부의 항목으로 표현되게 하는가?
|
|
||||||
|
|
||||||
2. `evidence_indexed`를 검증하는 json schema가 반드시 필요한가? 예, 아니오로 답하고 이유를 한 문단 이내로 설명하라.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
조건
|
|
||||||
Stage 1 part 1 단일 yaml 파일 생성
|
|
||||||
DAG 표현 정확히 기입
|
|
||||||
Python code 표현 정확히
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
케이무크 작업 실행 계획 세우기
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
4 + 2 + 3 + 0.5 = 9.5
|
|
||||||
1 + 1 + 0.5 + 0.5 = 3
|
|
||||||
|
|
||||||
12.5 1억 2천 5백
|
|
||||||
총 2억 모음 --> tips 진행
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user