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" data_gaps_roadmap: "spec/16_data_gaps_roadmap.yaml"
performance_contract: "spec/17_performance_contract.yaml" performance_contract: "spec/17_performance_contract.yaml"
settings_contract: "spec/18_settings_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" risk_control_index: "spec/risk/risk_control.yaml"
aggregate_risk: "spec/risk/aggregate_risk.yaml" aggregate_risk: "spec/risk/aggregate_risk.yaml"
circuit_breakers: "spec/risk/circuit_breakers.yaml" circuit_breakers: "spec/risk/circuit_breakers.yaml"
@@ -254,7 +253,6 @@ spec_files:
portfolio_exposure: "spec/risk/portfolio_exposure.yaml" portfolio_exposure: "spec/risk/portfolio_exposure.yaml"
risk_quality_control: "spec/risk/quality_control.yaml" risk_quality_control: "spec/risk/quality_control.yaml"
factor_risk: "spec/risk/factor_risk.yaml" factor_risk: "spec/risk/factor_risk.yaml"
strategy_rules_index: "spec/04_strategy_rules.yaml"
sector_model: "spec/strategy/sector_model.yaml" sector_model: "spec/strategy/sector_model.yaml"
entry_gates_index: "spec/strategy/entry_gates.yaml" entry_gates_index: "spec/strategy/entry_gates.yaml"
entry_core: "spec/strategy/entry_core.yaml" entry_core: "spec/strategy/entry_core.yaml"
@@ -297,6 +295,7 @@ spec_files:
event_response: "spec/exit/event_response.yaml" event_response: "spec/exit/event_response.yaml"
position_review: "spec/exit/position_review.yaml" position_review: "spec/exit/position_review.yaml"
dynamic_value_preservation_sell_v3: "spec/exit/dynamic_value_preservation_sell_v3.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" output_schema: "spec/07_output_schema.yaml"
machine_output_schema: "schemas/output_schema.json" machine_output_schema: "schemas/output_schema.json"
report_templates: "RetirementAssetPortfolioReportTemplate.yaml" report_templates: "RetirementAssetPortfolioReportTemplate.yaml"
@@ -337,6 +336,7 @@ spec_files:
- "spec/exit/event_response.yaml" - "spec/exit/event_response.yaml"
- "spec/exit/position_review.yaml" - "spec/exit/position_review.yaml"
- "spec/exit/dynamic_value_preservation_sell_v3.yaml" - "spec/exit/dynamic_value_preservation_sell_v3.yaml"
- "spec/exit/qualitative_sell_strategy_v1.yaml"
strategy: strategy:
- "spec/strategy/sector_model.yaml" - "spec/strategy/sector_model.yaml"
- "spec/strategy/entry_gates.yaml" - "spec/strategy/entry_gates.yaml"
+39 -2
View File
@@ -10,6 +10,23 @@ classification_summary:
display_text: 1 display_text: 1
unclassified_findings: 0 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 # Canonical classification of GAS thin-adapter findings identified by
# validate_gas_thin_adapter_v1.py. Each finding is classified by what type # validate_gas_thin_adapter_v1.py. Each finding is classified by what type
# of logic it contains and paired with a migration_action. # of logic it contains and paired with a migration_action.
@@ -21,7 +38,8 @@ findings:
classification: score_logic classification: score_logic
migration_action: REGISTER_SP_TAKE_PROFIT migration_action: REGISTER_SP_TAKE_PROFIT
target_file: formulas/score_thresholds_v1.py 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 - id: F02
file: src/gas_adapter_parts/gdf_01_price_metrics.gs file: src/gas_adapter_parts/gdf_01_price_metrics.gs
@@ -95,7 +113,8 @@ findings:
classification: score_logic classification: score_logic
migration_action: REGISTER_TAKE_PROFIT_BASE migration_action: REGISTER_TAKE_PROFIT_BASE
target_file: formulas/score_thresholds_v1.py 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 - id: F10
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
@@ -124,6 +143,14 @@ findings:
target_file: formulas/distribution_risk_v1.py target_file: formulas/distribution_risk_v1.py
status: TODO status: TODO
notes: Python canonical (build_distribution_risk_v1.py) already exists; GAS version is duplicate 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 - id: F13
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
@@ -133,6 +160,7 @@ findings:
migration_action: DELETE_DISTRIBUTION_RISK_GAS migration_action: DELETE_DISTRIBUTION_RISK_GAS
status: TODO status: TODO
notes: formula_id tag stays with Python canonical; remove from GAS notes: formula_id tag stays with Python canonical; remove from GAS
reviewed_2026_06_21: "F12와 동일 사유로 보류 — parity 테스트 선행 필요."
- id: F14 - id: F14
file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs file: src/gas_adapter_parts/gdf_03_portfolio_gates.gs
@@ -143,6 +171,15 @@ findings:
target_file: formulas/late_chase_risk_v1.py target_file: formulas/late_chase_risk_v1.py
status: TODO status: TODO
notes: Python canonical (build_alpha_lead_table_v1.py) computes late_chase_risk; GAS version is duplicate 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 - id: F15
file: src/gas_adapter_parts/gdf_04_execution_quality.gs 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" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
role: "compatibility_index" role: "compatibility_index"
has_code_implementation: false
redirect_only: true
purpose: "기존 spec/06_exit_policy.yaml 경로를 보존하기 위한 인덱스 파일." purpose: "기존 spec/06_exit_policy.yaml 경로를 보존하기 위한 인덱스 파일."
canonical_split_files: canonical_split_files:
+19 -4
View File
@@ -119,6 +119,11 @@ formula_registry:
- CONSECUTIVE_STREAK_V1 - CONSECUTIVE_STREAK_V1
- BREAKOUT_FAILURE_STOP_V1 - BREAKOUT_FAILURE_STOP_V1
- TREND_FILTER_GATE_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: implementation_map:
REGIME_CONDITIONAL_MACRO_FACTOR_V1: tools/build_predictive_alpha_dialectic_engine_v2.py:NF1 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 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 CONSECUTIVE_STREAK_V1: tools/build_consecutive_streak_v1.py
BREAKOUT_FAILURE_STOP_V1: tools/build_breakout_failure_stop_v1.py BREAKOUT_FAILURE_STOP_V1: tools/build_breakout_failure_stop_v1.py
TREND_FILTER_GATE_V1: tools/build_trend_filter_gate_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: formulas:
FLOW_CREDIT_V1: FLOW_CREDIT_V1:
owner: engine_owner owner: engine_owner
@@ -1209,8 +1219,11 @@ formula_registry:
/ 4) * (-1.5) / 4) * (-1.5)
' '
without_20d_fallback: 'frg_5d_sh < -500000 # 절대값 기준 임시 적용 OR flow_credit without_20d_fallback: 'avg_volume_5d IS NOT NULL AND frg_5d_sh < -1.5 * avg_volume_5d
< 0.30 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 volume_weakness: volume < avg_volume_5d * 0.80
@@ -1231,8 +1244,10 @@ formula_registry:
status: PASS status: PASS
missing_policy: missing_policy:
frg_5d_sh: W2 DATA_MISSING. 레이더 결과 무효. frg_5d_sh: W2 DATA_MISSING. 레이더 결과 무효.
avg_volume_5d: volume_weakness=false 처리 (보수적) avg_volume_5d: volume_weakness=false 처리 (보수적). frg_20d_sh도 없는 경우
frg_20d_sh: DATA_MISSING 시 fallback 기준 적용 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: cross_alert:
rule: W1_DIVERGENCE_ALERT + W2_OVERHANG_ALERT 동시 → CRITICAL_ALERT 상향 rule: W1_DIVERGENCE_ALERT + W2_OVERHANG_ALERT 동시 → CRITICAL_ALERT 상향
output_tag: '[W1+W2_CRITICAL_ALERT]' output_tag: '[W1+W2_CRITICAL_ALERT]'
+2
View File
@@ -5,6 +5,8 @@ meta:
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
role: "canonical" role: "canonical"
has_code_implementation: true
code_path: "src/quant_engine/snapshot_admin_store_v1.py"
purpose: > purpose: >
이미지 캡처로 제공되는 계좌·잔고·현금 데이터를 구조화하는 계약. 이미지 캡처로 제공되는 계좌·잔고·현금 데이터를 구조화하는 계약.
HTS 입력 가능 주문수량은 이 계약을 통과한 account_snapshot 없이는 산출 금지. HTS 입력 가능 주문수량은 이 계약을 통과한 account_snapshot 없이는 산출 금지.
+2
View File
@@ -5,6 +5,8 @@ meta:
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
role: "canonical" role: "canonical"
has_code_implementation: true
code_path: "src/quant_engine/snapshot_admin_store_v1.py"
purpose: > purpose: >
Google Sheets 'settings' 탭의 구조를 정의한다. Google Sheets 'settings' 탭의 구조를 정의한다.
GAS 함수 readSettingsTab_()이 이 탭을 읽어 파라미터를 공급한다. GAS 함수 readSettingsTab_()이 이 탭을 읽어 파라미터를 공급한다.
+2
View File
@@ -2,6 +2,8 @@ meta:
title: "은퇴자산포트폴리오 — 결정론적 실행 하네스 계약 (QEH)" title: "은퇴자산포트폴리오 — 결정론적 실행 하네스 계약 (QEH)"
parent_file: "RetirementAssetPortfolio.yaml" parent_file: "RetirementAssetPortfolio.yaml"
version: "2026-05-23-QEH-V5.0-PROPOSAL46" version: "2026-05-23-QEH-V5.0-PROPOSAL46"
has_code_implementation: true
code_path: "tools/validate_harness_context.py"
purpose: > purpose: >
LLM의 자의적 해석 및 주관적 계산을 원천 배제하고, 전문사(Analyst, Trader, Quant) 수준의 LLM의 자의적 해석 및 주관적 계산을 원천 배제하고, 전문사(Analyst, Trader, Quant) 수준의
정밀한 판단을 강제하기 위한 결정론적 하네스(Deterministic Harness)의 정밀한 판단을 강제하기 위한 결정론적 하네스(Deterministic Harness)의
+113 -1
View File
@@ -1,6 +1,8 @@
schema_version: release_dag.v3 schema_version: release_dag.v3
step_count: 99 step_count: 99
goal: Linearize package.json scripts into a validated DAG execution graph. 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: execution_order:
# 토폴로지 정렬 기준 병렬 실행 wave (의존성 없는 노드들을 동시에 실행 가능) # 토폴로지 정렬 기준 병렬 실행 wave (의존성 없는 노드들을 동시에 실행 가능)
wave_0: wave_0:
@@ -86,6 +88,11 @@ execution_order:
wave_6: wave_6:
- build_algorithm_guidance_proof - build_algorithm_guidance_proof
- build_artifact_chain_hash - 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_alpha_feedback_loop
- build_honest_proof_gap_analyzer - build_honest_proof_gap_analyzer
- build_operational_alpha_calibration - build_operational_alpha_calibration
@@ -220,6 +227,66 @@ dag:
artifact_policy: "keep" artifact_policy: "keep"
note: "WBS-4.3 alpha feedback loop — non-blocking diagnostic" 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: build_operational_alpha_calibration:
id: build_operational_alpha_calibration id: build_operational_alpha_calibration
command: ["python", "tools/build_operational_alpha_calibration_v2.py"] command: ["python", "tools/build_operational_alpha_calibration_v2.py"]
@@ -496,6 +563,20 @@ dag:
strict: true strict: true
artifact_policy: "keep" 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: validate_active_manifest:
id: validate_active_manifest id: validate_active_manifest
command: ["python", "tools/validate_active_manifest.py", "--manifest", "runtime/active_artifact_manifest.yaml", "--strict"] command: ["python", "tools/validate_active_manifest.py", "--manifest", "runtime/active_artifact_manifest.yaml", "--strict"]
@@ -731,6 +812,37 @@ dag:
artifact_policy: "keep" artifact_policy: "keep"
note: "섹터 유니버스 월간 갱신 provenance 검증 (warn_only) — GAS 재다운로드 시 Source_URL 소실이 정상. 월간 --apply 실행 후 PASS/WARN 달성. FAIL=비차단 경고만." 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: validate_cash_ledger:
id: 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"] 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"] command: ["python", "tools/prepare_upload_zip.py", "--skip-validate", "--skip-convert", "--validation-mode", "package-only"]
inputs: ["tools/prepare_upload_zip.py"] inputs: ["tools/prepare_upload_zip.py"]
outputs: [] 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 timeout_sec: 60
cache_key: "prepare_zip_v1" cache_key: "prepare_zip_v1"
strict: true strict: true
@@ -2,6 +2,8 @@ schema_version: execution_simulator_contract.v1
contract_id: H004_EXECUTION_SIMULATOR contract_id: H004_EXECUTION_SIMULATOR
harness_file: tools/validate_execution_simulator_v1.py harness_file: tools/validate_execution_simulator_v1.py
authority: spec/55_execution_simulator_contract.yaml 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' created_at: '2026-06-10T23:29:00+09:00'
purpose: > purpose: >
틱 정규화, 최소주문수량, 예수금, D+2 현금, 슬리피지 적용 후 틱 정규화, 최소주문수량, 예수금, D+2 현금, 슬리피지 적용 후
+16 -70
View File
@@ -1,80 +1,26 @@
meta: meta:
title: "은퇴자산포트폴리오 — 경로 alias registry" title: "은퇴자산포트폴리오 — 경로 alias registry"
version: "2026-05-15-F10_fragmentation_guard" version: "2026-06-21-WBS7.4_migration_closed"
role: "governance" role: "governance"
purpose: "legacy path와 canonical split path를 명시해 참조 혼선을 방지한다." purpose: "legacy path와 canonical split path를 명시해 참조 혼선을 방지한다."
aliases: # 2026-06-21 WBS-7.4 마이그레이션 종결 기록:
"spec/03_risk_policy.yaml:portfolio_exposure_framework": # 아래 17개 alias는 모두 remove_after=2026-06-30 만료 예정이었다.
canonical: "spec/risk/portfolio_exposure.yaml:portfolio_exposure_framework" # repo 전체(spec/src/tools/prompts/examples) grep으로 활성 참조가 0건임을 확인했고,
status: "deprecated" # 모든 canonical_split_files 대상 파일이 이미 실콘텐츠를 보유하고 있어 마이그레이션이
remove_after: "2026-06-30" # 완료된 것으로 판정, 데드라인 전에 alias 항목을 제거했다.
"spec/03_risk_policy.yaml:risk_control": #
canonical: "spec/risk/aggregate_risk.yaml:risk_control" # [2026-06-22 WBS-7.11 정정] 작성 당시 이 주석은 호환 인덱스 5개 중 "3개가
status: "deprecated" # deprecated_redirect라 삭제 보류 중"이라고 적었으나 부정확했다. 실제로는
remove_after: "2026-06-30" # spec/06_exit_policy.yaml도 role: compatibility_index(영구 유지 설계)였고,
"spec/risk/risk_control.yaml:risk_control.aggregate_risk_cap": # role: deprecated_redirect는 spec/03_risk_policy.yaml, spec/04_strategy_rules.yaml
canonical: "spec/risk/aggregate_risk.yaml:risk_control.aggregate_risk_cap" # 2개뿐이었다. WBS-7.11에서 이 2개의 활성 참조 0건을 재확인 후 실삭제했고,
status: "deprecated" # spec/06_exit_policy.yaml/spec/risk/risk_control.yaml/spec/strategy/entry_gates.yaml
remove_after: "2026-06-30" # 3개는 has_code_implementation:false + redirect_only:true로 태깅해 영구 유지한다.
"spec/risk/risk_control.yaml:risk_control.market_risk_score_based_cash": aliases: {}
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"
policy: policy:
- "신규 문서는 canonical 경로만 사용한다." - "신규 문서는 canonical 경로만 사용한다."
- "compatibility index와 aliases.yaml 내부의 deprecated 경로는 허용한다." - "compatibility index와 aliases.yaml 내부의 deprecated 경로는 허용한다."
- "remove_after 이후 deprecated 경로가 active 문서에 남으면 검증 실패로 전환한다." - "remove_after 이후 deprecated 경로가 active 문서에 남으면 검증 실패로 전환한다."
- "alias 항목을 등록할 때는 반드시 remove_after 데드라인을 두고, 데드라인 전에 활성 참조 0건을 확인한 뒤 제거한다(2026-06-21 사례 참조)."
+3 -10
View File
@@ -82,16 +82,9 @@ ownership_map:
must_not_own: ["투자 규칙 수치"] must_not_own: ["투자 규칙 수치"]
# ── 호환 인덱스 (redirect-only, 실제 규칙은 canonical_split_files 참조) ── # ── 호환 인덱스 (redirect-only, 실제 규칙은 canonical_split_files 참조) ──
"spec/03_risk_policy.yaml": # 2026-06-22 WBS-7.11: spec/03_risk_policy.yaml, spec/04_strategy_rules.yaml은
role: "compatibility_index" # role: deprecated_redirect(영구 유지가 아닌 완전 폐기 대상)였으며 활성 참조 0건을
owns: ["legacy path alias for spec/risk/*.yaml"] # 확인 후 실삭제했다. 캐노니컬 split 파일들은 영향 없이 그대로 유지된다.
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"]
"spec/06_exit_policy.yaml": "spec/06_exit_policy.yaml":
role: "compatibility_index" role: "compatibility_index"
owns: ["legacy path alias for spec/exit/*.yaml"] owns: ["legacy path alias for spec/exit/*.yaml"]
+3 -2
View File
@@ -1,6 +1,7 @@
# Risk Spec Split Plan # 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. The canonical risk rules are the split files in this directory.
Canonical split files: Canonical split files:
@@ -14,7 +15,7 @@ Canonical split files:
Migration rule: 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`. - Keep old paths valid only through compatibility indexes and `spec/aliases.yaml`.
- New documents must reference canonical split files directly. - New documents must reference canonical split files directly.
- `spec/00_execution_contract.yaml` remains higher authority than all risk split files. - `spec/00_execution_contract.yaml` remains higher authority than all risk split files.
+1 -1
View File
@@ -1,6 +1,6 @@
meta: meta:
title: "은퇴자산포트폴리오 — 포트폴리오 노출·현금 정책 분할 후보" 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" version: "2026-05-16-F9_secular_leader"
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
+1 -1
View File
@@ -1,6 +1,6 @@
meta: meta:
title: "은퇴자산포트폴리오 — 포트폴리오 노출·현금 정책 분할 후보" 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" version: "2026-05-18-F10_score_clamp_d2_fix"
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
+1 -1
View File
@@ -1,6 +1,6 @@
meta: meta:
title: "은퇴자산포트폴리오 — 리스크 품질관리 분할 후보" 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" version: "2026-05-15-F8_split"
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
+3 -1
View File
@@ -1,10 +1,12 @@
meta: meta:
title: "은퇴자산포트폴리오 — 리스크 제어 호환 인덱스" 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" version: "2026-05-15-F12_index_only"
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
role: "compatibility_index" role: "compatibility_index"
has_code_implementation: false
redirect_only: true
purpose: "기존 risk_control 경로를 보존하기 위한 인덱스 파일." purpose: "기존 risk_control 경로를 보존하기 위한 인덱스 파일."
canonical_split_files: canonical_split_files:
+3 -2
View File
@@ -1,6 +1,7 @@
# Strategy Spec Split Plan # 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. The canonical strategy rules are the split files in this directory.
Canonical split files: Canonical split files:
@@ -17,5 +18,5 @@ Canonical split files:
Migration rule: Migration rule:
- Do not duplicate thresholds without `canonical_ref`. - 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. - `spec/09_decision_flow.yaml` controls execution order; strategy split files only define domain logic.
+3 -1
View File
@@ -1,10 +1,12 @@
meta: meta:
title: "은퇴자산포트폴리오 — 진입 게이트 호환 인덱스" 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" version: "2026-05-15-F11_index_only"
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
role: "compatibility_index" role: "compatibility_index"
has_code_implementation: false
redirect_only: true
purpose: > purpose: >
기존 spec/strategy/entry_gates.yaml 경로를 보존하기 위한 인덱스 파일. 기존 spec/strategy/entry_gates.yaml 경로를 보존하기 위한 인덱스 파일.
실제 진입 규칙은 세부 split 파일을 canonical로 사용한다. 실제 진입 규칙은 세부 split 파일을 canonical로 사용한다.
+1 -1
View File
@@ -1,6 +1,6 @@
meta: meta:
title: "은퇴자산포트폴리오 — 리밸런싱 트리거 분할 후보" 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" version: "2026-05-15-F8_split"
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
+1 -1
View File
@@ -1,6 +1,6 @@
meta: meta:
title: "은퇴자산포트폴리오 — 섹터 모델 분할 후보" 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" version: "2026-05-15-F8_split"
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" timezone: "Asia/Seoul"
+1 -1
View File
@@ -1,6 +1,6 @@
meta: meta:
title: "은퇴자산포트폴리오 — 종목 모델 분할 후보" 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" version: "2026-05-16-F10_peg_gate"
language: "ko-KR" language: "ko-KR"
timezone: "Asia/Seoul" 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", "ALPHA_FEEDBACK_LOOP_V2", "ALPHA_LEAD_THRESHOLD_OPTIMIZER_V1",
# ENGINE_AUDIT — Python-tool-only 감사 게이트 (GAS 런타임 비개입) # ENGINE_AUDIT — Python-tool-only 감사 게이트 (GAS 런타임 비개입)
"IMPUTED_DATA_EXPOSURE_GATE_V1", "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(): for formula_id, formula in all_formulas.items():
if not isinstance(formula, dict): 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}") 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: def main() -> int:
errors: list[str] = [] errors: list[str] = []
@@ -660,10 +720,9 @@ def main() -> int:
manifest_text = (ROOT / "RetirementAssetPortfolio.yaml").read_text(encoding="utf-8") manifest_text = (ROOT / "RetirementAssetPortfolio.yaml").read_text(encoding="utf-8")
for path in sorted((ROOT / "spec").rglob("*.yaml")): for path in sorted((ROOT / "spec").rglob("*.yaml")):
rel = path.relative_to(ROOT).as_posix() 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}") fail(errors, f"spec file not registered in manifest: {rel}")
if path.stat().st_size > MAX_SPEC_BYTES and path.name not in { 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", "13_formula_registry.yaml", "13b_harness_formulas.yaml",
"12_field_dictionary.yaml", "12_field_dictionary.yaml",
"51_formula_lifecycle_registry.yaml", # 290+ formula lifecycle registry (Proposal51-P1) "51_formula_lifecycle_registry.yaml", # 290+ formula lifecycle registry (Proposal51-P1)
@@ -770,13 +829,12 @@ def main() -> int:
validate_formula_registry(errors) validate_formula_registry(errors)
validate_output_rendering_contract(schema, errors) validate_output_rendering_contract(schema, errors)
validate_harness_contract_consistency(errors) validate_harness_contract_consistency(errors)
validate_spec_code_sync(errors)
aliases = load_yaml(ROOT / "spec" / "aliases.yaml", errors) or {} aliases = load_yaml(ROOT / "spec" / "aliases.yaml", errors) or {}
alias_map = aliases.get("aliases") or {} alias_map = aliases.get("aliases") or {}
alias_files = { alias_files = {
ROOT / "spec" / "aliases.yaml", ROOT / "spec" / "aliases.yaml",
ROOT / "spec" / "03_risk_policy.yaml",
ROOT / "spec" / "04_strategy_rules.yaml",
ROOT / "spec" / "06_exit_policy.yaml", ROOT / "spec" / "06_exit_policy.yaml",
ROOT / "spec" / "risk" / "risk_control.yaml", ROOT / "spec" / "risk" / "risk_control.yaml",
ROOT / "spec" / "strategy" / "entry_gates.yaml", ROOT / "spec" / "strategy" / "entry_gates.yaml",