149 lines
7.8 KiB
Python
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)
|