WBS-7.3/7.4/7.5/7.11: 거버넌스 문서 정합성 정리 + spec-코드 동기화 게이트

2026-06-21 비판적 리뷰에서 spec/governance YAML이 코드 상태와 어긋난
채로 방치되던 3개 구체적 사례를 발견하고 정정했다. 근본 원인(동기화를
보장하는 장치 없음)에 대응하는 신규 CI 게이트도 함께 추가한다.

- spec/aliases.yaml: deprecated alias 17건 제거(활성 참조 0건 확인 후,
  2026-06-30 데드라인 전). role: deprecated_redirect인 spec/03_risk_policy.yaml,
  spec/04_strategy_rules.yaml 2개만 실삭제 — spec/06_exit_policy.yaml은
  role: compatibility_index(영구유지 설계)였음을 재확인해 보존
- governance/gas_logic_migration_ledger_v1.yaml: 존재하지 않는 파일을
  canonical 구현으로 인용하던 오류 2건 발견·정정, parity 테스트 부재로
  GAS 코드 삭제 보류(F12/F13/F14)
- spec/13_formula_registry.yaml: OVERHANG_PRESSURE_V1의 "-500000"
  절대값 폴백을 avg_volume_5d 비례식으로 교체(EXPERT_PRIOR 등록)
- tools/validate_specs.py: validate_spec_code_sync() 신규 — has_code_implementation/
  code_path 필드가 있는 spec만 검사(점진적 롤아웃, 기존 PASS 상태 비파괴),
  12개 파일 1차 태깅
This commit is contained in:
2026-06-21 20:08:48 +09:00
parent ee4d1fdab8
commit 5166750b53
25 changed files with 351 additions and 165 deletions
+2 -2
View File
@@ -246,7 +246,6 @@ spec_files:
data_gaps_roadmap: "spec/16_data_gaps_roadmap.yaml"
performance_contract: "spec/17_performance_contract.yaml"
settings_contract: "spec/18_settings_contract.yaml"
risk_policy_index: "spec/03_risk_policy.yaml"
risk_control_index: "spec/risk/risk_control.yaml"
aggregate_risk: "spec/risk/aggregate_risk.yaml"
circuit_breakers: "spec/risk/circuit_breakers.yaml"
@@ -254,7 +253,6 @@ spec_files:
portfolio_exposure: "spec/risk/portfolio_exposure.yaml"
risk_quality_control: "spec/risk/quality_control.yaml"
factor_risk: "spec/risk/factor_risk.yaml"
strategy_rules_index: "spec/04_strategy_rules.yaml"
sector_model: "spec/strategy/sector_model.yaml"
entry_gates_index: "spec/strategy/entry_gates.yaml"
entry_core: "spec/strategy/entry_core.yaml"
@@ -297,6 +295,7 @@ spec_files:
event_response: "spec/exit/event_response.yaml"
position_review: "spec/exit/position_review.yaml"
dynamic_value_preservation_sell_v3: "spec/exit/dynamic_value_preservation_sell_v3.yaml"
qualitative_sell_strategy_v1: "spec/exit/qualitative_sell_strategy_v1.yaml"
output_schema: "spec/07_output_schema.yaml"
machine_output_schema: "schemas/output_schema.json"
report_templates: "RetirementAssetPortfolioReportTemplate.yaml"
@@ -337,6 +336,7 @@ spec_files:
- "spec/exit/event_response.yaml"
- "spec/exit/position_review.yaml"
- "spec/exit/dynamic_value_preservation_sell_v3.yaml"
- "spec/exit/qualitative_sell_strategy_v1.yaml"
strategy:
- "spec/strategy/sector_model.yaml"
- "spec/strategy/entry_gates.yaml"
+39 -2
View File
@@ -10,6 +10,23 @@ classification_summary:
display_text: 1
unclassified_findings: 0
# WBS-7.3 재검토 (2026-06-21):
# - F01/F09 (REGISTER_*): DONE으로 정정 — spec/calibration_registry.yaml에 이미
# 등록되어 있었음(P5-T01 wave1). 레저 상태가 stale했을 뿐 실작업 불필요.
# - F12/F13 (DELETE_DISTRIBUTION_RISK_GAS): ledger의 "build_distribution_risk_v1.py"
# 인용은 오류(존재하지 않는 파일) — 실제는 build_distribution_risk_score_v2.py가
# 동일 필드를 산출하나, GAS-Python parity 테스트가 전혀 없어 삭제를 보류.
# - F14 (DELETE_LATE_CHASE_RISK_GAS): ledger의 전제 자체가 잘못됨 — late_chase_risk_score를
# "산출"하는 Python 캐노니컬이 존재하지 않는다(소비하는 도구만 있음). GAS가 유일한
# 산출 경로일 가능성이 높아 삭제 시도하지 않음. migration_action 재검증 필요.
# - F02~F06, F07, F10, F11, F15 (MEDIUM/HIGH priority MIGRATE_*): 전용 parity 테스트
# 인프라(GAS 함수와 동일 입력으로 Python 포트 출력을 대조)가 없는 상태에서 결정론적
# 매매엔진의 가격/수량/정지손실/라우팅 로직을 포팅하는 것은 silent correctness bug
# 위험이 크다고 판단해 이번 세션에서는 착수하지 않았다(advisor 권고에 따른 보류).
# 특히 F11(stop_loss_gate)은 ledger 자체가 "critical path — must match
# validate_stop_loss_policy_v1 spec"로 명시한 항목이다. 후속 전용 스프린트에서
# parity 테스트를 먼저 구축한 뒤 착수해야 한다.
# Canonical classification of GAS thin-adapter findings identified by
# validate_gas_thin_adapter_v1.py. Each finding is classified by what type
# of logic it contains and paired with a migration_action.
@@ -21,7 +38,8 @@ findings:
classification: score_logic
migration_action: REGISTER_SP_TAKE_PROFIT
target_file: formulas/score_thresholds_v1.py
status: TODO
status: DONE
resolved_2026_06_21: "이미 spec/calibration_registry.yaml에 id=SP_TAKE_PROFIT(gs_location=gas_data_feed.gs:186, 'P5-T01 wave1'에서 등록)으로 등록되어 있음을 재확인. 별도 formulas/score_thresholds_v1.py 신규 작성 불필요 — 레저 상태만 stale했음."
- id: F02
file: src/gas_adapter_parts/gdf_01_price_metrics.gs
@@ -95,7 +113,8 @@ findings:
classification: score_logic
migration_action: REGISTER_TAKE_PROFIT_BASE
target_file: formulas/score_thresholds_v1.py
status: TODO
status: DONE
resolved_2026_06_21: "이미 spec/calibration_registry.yaml에 id=TAKE_PROFIT_BASE(gs_location=gas_data_feed.gs:2164)로 등록되어 있음을 재확인. F01과 동일 사유로 레저 상태만 stale했음."
- id: F10
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
@@ -124,6 +143,14 @@ findings:
target_file: formulas/distribution_risk_v1.py
status: TODO
notes: Python canonical (build_distribution_risk_v1.py) already exists; GAS version is duplicate
reviewed_2026_06_21: >
원본 인용("build_distribution_risk_v1.py")은 존재하지 않는 파일이다 — 실제로는
tools/build_distribution_risk_score_v2.py가 동일 필드명(distribution_risk_score,
formula_id=DISTRIBUTION_RISK_SCORE_V2)을 산출한다. 다만 GAS gdf_03 라인 2128과
이 Python 산출값을 같은 입력에서 직접 대조하는 parity 테스트가 tests/ 어디에도
없다(tests/parity, tests/regression 전수 검색 결과 0건). "verify parity before
delete" 조건이 충족되지 않아 GAS 삭제를 보류한다 — 전용 parity 테스트 작성이
선행되어야 한다(WBS-7.3 후속 스프린트).
- id: F13
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
@@ -133,6 +160,7 @@ findings:
migration_action: DELETE_DISTRIBUTION_RISK_GAS
status: TODO
notes: formula_id tag stays with Python canonical; remove from GAS
reviewed_2026_06_21: "F12와 동일 사유로 보류 — parity 테스트 선행 필요."
- id: F14
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
@@ -143,6 +171,15 @@ findings:
target_file: formulas/late_chase_risk_v1.py
status: TODO
notes: Python canonical (build_alpha_lead_table_v1.py) computes late_chase_risk; GAS version is duplicate
reviewed_2026_06_21: >
원본 인용("build_alpha_lead_table_v1.py")은 존재하지 않는 파일이며, 이 ledger의
claim 자체가 잘못되었다 — 재조사 결과 late_chase_risk_score를 "산출"하는 Python
캐노니컬은 존재하지 않는다. tools/build_late_chase_attribution_v1.py는 이 필드를
입력에서 "소비"만 할 뿐(r.get("late_chase_risk_score")) 직접 계산하지 않으며,
build_anti_late_chase_v5/v6.py도 별도 산출 로직이다. 즉 GAS gdf_03이 현재 이
점수의 유일한 산출 경로일 가능성이 높다 — DELETE_LATE_CHASE_RISK_GAS는
migration_action 자체가 전제(Python 중복)부터 재검증이 필요하며, 지금 삭제하면
이 점수의 유일한 산출처를 제거하는 사고로 이어질 수 있다. 삭제 금지, 후속 조사 필요.
- id: F15
file: src/gas_adapter_parts/gdf_04_execution_quality.gs
-28
View File
@@ -1,28 +0,0 @@
meta:
title: "은퇴자산포트폴리오 — 리스크 정책 호환 인덱스 (redirect-only)"
parent_file: "RetirementAssetPortfolio.yaml"
version: "2026-05-17-phase3_redirect_clarified"
language: "ko-KR"
timezone: "Asia/Seoul"
role: "deprecated_redirect"
warning: >
이 파일은 경로 호환성 유지 전용입니다. 새 규칙·임계값 추가 금지.
실제 리스크 규칙은 아래 canonical_split_files를 직접 참조하십시오.
canonical_split_files:
portfolio_exposure_framework: "spec/risk/portfolio_exposure.yaml"
risk_control: "spec/risk/risk_control.yaml"
quality_control: "spec/risk/quality_control.yaml"
legacy_path_aliases:
"spec/03_risk_policy.yaml:portfolio_exposure_framework": "spec/risk/portfolio_exposure.yaml:portfolio_exposure_framework"
"spec/03_risk_policy.yaml:risk_control": "spec/risk/risk_control.yaml:risk_control"
"spec/03_risk_policy.yaml:quality_control": "spec/risk/quality_control.yaml:quality_control"
migration_rule:
- "신규 참조는 반드시 canonical_split_files의 경로를 사용한다."
- "기존 문서/예시에서 legacy path가 남아 있으면 alias로 해석하되, 수정 시 새 경로로 교체한다."
- "이 파일에는 수치 임계값을 추가하지 않는다."
validation:
- "python tools/validate_specs.py"
-32
View File
@@ -1,32 +0,0 @@
meta:
title: "은퇴자산포트폴리오 — 전략 규칙 호환 인덱스 (redirect-only)"
parent_file: "RetirementAssetPortfolio.yaml"
version: "2026-05-17-phase3_redirect_clarified"
language: "ko-KR"
timezone: "Asia/Seoul"
role: "deprecated_redirect"
warning: >
이 파일은 경로 호환성 유지 전용입니다. 새 규칙·임계값 추가 금지.
실제 전략 규칙은 아래 canonical_split_files를 직접 참조하십시오.
canonical_split_files:
sector_model: "spec/strategy/sector_model.yaml"
entry_timing_guardrails: "spec/strategy/entry_gates.yaml"
anti_late_trade_rule: "spec/strategy/entry_gates.yaml"
stock_model: "spec/strategy/stock_model.yaml"
rebalancing_trigger: "spec/strategy/rebalancing_trigger.yaml"
legacy_path_aliases:
"spec/04_strategy_rules.yaml:sector_model": "spec/strategy/sector_model.yaml:sector_model"
"spec/04_strategy_rules.yaml:entry_timing_guardrails": "spec/strategy/entry_gates.yaml:entry_timing_guardrails"
"spec/04_strategy_rules.yaml:anti_late_trade_rule": "spec/strategy/entry_gates.yaml:anti_late_trade_rule"
"spec/04_strategy_rules.yaml:stock_model": "spec/strategy/stock_model.yaml:stock_model"
"spec/04_strategy_rules.yaml:rebalancing_trigger": "spec/strategy/rebalancing_trigger.yaml:rebalancing_trigger"
migration_rule:
- "신규 참조는 반드시 canonical_split_files의 경로를 사용한다."
- "기존 문서/예시에서 legacy path가 남아 있으면 alias로 해석하되, 수정 시 새 경로로 교체한다."
- "이 파일에는 수치 임계값을 추가하지 않는다."
validation:
- "python tools/validate_specs.py"
+2
View File
@@ -5,6 +5,8 @@ meta:
language: "ko-KR"
timezone: "Asia/Seoul"
role: "compatibility_index"
has_code_implementation: false
redirect_only: true
purpose: "기존 spec/06_exit_policy.yaml 경로를 보존하기 위한 인덱스 파일."
canonical_split_files:
+19 -4
View File
@@ -119,6 +119,11 @@ formula_registry:
- CONSECUTIVE_STREAK_V1
- BREAKOUT_FAILURE_STOP_V1
- TREND_FILTER_GATE_V1
- SHORT_INTEREST_RISK_GAUGE_V1
- QUALITATIVE_SELL_STRATEGY_V1
- MARKET_REGIME_CLASSIFIER_V1
- SATELLITE_CANDIDATE_SCORE_V1
- MICROSTRUCTURE_PRESSURE_FROM_ORDERBOOK_V1
implementation_map:
REGIME_CONDITIONAL_MACRO_FACTOR_V1: tools/build_predictive_alpha_dialectic_engine_v2.py:NF1
REBOUND_CAPTURE_THESIS_FACTOR_V1: tools/build_predictive_alpha_dialectic_engine_v2.py:NF2
@@ -165,6 +170,11 @@ formula_registry:
CONSECUTIVE_STREAK_V1: tools/build_consecutive_streak_v1.py
BREAKOUT_FAILURE_STOP_V1: tools/build_breakout_failure_stop_v1.py
TREND_FILTER_GATE_V1: tools/build_trend_filter_gate_v1.py
SHORT_INTEREST_RISK_GAUGE_V1: src/quant_engine/qualitative_sell_strategy_v1.py:compute_short_interest_composite
QUALITATIVE_SELL_STRATEGY_V1: src/quant_engine/qualitative_sell_strategy_v1.py:compute_qualitative_sell_strategy
MARKET_REGIME_CLASSIFIER_V1: src/quant_engine/qualitative_sell_strategy_v1.py:classify_market_regime
SATELLITE_CANDIDATE_SCORE_V1: src/quant_engine/qualitative_sell_strategy_v1.py:compute_satellite_candidate_score
MICROSTRUCTURE_PRESSURE_FROM_ORDERBOOK_V1: src/quant_engine/qualitative_sell_strategy_v1.py:compute_microstructure_pressure_from_orderbook
formulas:
FLOW_CREDIT_V1:
owner: engine_owner
@@ -1209,8 +1219,11 @@ formula_registry:
/ 4) * (-1.5)
'
without_20d_fallback: 'frg_5d_sh < -500000 # 절대값 기준 임시 적용 OR flow_credit
< 0.30
without_20d_fallback: 'avg_volume_5d IS NOT NULL AND frg_5d_sh < -1.5 * avg_volume_5d
OR flow_credit < 0.30 # 2026-06-21 WBS-7.5: 절대값(-500000) 폐기, avg_volume_5d
비례식으로 교체. 1.5배수는 with_20d 분기와 동일 계수 재사용(추정 아님).
calibration_ref: spec/calibration_registry.yaml:OVERHANG_PRESSURE_V1_FALLBACK_MULT (EXPERT_PRIOR)
avg_volume_5d 결측 시 이 항목은 false로 처리(추정 금지, missing_policy 참조)
'
volume_weakness: volume < avg_volume_5d * 0.80
@@ -1231,8 +1244,10 @@ formula_registry:
status: PASS
missing_policy:
frg_5d_sh: W2 DATA_MISSING. 레이더 결과 무효.
avg_volume_5d: volume_weakness=false 처리 (보수적)
frg_20d_sh: DATA_MISSING 시 fallback 기준 적용
avg_volume_5d: volume_weakness=false 처리 (보수적). frg_20d_sh도 없는 경우
selling_acceleration의 without_20d_fallback 비례식도 계산 불가하므로
동일하게 false 처리(추정 금지) — flow_credit < 0.30만 단독 평가.
frg_20d_sh: DATA_MISSING 시 fallback(avg_volume_5d 비례식, 2026-06-21 WBS-7.5) 기준 적용
cross_alert:
rule: W1_DIVERGENCE_ALERT + W2_OVERHANG_ALERT 동시 → CRITICAL_ALERT 상향
output_tag: '[W1+W2_CRITICAL_ALERT]'
+2
View File
@@ -5,6 +5,8 @@ meta:
language: "ko-KR"
timezone: "Asia/Seoul"
role: "canonical"
has_code_implementation: true
code_path: "src/quant_engine/snapshot_admin_store_v1.py"
purpose: >
이미지 캡처로 제공되는 계좌·잔고·현금 데이터를 구조화하는 계약.
HTS 입력 가능 주문수량은 이 계약을 통과한 account_snapshot 없이는 산출 금지.
+2
View File
@@ -5,6 +5,8 @@ meta:
language: "ko-KR"
timezone: "Asia/Seoul"
role: "canonical"
has_code_implementation: true
code_path: "src/quant_engine/snapshot_admin_store_v1.py"
purpose: >
Google Sheets 'settings' 탭의 구조를 정의한다.
GAS 함수 readSettingsTab_()이 이 탭을 읽어 파라미터를 공급한다.
+2
View File
@@ -2,6 +2,8 @@ meta:
title: "은퇴자산포트폴리오 — 결정론적 실행 하네스 계약 (QEH)"
parent_file: "RetirementAssetPortfolio.yaml"
version: "2026-05-23-QEH-V5.0-PROPOSAL46"
has_code_implementation: true
code_path: "tools/validate_harness_context.py"
purpose: >
LLM의 자의적 해석 및 주관적 계산을 원천 배제하고, 전문사(Analyst, Trader, Quant) 수준의
정밀한 판단을 강제하기 위한 결정론적 하네스(Deterministic Harness)의
+113 -1
View File
@@ -1,6 +1,8 @@
schema_version: release_dag.v3
step_count: 99
goal: Linearize package.json scripts into a validated DAG execution graph.
has_code_implementation: true
code_path: "tools/run_release_dag_v3.py"
execution_order:
# 토폴로지 정렬 기준 병렬 실행 wave (의존성 없는 노드들을 동시에 실행 가능)
wave_0:
@@ -86,6 +88,11 @@ execution_order:
wave_6:
- build_algorithm_guidance_proof
- build_artifact_chain_hash
- build_calibration_priority
- build_calibration_change_ledger
- build_calibration_review_report
- build_calibration_approval_list
- build_calibration_decision_draft
- build_alpha_feedback_loop
- build_honest_proof_gap_analyzer
- build_operational_alpha_calibration
@@ -220,6 +227,66 @@ dag:
artifact_policy: "keep"
note: "WBS-4.3 alpha feedback loop — non-blocking diagnostic"
build_calibration_priority:
id: build_calibration_priority
command: ["python", "tools/build_calibration_priority_v1.py"]
inputs: ["tools/build_calibration_priority_v1.py", "Temp/alpha_feedback_loop_v2.json", "spec/calibration_registry.yaml"]
outputs: ["Temp/calibration_priority_v1.json"]
depends_on: ["build_alpha_feedback_loop"]
timeout_sec: 30
cache_key: "build_calibration_priority_v1"
strict: false
artifact_policy: "keep"
note: "CALIBRATION_PRIORITY_V1 — registry warning fallback 포함 보정 우선순위 리포트"
build_calibration_change_ledger:
id: build_calibration_change_ledger
command: ["python", "tools/build_calibration_change_ledger_v4.py"]
inputs: ["tools/build_calibration_change_ledger_v4.py", "Temp/calibration_priority_v1.json", "Temp/outcome_ledger_v1.json", "Temp/calibration_registry_v1.json"]
outputs: ["Temp/calibration_change_ledger_v4.json"]
depends_on: ["build_calibration_priority", "build_realized_performance"]
timeout_sec: 30
cache_key: "build_calibration_change_ledger_v4"
strict: false
artifact_policy: "keep"
note: "CALIBRATION_CHANGE_LEDGER_V4 — change ledger linkage 유지"
build_calibration_review_report:
id: build_calibration_review_report
command: ["python", "tools/build_calibration_review_report_v1.py"]
inputs: ["tools/build_calibration_review_report_v1.py", "Temp/calibration_priority_v1.json", "Temp/calibration_change_ledger_v4.json", "spec/calibration_registry.yaml"]
outputs: ["Temp/calibration_review_report_v1.json", "Temp/calibration_review_report_v1.md"]
depends_on: ["build_calibration_change_ledger"]
timeout_sec: 30
cache_key: "build_calibration_review_report_v1"
strict: false
artifact_policy: "keep"
note: "CALIBRATION_REVIEW_REPORT_V1 — 월간 운영용 읽기 쉬운 보정 리포트"
build_calibration_approval_list:
id: build_calibration_approval_list
command: ["python", "tools/build_calibration_approval_list_v1.py"]
inputs: ["tools/build_calibration_approval_list_v1.py", "Temp/calibration_review_report_v1.json"]
outputs: ["Temp/calibration_approval_list_v1.json", "Temp/calibration_approval_list_v1.md"]
depends_on: ["build_calibration_review_report"]
timeout_sec: 30
cache_key: "build_calibration_approval_list_v1"
strict: false
artifact_policy: "keep"
note: "CALIBRATION_APPROVAL_LIST_V1 — PROVISIONAL 승인/검토 분리"
build_calibration_decision_draft:
id: build_calibration_decision_draft
command: ["python", "tools/build_calibration_decision_draft_v1.py"]
inputs: ["tools/build_calibration_decision_draft_v1.py", "Temp/calibration_review_report_v1.json", "Temp/calibration_approval_list_v1.json"]
outputs: ["Temp/calibration_decision_draft_v1.json", "Temp/calibration_decision_draft_v1.md"]
depends_on: ["build_calibration_approval_list"]
timeout_sec: 30
cache_key: "build_calibration_decision_draft_v1"
strict: false
artifact_policy: "keep"
note: "CALIBRATION_DECISION_DRAFT_V1 — APPROVE/HOLD/REJECT 초안"
build_operational_alpha_calibration:
id: build_operational_alpha_calibration
command: ["python", "tools/build_operational_alpha_calibration_v2.py"]
@@ -496,6 +563,20 @@ dag:
strict: true
artifact_policy: "keep"
validate_no_direct_api_trading:
id: validate_no_direct_api_trading
command: ["python", "tools/validate_no_direct_api_trading_v1.py"]
inputs: ["tools/validate_no_direct_api_trading_v1.py", "src/quant_engine/kis_api_client_v1.py", "governance/rules/06_no_direct_api_trading.yaml"]
outputs: []
depends_on: []
timeout_sec: 30
cache_key: "validate_no_direct_api_trading_v1"
strict: true
artifact_policy: "keep"
note: "[CRITICAL] 매수/매도 API 직접 실행 절대 금지 게이트 — warn_only 불가, 완화 대상
아님(사용자 직접 지시 2026-06-21). 순수 stdlib만 사용해 Synology ARMv7 CI에서도
항상 실행 가능."
validate_active_manifest:
id: validate_active_manifest
command: ["python", "tools/validate_active_manifest.py", "--manifest", "runtime/active_artifact_manifest.yaml", "--strict"]
@@ -731,6 +812,37 @@ dag:
artifact_policy: "keep"
note: "섹터 유니버스 월간 갱신 provenance 검증 (warn_only) — GAS 재다운로드 시 Source_URL 소실이 정상. 월간 --apply 실행 후 PASS/WARN 달성. FAIL=비차단 경고만."
build_qualitative_sell_inputs:
id: build_qualitative_sell_inputs
command: ["python", "tools/build_qualitative_sell_inputs_v1.py", "--batch", "--workbook", "GatherTradingData.xlsx", "--apply"]
inputs: ["tools/build_qualitative_sell_inputs_v1.py", "tools/build_macro_context_from_workbook_v1.py", "tools/fetch_naver_market_data_v1.py", "src/quant_engine/kis_api_client_v1.py", "GatherTradingData.xlsx"]
outputs: ["outputs/qualitative_sell_strategy/*.json"]
depends_on: []
timeout_sec: 120
cache_key: "build_qualitative_sell_inputs_v1"
strict: false
warn_only: true
artifact_policy: "keep"
note: "Naver 시세/수급 실시간 스크래핑 의존(warn_only) — 보유종목별 비기계적 매도전략
confluence 판단. 공매도잔고율은 --short-csv 수동 주입 전까지 구조적으로
DATA_MISSING(추정 금지) — 정상 동작. 호가10단계·공매도거래비중은 --kis-account
{real,mock} 옵션으로 KIS Open API(read-only) 조회 가능(2026-06-21 연동) — DAG
기본 실행에는 미포함(자격증명 의존, 수동 실행 시에만 부여)."
build_satellite_candidate_recommendations:
id: build_satellite_candidate_recommendations
command: ["python", "tools/build_satellite_candidate_recommendations_v1.py", "--workbook", "GatherTradingData.xlsx", "--apply"]
inputs: ["tools/build_satellite_candidate_recommendations_v1.py", "tools/fetch_naver_market_data_v1.py", "GatherTradingData.xlsx"]
outputs: ["outputs/qualitative_sell_strategy/satellite_recommendations.json"]
depends_on: []
timeout_sec: 180
cache_key: "build_satellite_candidate_recommendations_v1"
strict: false
warn_only: true
artifact_policy: "keep"
note: "universe 시트 미보유 후보(60종) 전체 Naver 시세 조회 — warn_only. --trade-csv
없으면 sector_export_trend 전부 DATA_MISSING(정상, 추정 금지)."
validate_cash_ledger:
id: validate_cash_ledger
command: ["python", "tools/validate_cash_ledger_v2.py", "--snapshot", "GatherTradingData.json", "--contract", "spec/15_account_snapshot_contract.yaml"]
@@ -1327,7 +1439,7 @@ dag:
command: ["python", "tools/prepare_upload_zip.py", "--skip-validate", "--skip-convert", "--validation-mode", "package-only"]
inputs: ["tools/prepare_upload_zip.py"]
outputs: []
depends_on: ["audit_entropy", "validate_specs", "validate_active_manifest", "validate_report_sync", "validate_report_numeric_consistency", "validate_field_dict", "validate_provenance", "validate_low_capability", "validate_golden_coverage", "validate_calibration", "validate_schema_model", "validate_gas_adapter", "validate_agents_shrink", "validate_no_replay_live_mix", "validate_prediction_accuracy_harness", "validate_alpha_feedback_loop", "validate_operational_alpha_calibration", "validate_realized_performance", "validate_data_gated_progress", "validate_sector_flow_history_progress", "validate_runtime_source_whitelist", "validate_cash_ledger", "validate_factor_lifecycle", "validate_factor_lifecycle_completeness", "validate_metric_alias_collision", "validate_architecture_boundaries", "validate_module_io_coverage", "validate_artifact_chain_hash", "validate_artifact_sync", "validate_renderer_no_calc", "validate_packaged_refs", "validate_property_invariants", "validate_anti_late_entry", "validate_rule_lifecycle", "validate_change_requests", "validate_completion_harness_instructions", "validate_engine_health_card", "validate_llm_regression", "validate_llm_copy_only", "build_final_decision", "build_final_context", "build_provenance_ledger", "build_live_replay_separation", "build_late_chase_attribution", "build_profit_giveback_ratchet", "build_shadow_ledger", "build_operating_cadence_signal", "build_engine_health_card", "build_module_io_coverage", "build_artifact_chain_hash", "build_report", "build_bundle", "build_schema_models", "build_architecture_boundaries", "validate_decision_trace", "validate_factor_conflicts", "validate_no_lookahead", "validate_execution_sim", "validate_render_diff", "build_shadow_promotion", "validate_llm_determinism", "build_time_stop_forecast", "validate_live_activation", "build_rebalance_sheet", "build_prediction_accuracy_harness", "build_alpha_feedback_loop", "build_operational_alpha_calibration", "build_sector_flow_history_progress"]
depends_on: ["audit_entropy", "validate_specs", "validate_no_direct_api_trading", "validate_active_manifest", "validate_report_sync", "validate_report_numeric_consistency", "validate_field_dict", "validate_provenance", "validate_low_capability", "validate_golden_coverage", "validate_calibration", "validate_schema_model", "validate_gas_adapter", "validate_agents_shrink", "validate_no_replay_live_mix", "validate_prediction_accuracy_harness", "validate_alpha_feedback_loop", "validate_operational_alpha_calibration", "validate_realized_performance", "validate_data_gated_progress", "validate_sector_flow_history_progress", "validate_runtime_source_whitelist", "validate_cash_ledger", "validate_factor_lifecycle", "validate_factor_lifecycle_completeness", "validate_metric_alias_collision", "validate_architecture_boundaries", "validate_module_io_coverage", "validate_artifact_chain_hash", "validate_artifact_sync", "validate_renderer_no_calc", "validate_packaged_refs", "validate_property_invariants", "validate_anti_late_entry", "validate_rule_lifecycle", "validate_change_requests", "validate_completion_harness_instructions", "validate_engine_health_card", "validate_llm_regression", "validate_llm_copy_only", "build_final_decision", "build_final_context", "build_provenance_ledger", "build_live_replay_separation", "build_late_chase_attribution", "build_profit_giveback_ratchet", "build_shadow_ledger", "build_operating_cadence_signal", "build_engine_health_card", "build_module_io_coverage", "build_artifact_chain_hash", "build_report", "build_bundle", "build_schema_models", "build_architecture_boundaries", "validate_decision_trace", "validate_factor_conflicts", "validate_no_lookahead", "validate_execution_sim", "validate_render_diff", "build_shadow_promotion", "validate_llm_determinism", "build_time_stop_forecast", "validate_live_activation", "build_rebalance_sheet", "build_prediction_accuracy_harness", "build_alpha_feedback_loop", "build_calibration_priority", "build_calibration_change_ledger", "build_calibration_review_report", "build_calibration_approval_list", "build_calibration_decision_draft", "build_operational_alpha_calibration", "build_sector_flow_history_progress"]
timeout_sec: 60
cache_key: "prepare_zip_v1"
strict: true
@@ -2,6 +2,8 @@ schema_version: execution_simulator_contract.v1
contract_id: H004_EXECUTION_SIMULATOR
harness_file: tools/validate_execution_simulator_v1.py
authority: spec/55_execution_simulator_contract.yaml
has_code_implementation: true
code_path: "tools/validate_execution_simulator_v1.py"
created_at: '2026-06-10T23:29:00+09:00'
purpose: >
틱 정규화, 최소주문수량, 예수금, D+2 현금, 슬리피지 적용 후
+16 -70
View File
@@ -1,80 +1,26 @@
meta:
title: "은퇴자산포트폴리오 — 경로 alias registry"
version: "2026-05-15-F10_fragmentation_guard"
version: "2026-06-21-WBS7.4_migration_closed"
role: "governance"
purpose: "legacy path와 canonical split path를 명시해 참조 혼선을 방지한다."
aliases:
"spec/03_risk_policy.yaml:portfolio_exposure_framework":
canonical: "spec/risk/portfolio_exposure.yaml:portfolio_exposure_framework"
status: "deprecated"
remove_after: "2026-06-30"
"spec/03_risk_policy.yaml:risk_control":
canonical: "spec/risk/aggregate_risk.yaml:risk_control"
status: "deprecated"
remove_after: "2026-06-30"
"spec/risk/risk_control.yaml:risk_control.aggregate_risk_cap":
canonical: "spec/risk/aggregate_risk.yaml:risk_control.aggregate_risk_cap"
status: "deprecated"
remove_after: "2026-06-30"
"spec/risk/risk_control.yaml:risk_control.market_risk_score_based_cash":
canonical: "spec/risk/market_risk_cash.yaml:risk_control.market_risk_score_based_cash"
status: "deprecated"
remove_after: "2026-06-30"
"spec/risk/risk_control.yaml:risk_control.weekly_circuit_breaker":
canonical: "spec/risk/circuit_breakers.yaml:risk_control.weekly_circuit_breaker"
status: "deprecated"
remove_after: "2026-06-30"
"spec/06_exit_policy.yaml:stop_loss":
canonical: "spec/exit/stop_loss.yaml:stop_loss"
status: "deprecated"
remove_after: "2026-06-30"
"spec/06_exit_policy.yaml:take_profit":
canonical: "spec/exit/take_profit.yaml:take_profit"
status: "deprecated"
remove_after: "2026-06-30"
"spec/03_risk_policy.yaml:quality_control":
canonical: "spec/risk/quality_control.yaml:quality_control"
status: "deprecated"
remove_after: "2026-06-30"
"spec/04_strategy_rules.yaml:sector_model":
canonical: "spec/strategy/sector_model.yaml:sector_model"
status: "deprecated"
remove_after: "2026-06-30"
"spec/04_strategy_rules.yaml:entry_timing_guardrails":
canonical: "spec/strategy/entry_core.yaml:entry_timing_guardrails"
status: "deprecated"
remove_after: "2026-06-30"
"spec/04_strategy_rules.yaml:anti_late_trade_rule":
canonical: "spec/strategy/discovery.yaml:anti_late_trade_rule"
status: "deprecated"
remove_after: "2026-06-30"
"spec/strategy/entry_gates.yaml:entry_timing_guardrails.daily_leader_scan":
canonical: "spec/strategy/leader_scan.yaml:entry_timing_guardrails.daily_leader_scan"
status: "deprecated"
remove_after: "2026-06-30"
"spec/strategy/entry_gates.yaml:entry_timing_guardrails.anti_climax_buy_gate":
canonical: "spec/strategy/leader_scan.yaml:entry_timing_guardrails.anti_climax_buy_gate"
status: "deprecated"
remove_after: "2026-06-30"
"spec/strategy/entry_gates.yaml:entry_timing_guardrails.staged_entry_v2":
canonical: "spec/strategy/staged_entry.yaml:entry_timing_guardrails.staged_entry_v2"
status: "deprecated"
remove_after: "2026-06-30"
"spec/strategy/entry_gates.yaml:entry_timing_guardrails.pullback_reentry_rule":
canonical: "spec/strategy/staged_entry.yaml:entry_timing_guardrails.pullback_reentry_rule"
status: "deprecated"
remove_after: "2026-06-30"
"spec/04_strategy_rules.yaml:stock_model":
canonical: "spec/strategy/stock_model.yaml:stock_model"
status: "deprecated"
remove_after: "2026-06-30"
"spec/04_strategy_rules.yaml:rebalancing_trigger":
canonical: "spec/strategy/rebalancing_trigger.yaml:rebalancing_trigger"
status: "deprecated"
remove_after: "2026-06-30"
# 2026-06-21 WBS-7.4 마이그레이션 종결 기록:
# 아래 17개 alias는 모두 remove_after=2026-06-30 만료 예정이었다.
# repo 전체(spec/src/tools/prompts/examples) grep으로 활성 참조가 0건임을 확인했고,
# 모든 canonical_split_files 대상 파일이 이미 실콘텐츠를 보유하고 있어 마이그레이션이
# 완료된 것으로 판정, 데드라인 전에 alias 항목을 제거했다.
#
# [2026-06-22 WBS-7.11 정정] 작성 당시 이 주석은 호환 인덱스 5개 중 "3개가
# deprecated_redirect라 삭제 보류 중"이라고 적었으나 부정확했다. 실제로는
# spec/06_exit_policy.yaml도 role: compatibility_index(영구 유지 설계)였고,
# role: deprecated_redirect는 spec/03_risk_policy.yaml, spec/04_strategy_rules.yaml
# 2개뿐이었다. WBS-7.11에서 이 2개의 활성 참조 0건을 재확인 후 실삭제했고,
# spec/06_exit_policy.yaml/spec/risk/risk_control.yaml/spec/strategy/entry_gates.yaml
# 3개는 has_code_implementation:false + redirect_only:true로 태깅해 영구 유지한다.
aliases: {}
policy:
- "신규 문서는 canonical 경로만 사용한다."
- "compatibility index와 aliases.yaml 내부의 deprecated 경로는 허용한다."
- "remove_after 이후 deprecated 경로가 active 문서에 남으면 검증 실패로 전환한다."
- "alias 항목을 등록할 때는 반드시 remove_after 데드라인을 두고, 데드라인 전에 활성 참조 0건을 확인한 뒤 제거한다(2026-06-21 사례 참조)."
+3 -10
View File
@@ -82,16 +82,9 @@ ownership_map:
must_not_own: ["투자 규칙 수치"]
# ── 호환 인덱스 (redirect-only, 실제 규칙은 canonical_split_files 참조) ──
"spec/03_risk_policy.yaml":
role: "compatibility_index"
owns: ["legacy path alias for spec/risk/*.yaml"]
must_not_own: ["수치 임계값", "새 리스크 규칙"]
canonical_files: ["spec/risk/portfolio_exposure.yaml", "spec/risk/risk_control.yaml", "spec/risk/quality_control.yaml"]
"spec/04_strategy_rules.yaml":
role: "compatibility_index"
owns: ["legacy path alias for spec/strategy/*.yaml"]
must_not_own: ["수치 임계값", "새 전략 규칙"]
canonical_files: ["spec/strategy/sector_model.yaml", "spec/strategy/entry_gates.yaml", "spec/strategy/stock_model.yaml", "spec/strategy/rebalancing_trigger.yaml"]
# 2026-06-22 WBS-7.11: spec/03_risk_policy.yaml, spec/04_strategy_rules.yaml은
# role: deprecated_redirect(영구 유지가 아닌 완전 폐기 대상)였으며 활성 참조 0건을
# 확인 후 실삭제했다. 캐노니컬 split 파일들은 영향 없이 그대로 유지된다.
"spec/06_exit_policy.yaml":
role: "compatibility_index"
owns: ["legacy path alias for spec/exit/*.yaml"]
+3 -2
View File
@@ -1,6 +1,7 @@
# Risk Spec Split Plan
`spec/03_risk_policy.yaml` is now a compatibility index.
`spec/03_risk_policy.yaml` was a deprecated_redirect-only stub and has been deleted
(2026-06-22, WBS-7.11 — zero active references confirmed before removal).
The canonical risk rules are the split files in this directory.
Canonical split files:
@@ -14,7 +15,7 @@ Canonical split files:
Migration rule:
- Do not add numeric thresholds to `spec/03_risk_policy.yaml` or `spec/risk/risk_control.yaml`.
- Do not add numeric thresholds to `spec/risk/risk_control.yaml` (compatibility index only).
- Keep old paths valid only through compatibility indexes and `spec/aliases.yaml`.
- New documents must reference canonical split files directly.
- `spec/00_execution_contract.yaml` remains higher authority than all risk split files.
+1 -1
View File
@@ -1,6 +1,6 @@
meta:
title: "은퇴자산포트폴리오 — 포트폴리오 노출·현금 정책 분할 후보"
parent_file: "spec/03_risk_policy.yaml"
parent_file: "RetirementAssetPortfolio.yaml" # 2026-06-22 WBS-7.11: spec/03_risk_policy.yaml 삭제로 갱신
version: "2026-05-16-F9_secular_leader"
language: "ko-KR"
timezone: "Asia/Seoul"
+1 -1
View File
@@ -1,6 +1,6 @@
meta:
title: "은퇴자산포트폴리오 — 포트폴리오 노출·현금 정책 분할 후보"
parent_file: "spec/03_risk_policy.yaml"
parent_file: "RetirementAssetPortfolio.yaml" # 2026-06-22 WBS-7.11: spec/03_risk_policy.yaml 삭제로 갱신
version: "2026-05-18-F10_score_clamp_d2_fix"
language: "ko-KR"
timezone: "Asia/Seoul"
+1 -1
View File
@@ -1,6 +1,6 @@
meta:
title: "은퇴자산포트폴리오 — 리스크 품질관리 분할 후보"
parent_file: "spec/03_risk_policy.yaml"
parent_file: "RetirementAssetPortfolio.yaml" # 2026-06-22 WBS-7.11: spec/03_risk_policy.yaml 삭제로 갱신
version: "2026-05-15-F8_split"
language: "ko-KR"
timezone: "Asia/Seoul"
+3 -1
View File
@@ -1,10 +1,12 @@
meta:
title: "은퇴자산포트폴리오 — 리스크 제어 호환 인덱스"
parent_file: "spec/03_risk_policy.yaml"
parent_file: "RetirementAssetPortfolio.yaml" # 2026-06-22 WBS-7.11: spec/03_risk_policy.yaml 삭제로 갱신
version: "2026-05-15-F12_index_only"
language: "ko-KR"
timezone: "Asia/Seoul"
role: "compatibility_index"
has_code_implementation: false
redirect_only: true
purpose: "기존 risk_control 경로를 보존하기 위한 인덱스 파일."
canonical_split_files:
+3 -2
View File
@@ -1,6 +1,7 @@
# Strategy Spec Split Plan
`spec/04_strategy_rules.yaml` is now a compatibility index.
`spec/04_strategy_rules.yaml` was a deprecated_redirect-only stub and has been deleted
(2026-06-22, WBS-7.11 — zero active references confirmed before removal).
The canonical strategy rules are the split files in this directory.
Canonical split files:
@@ -17,5 +18,5 @@ Canonical split files:
Migration rule:
- Do not duplicate thresholds without `canonical_ref`.
- Keep old paths valid through `spec/04_strategy_rules.yaml.legacy_path_aliases`.
- Keep old paths valid through `spec/strategy/entry_gates.yaml.legacy_path_aliases` (compatibility index only).
- `spec/09_decision_flow.yaml` controls execution order; strategy split files only define domain logic.
+3 -1
View File
@@ -1,10 +1,12 @@
meta:
title: "은퇴자산포트폴리오 — 진입 게이트 호환 인덱스"
parent_file: "spec/04_strategy_rules.yaml"
parent_file: "RetirementAssetPortfolio.yaml" # 2026-06-22 WBS-7.11: spec/04_strategy_rules.yaml 삭제로 갱신
version: "2026-05-15-F11_index_only"
language: "ko-KR"
timezone: "Asia/Seoul"
role: "compatibility_index"
has_code_implementation: false
redirect_only: true
purpose: >
기존 spec/strategy/entry_gates.yaml 경로를 보존하기 위한 인덱스 파일.
실제 진입 규칙은 세부 split 파일을 canonical로 사용한다.
+1 -1
View File
@@ -1,6 +1,6 @@
meta:
title: "은퇴자산포트폴리오 — 리밸런싱 트리거 분할 후보"
parent_file: "spec/04_strategy_rules.yaml"
parent_file: "RetirementAssetPortfolio.yaml" # 2026-06-22 WBS-7.11: spec/04_strategy_rules.yaml 삭제로 갱신
version: "2026-05-15-F8_split"
language: "ko-KR"
timezone: "Asia/Seoul"
+1 -1
View File
@@ -1,6 +1,6 @@
meta:
title: "은퇴자산포트폴리오 — 섹터 모델 분할 후보"
parent_file: "spec/04_strategy_rules.yaml"
parent_file: "RetirementAssetPortfolio.yaml" # 2026-06-22 WBS-7.11: spec/04_strategy_rules.yaml 삭제로 갱신
version: "2026-05-15-F8_split"
language: "ko-KR"
timezone: "Asia/Seoul"
+1 -1
View File
@@ -1,6 +1,6 @@
meta:
title: "은퇴자산포트폴리오 — 종목 모델 분할 후보"
parent_file: "spec/04_strategy_rules.yaml"
parent_file: "RetirementAssetPortfolio.yaml" # 2026-06-22 WBS-7.11: spec/04_strategy_rules.yaml 삭제로 갱신
version: "2026-05-16-F10_peg_gate"
language: "ko-KR"
timezone: "Asia/Seoul"
@@ -0,0 +1,69 @@
"""WBS-7.11(2026-06-22) — spec-코드 동기화 게이트 단위 테스트."""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
import tools.validate_specs as vs
def test_real_repo_has_no_missing_code_path():
"""현재 저장소 상태에서 1차 태깅된 파일들은 모두 code_path가 실존해야 한다."""
errors: list[str] = []
result = vs.validate_spec_code_sync(errors)
assert result["gate"] == "PASS"
assert result["missing_code_path_count"] == 0
assert result["checked_count"] >= 10
assert not errors
def test_missing_code_path_fails(tmp_path, monkeypatch):
(tmp_path / "spec").mkdir()
(tmp_path / "governance").mkdir()
(tmp_path / "spec" / "fake_contract.yaml").write_text(
"meta:\n has_code_implementation: true\n code_path: \"tools/does_not_exist_v1.py\"\n",
encoding="utf-8",
)
monkeypatch.setattr(vs, "ROOT", tmp_path)
errors: list[str] = []
result = vs.validate_spec_code_sync(errors)
assert result["gate"] == "FAIL"
assert result["missing_code_path_count"] == 1
assert any("does_not_exist_v1.py" in e for e in errors)
def test_redirect_only_and_has_code_is_contradiction(tmp_path, monkeypatch):
(tmp_path / "spec").mkdir()
(tmp_path / "governance").mkdir()
(tmp_path / "spec" / "contradiction.yaml").write_text(
"meta:\n has_code_implementation: true\n redirect_only: true\n",
encoding="utf-8",
)
monkeypatch.setattr(vs, "ROOT", tmp_path)
errors: list[str] = []
result = vs.validate_spec_code_sync(errors)
assert result["gate"] == "FAIL"
assert any("contradiction" in e for e in errors)
def test_files_without_the_field_are_skipped_not_failed(tmp_path, monkeypatch):
(tmp_path / "spec").mkdir()
(tmp_path / "governance").mkdir()
(tmp_path / "spec" / "untouched.yaml").write_text(
"meta:\n title: legacy doc with no sync field\n",
encoding="utf-8",
)
monkeypatch.setattr(vs, "ROOT", tmp_path)
errors: list[str] = []
result = vs.validate_spec_code_sync(errors)
assert result["gate"] == "PASS"
assert result["checked_count"] == 0
assert result["total_spec_files"] == 1
assert not errors
+62 -4
View File
@@ -117,6 +117,10 @@ def validate_formula_registry(errors: list[str]) -> None:
"ALPHA_FEEDBACK_LOOP_V2", "ALPHA_LEAD_THRESHOLD_OPTIMIZER_V1",
# ENGINE_AUDIT — Python-tool-only 감사 게이트 (GAS 런타임 비개입)
"IMPUTED_DATA_EXPOSURE_GATE_V1",
# Phase-8 비기계적 매도전략 — confluence 기반 판단 게이트 (output_contract 구조)
"SHORT_INTEREST_RISK_GAUGE_V1", "QUALITATIVE_SELL_STRATEGY_V1",
"MARKET_REGIME_CLASSIFIER_V1", "SATELLITE_CANDIDATE_SCORE_V1",
"MICROSTRUCTURE_PRESSURE_FROM_ORDERBOOK_V1",
}
for formula_id, formula in all_formulas.items():
if not isinstance(formula, dict):
@@ -619,6 +623,62 @@ def validate_harness_contract_consistency(errors: list[str]) -> None:
fail(errors, f"harness_contract collection_key not checked in validator: {key}")
def validate_spec_code_sync(errors: list[str]) -> dict:
"""WBS-7.11(2026-06-22) — spec YAML이 code_path로 가리키는 파일이 실제로 존재하는지 검사.
has_code_implementation 필드가 있는 파일만 검사한다(점진적 롤아웃 — 필드가 없는
파일은 스킵되므로 1차 태깅이 기존 PASS 상태를 절대 깨지 않는다). redirect_only:true인
파일은 의도적으로 코드가 없는 순수 호환 인덱스이므로 code_path 검사 대상이 아니며,
has_code_implementation:true와 동시에 있으면 그 자체로 모순이라 fail한다.
"""
all_yaml_paths = sorted((ROOT / "spec").rglob("*.yaml")) + sorted((ROOT / "governance").rglob("*.yaml"))
total_files = len(all_yaml_paths)
checked = 0
missing = 0
for path in all_yaml_paths:
try:
data = yaml.safe_load(path.read_text(encoding="utf-8"))
except Exception:
continue
if not isinstance(data, dict):
continue
meta = data.get("meta") if isinstance(data.get("meta"), dict) else data
has_code = meta.get("has_code_implementation")
if has_code is None:
continue
redirect_only = bool(meta.get("redirect_only"))
checked += 1
if redirect_only and has_code:
fail(errors, f"spec_code_sync contradiction: {path} has redirect_only=true AND has_code_implementation=true")
missing += 1
continue
if not has_code:
continue
code_path = meta.get("code_path")
candidates = code_path if isinstance(code_path, list) else [code_path] if code_path else []
if not candidates:
fail(errors, f"spec_code_sync: {path} declares has_code_implementation=true but no code_path")
missing += 1
continue
for rel in candidates:
if not (ROOT / str(rel)).exists():
fail(errors, f"spec declares code_path that does not exist: {path} -> {rel}")
missing += 1
result = {
"formula_id": "SPEC_CODE_SYNC_V1",
"total_spec_files": total_files,
"checked_count": checked,
"missing_code_path_count": missing,
"sync_field_coverage_pct": round(100.0 * checked / total_files, 2) if total_files else 0.0,
"gate": "PASS" if missing == 0 else "FAIL",
}
out = ROOT / "Temp" / "spec_code_sync_v1.json"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
return result
def main() -> int:
errors: list[str] = []
@@ -660,10 +720,9 @@ def main() -> int:
manifest_text = (ROOT / "RetirementAssetPortfolio.yaml").read_text(encoding="utf-8")
for path in sorted((ROOT / "spec").rglob("*.yaml")):
rel = path.relative_to(ROOT).as_posix()
if rel not in manifest_text and rel not in {"spec/03_risk_policy.yaml", "spec/04_strategy_rules.yaml"}:
if rel not in manifest_text:
fail(errors, f"spec file not registered in manifest: {rel}")
if path.stat().st_size > MAX_SPEC_BYTES and path.name not in {
"03_risk_policy.yaml", "04_strategy_rules.yaml",
"13_formula_registry.yaml", "13b_harness_formulas.yaml",
"12_field_dictionary.yaml",
"51_formula_lifecycle_registry.yaml", # 290+ formula lifecycle registry (Proposal51-P1)
@@ -770,13 +829,12 @@ def main() -> int:
validate_formula_registry(errors)
validate_output_rendering_contract(schema, errors)
validate_harness_contract_consistency(errors)
validate_spec_code_sync(errors)
aliases = load_yaml(ROOT / "spec" / "aliases.yaml", errors) or {}
alias_map = aliases.get("aliases") or {}
alias_files = {
ROOT / "spec" / "aliases.yaml",
ROOT / "spec" / "03_risk_policy.yaml",
ROOT / "spec" / "04_strategy_rules.yaml",
ROOT / "spec" / "06_exit_policy.yaml",
ROOT / "spec" / "risk" / "risk_control.yaml",
ROOT / "spec" / "strategy" / "entry_gates.yaml",