Files

149 lines
7.8 KiB
Python

#!/usr/bin/env python3
"""P3-B 파일럿 e2e — DES-DIRECTOR→DES-PROD→DES-PLATFORM handoff 체인(standalone check).
실제 authored Contract v2(디스크) 3종을 **임시 레지스트리**에 trusted CLI 4단 게이트로 활성화하고
(사람 signoff 는 주입으로 시뮬레이션 — 실제 활성화는 사용자 real signoff 필요), active 상태에서
(1) validate_method_execution / validate_report 의 step-results Hard Fail
(2) spawn/transition handoff Hard Fail(both-active 엣지)
이 실제로 발동하는지 end-to-end 로 검증한다. **프로덕션 레지스트리(org-os/..)는 건드리지 않는다.**
"""
import hashlib
import importlib.util
import os
import sys
import tempfile
import yaml
ROOT = os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
HOOKS = os.path.join(ROOT, ".claude", "hooks")
passed = failed = 0
def check(name, ok):
global passed, failed
if ok:
passed += 1
print(f" ✅ {name}")
else:
failed += 1
print(f" ❌ {name}")
def _load(mod, path):
spec = importlib.util.spec_from_file_location(mod, os.path.join(HOOKS, path))
m = importlib.util.module_from_spec(spec)
if HOOKS not in sys.path:
sys.path.insert(0, HOOKS)
spec.loader.exec_module(m)
return m
mc = _load("method_contracts", "method_contracts.py")
amc = _load("activate_method_contract", "activate_method_contract.py")
vr = _load("validate_report", "validate_report.py")
CHAIN = [("DES-DIRECTOR", "converge-directions"),
("DES-PROD", "post-direction"),
("DES-PLATFORM", "tokenize")]
_rm = mc.load_role_methods()
_yes = lambda w, s: True # noqa: E731 — 사람 signoff 존재 시뮬레이션
_no = lambda e: False # noqa: E731
_ok = lambda e: True # noqa: E731
print("== 파일럿: 실제 계약 3종을 임시 레지스트리에 활성화(4단 게이트) ==")
with tempfile.TemporaryDirectory() as td:
reg = os.path.join(td, "activations.yaml")
hashes = {}
for role, mid in CHAIN:
prof = mc.resolve_method_profile(role, mid)
h = mc.canonical_contract_hash(prof)
hashes[(role, mid)] = h
rep = os.path.join(td, f"{role}.golden.report.yaml")
open(rep, "w").write(f"bottom-line: golden {role}/{mid}\n")
rh = hashlib.sha256(open(rep, "rb").read()).hexdigest()
ok, errs = amc.verify(role, mid, h, rep, rh, "wf-pilot", has_signoff=_yes)
check(f"{role}/{mid} 4-gate verify 통과(real hash+golden+signoff)", ok)
# wrong hash → 거부(리뷰본과 다른 계약 활성화 차단)
okb, _ = amc.verify(role, mid, "0" * 64, rep, rh, "wf-pilot", has_signoff=_yes)
check(f"{role}/{mid} 잘못된 hash → 거부", not okb)
amc.apply_activation(role, mid, {
"status": "active", "contract-sha256": h,
"validation-report": rep, "validation-report-sha256": rh,
"acceptance-workflow": "wf-pilot", "acceptance-stage": amc.signoff_stage(role, mid, h),
}, registry_path=reg)
ACTIVE = yaml.safe_load(open(reg))["method-contract-activations"]["roles"]
check("3 계약 모두 active 기록", all(ACTIVE[r]["methods"][m]["status"] == "active" for r, m in CHAIN))
print("== active: validate_method_execution step-results Hard Fail(실제 계약) ==")
dd_h = hashes[("DES-DIRECTOR", "converge-directions")]
# (1) method-execution 누락 → Hard Fail
errs = mc.validate_method_execution({"role-id": "DES-DIRECTOR", "tier": "standard"}, activations=ACTIVE)
check("method-execution 누락 → Hard Fail", errs != [])
# (2) 정상 실행(모든 step completed+artifact / 대안 3) → 통과
_steps = ["rehydrate-originals", "compare-tradeoffs", "converge-one", "preserve-dissent"]
me_ok = {"role-id": "DES-DIRECTOR", "method-id": "converge-directions", "contract-sha256": dd_h,
"step-results": [{"step-id": s, "status": "completed",
"artifact-refs": [{"report-id": "r", "sha256": "a" * 64}]} for s in _steps],
"decisions": [{"decision-id": "d1", "selected-option-id": "a",
"alternatives": [{"option-id": "a"}, {"option-id": "b"}, {"option-id": "c"}]}]}
check("정상 method-execution → 통과",
mc.validate_method_execution({"role-id": "DES-DIRECTOR", "tier": "standard",
"method-execution": me_ok}, activations=ACTIVE) == [])
# (3) converge-one artifact-ref 누락 → Hard Fail(자기신고 금지)
me_bad = {"role-id": "DES-DIRECTOR", "method-id": "converge-directions", "contract-sha256": dd_h,
"step-results": [{"step-id": s, "status": "completed",
"artifact-refs": ([] if s == "converge-one" else [{"report-id": "r", "sha256": "a" * 64}])}
for s in _steps],
"decisions": me_ok["decisions"]}
check("converge-one artifact-ref 누락 → Hard Fail",
any("converge-one" in e for e in mc.validate_method_execution(
{"role-id": "DES-DIRECTOR", "tier": "standard", "method-execution": me_bad}, activations=ACTIVE)))
# (4) 대안 부족(<3) → Hard Fail
me_alt = {**me_ok, "decisions": [{"decision-id": "d1", "selected-option-id": "a",
"alternatives": [{"option-id": "a"}]}]}
check("대안 부족(<min 3) → Hard Fail",
any("대안" in e for e in mc.validate_method_execution(
{"role-id": "DES-DIRECTOR", "tier": "standard", "method-execution": me_alt}, activations=ACTIVE)))
# (5) draft(=light 시뮬)·light tier → 무강제
check("light tier → 무강제(active여도)",
mc.validate_method_execution({"role-id": "DES-DIRECTOR", "tier": "light"}, activations=ACTIVE) == [])
print("== active: validate_report 통합 Hard Fail ==")
_oa, _om = vr._MC.load_activations, vr._MC.load_role_methods
vr._MC.load_activations = lambda: ACTIVE
vr._MC.load_role_methods = lambda: _rm
try:
errs = vr.validate({"role-id": "DES-DIRECTOR", "tier": "standard",
"report-header": {"bottom-line": "x"}, "method-execution": None})
check("validate_report 가 active 계약 method-execution 누락 Hard Fail",
any("method-execution" in e for e in errs))
finally:
vr._MC.load_activations, vr._MC.load_role_methods = _oa, _om
print("== active: spawn/transition handoff Hard Fail(both-active 엣지) ==")
# DES-PROD/post-direction ← selected-direction(DES-DIRECTOR, 둘 다 active) : 미수락 → 차단
e1, _ = mc.handoff_violations("DES-PROD", "post-direction", present=_no, accepted=_no,
activations=ACTIVE, methods=_rm)
check("DES-PROD/post-direction: selected-direction 미수락 → 차단(both-active hard)", e1 != [])
e1ok, _ = mc.handoff_violations("DES-PROD", "post-direction", present=_ok, accepted=_ok,
activations=ACTIVE, methods=_rm)
check("DES-PROD/post-direction: 충족 → 통과", e1ok == [])
# DES-PLATFORM/tokenize ← design-decision-record(DES-PROD, 둘 다 active) : 미수락 → 차단
e2, _ = mc.handoff_violations("DES-PLATFORM", "tokenize", present=_no, accepted=_no,
activations=ACTIVE, methods=_rm)
check("DES-PLATFORM/tokenize: design-decision-record 미수락 → 차단", e2 != [])
# 한쪽 draft 로 강등 시 soft(debt·비차단) — DES-DIRECTOR 를 draft 로
ACTIVE_SOFT = {**ACTIVE, "DES-DIRECTOR": {"methods": {"converge-directions": {"status": "draft"}}}}
e3, d3 = mc.handoff_violations("DES-PROD", "post-direction", present=_no, accepted=_no,
activations=ACTIVE_SOFT, methods=_rm)
check("producer draft 강등 → 비차단(soft) + debt", e3 == [] and d3 != [])
print(f"\n{passed} passed · {failed} failed")
sys.exit(1 if failed else 0)