Files
company-haness/.claude/tests/test_p1_build.py
T

92 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""P1-F (#8) tests: implementation loop is a first-class contract + proportional
governance (light path) in build.md, embedded in FAM-ENG-* agent bodies, and a
reusable build-loop skill exists. Standalone; exit 0 = all pass. Does NOT edit or
import test_enforcement.py.
"""
import os
import re
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
AG = os.path.join(ROOT, ".claude", "agents")
passed, failed = 0, 0
def check(name, ok):
global passed, failed
if ok:
passed += 1
print(f" PASS {name}")
else:
failed += 1
print(f" FAIL {name}")
def read(*p):
with open(os.path.join(ROOT, *p), encoding="utf-8") as f:
return f.read()
# 구현 루프의 7단계 마커(순서 무관하게 존재 검증)
LOOP_STEPS = ["inspect", "targeted verify", "broader verify"]
print("== build.md: implementation loop is first-class ==")
_build = read(".claude", "commands", "build.md")
check("build.md has explicit implementation loop steps",
all(s in _build for s in LOOP_STEPS)
and "smallest safe change" in _build.lower()
and "구현 루프" in _build)
check("build.md loop names inspect-own-diff + honest report (verified vs not run)",
"diff" in _build and ("실행하지 않은 것" in _build or "실행하지 않았는지" in _build))
check("build.md references reusable build-loop skill",
"build-loop" in _build)
print("== build.md: proportional governance (light path for simple changes) ==")
# 단순 변경(버그픽스/문서/ops/작은 수정)에 full PRD/API-contract/data-model/threat-model을 일괄 요구하지 않는 경로
check("build.md has an explicit light/simple-change path",
("light path" in _build or "light 경로" in _build)
and "단순 변경" in _build
and "버그픽스" in _build)
check("build.md light path does NOT require full design paperwork blanket",
("불요" in _build or "일괄" in _build)
and "PRD" in _build and "위협모델" in _build)
check("build.md scales paperwork with risk/tier",
("비례" in _build)
and ("risk" in _build.lower() or "위험도" in _build)
and ("tier" in _build.lower()))
check("build.md keeps 'design Accepted before implementation' for substantial work",
("substantial" in _build.lower())
and "Accepted" in _build)
print("== selected concrete ENG agent bodies carry the implementation loop ==")
for fam in ("eng-be", "eng-fe", "eng-desktop"):
body = ""
p = os.path.join(AG, fam + ".md")
if os.path.exists(p):
with open(p, encoding="utf-8") as f:
body = f.read()
check(f"{fam}.md carries implementation-loop steps",
"구현 루프" in body and all(s in body for s in LOOP_STEPS)
and "호출부" in body)
print("== build-loop skill exists with valid frontmatter ==")
_skill_path = os.path.join(ROOT, ".claude", "skills", "build-loop", "SKILL.md")
check("build-loop SKILL.md exists", os.path.exists(_skill_path))
_skill = read(".claude", "skills", "build-loop", "SKILL.md") if os.path.exists(_skill_path) else ""
_fm = re.match(r"^---\n(.*?)\n---\n", _skill, re.DOTALL)
check("build-loop SKILL.md has frontmatter block", bool(_fm))
_fmtext = _fm.group(1) if _fm else ""
check("build-loop frontmatter has name: build-loop",
bool(re.search(r"^name:\s*build-loop\s*$", _fmtext, re.MULTILINE)))
check("build-loop frontmatter has non-empty description",
bool(re.search(r"^description:\s*\S", _fmtext, re.MULTILINE)))
check("build-loop skill body documents the 7-step loop + proportional gate",
all(s in _skill for s in LOOP_STEPS)
and "smallest safe change" in _skill.lower()
and ("proportional" in _skill.lower() or "비례" in _skill))
print(f"\n{passed} passed, {failed} failed")
sys.exit(1 if failed else 0)