init: company-haness 설계

This commit is contained in:
DongHyeonka
2026-07-23 17:49:00 +09:00
parent 57d1bab894
commit f668d6a158
962 changed files with 98989 additions and 1 deletions
+174
View File
@@ -0,0 +1,174 @@
#!/usr/bin/env python3
"""Lens-cap on fan-out width — 2축 모델(lens 다양성 × sub-specialty 커버리지). (권고 #2, 리뷰 #12)
배경(리뷰 finding #12): 이전 버전은 role별이 아니라 family의 carries-lenses를 모든 멤버에
복사했다 → 같은 lens면 무조건 primary 1명만 남기고 나머지 전문분야를 삭제했다
(아키텍트 7명 EA/솔루션/앱/기술/IT/시스템분석/SWAT 전부 LENS-TECH → standard에서 6명 삭제;
PM vs TPO, product vs platform design도 동일 피해). 그러나 lens 다양성과 domain/sub-specialty
커버리지는 서로 다른 축이다.
두 축:
- lens(다양성 바닥, 12 lens): 무엇을 보는가. 절대 병합 금지(lens-registry R1).
- sub-specialty(커버리지, lens-registry sub-specialty-axis): 어떤 전문성인가.
같은 lens라도 서로 다른 sub-specialty(application vs system architecture …)는 중복이 아니다.
위반 판정(tier != heavy):
같은 lens 안에서
(a) 같은/미분화 sub-specialty를 2명 이상 spawn(= 진짜 중복), 또는
(b) distinct sub-specialty 수가 tier 상한 초과(= fan-out 폭 가드)
→ 위반. distinct sub-specialty는 tier 상한까지 허용(전문분야 삭제를 멈춘다).
heavy는 permissive(sub-angle 분화 전면 허용).
role→sub-specialty와 tier 상한은 lens-registry.yaml의 sub-specialty-axis에서 읽는다(SoT).
role-id는 대소문자 무시로 비교한다.
Usage:
lens_cap.py --tier light|standard|heavy --roles r1,r2,r3
exit 0 = 허용, exit 2 = 위반(같은 lens+같은 sub-specialty 중복 또는 tier 상한 초과)
"""
import os
import sys
from collections import defaultdict
import yaml
ROOT = os.environ.get("CLAUDE_PROJECT_DIR") or os.path.dirname(
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)
REG = os.path.join(ROOT, "org-os", "00-role-registry")
FAMILIES = os.path.join(REG, "capability-families.yaml")
LENS_REGISTRY = os.path.join(REG, "lens-registry.yaml")
# lens-registry에 sub-specialty-axis가 없을 때의 안전한 기본값.
DEFAULT_DISTINCT_CAP = {"light": 3, "standard": 8, "heavy": None}
def _norm(rid):
return (rid or "").strip().upper()
def role_lenses():
"""role-id(UPPER) -> [lens-id]. family의 carries-lenses에서 파생."""
fams = yaml.safe_load(open(FAMILIES))["capability-families"]["families"]
m = {}
for fam in fams:
for rid in fam.get("member-role-ids") or []:
m[_norm(rid)] = fam.get("carries-lenses") or []
return m
def load_axis():
"""(caps, role->sub-specialty) 반환. lens-registry.yaml sub-specialty-axis에서 읽는다."""
reg = yaml.safe_load(open(LENS_REGISTRY))["lens-registry"]
axis = reg.get("sub-specialty-axis") or {}
caps = dict(DEFAULT_DISTINCT_CAP)
for tier, v in (axis.get("distinct-sub-specialties-per-lens") or {}).items():
caps[tier] = v
smap = {}
for rid, ss in (axis.get("role-sub-specialty") or {}).items():
if ss is not None:
smap[_norm(rid)] = str(ss).strip().lower()
return caps, smap
def sub_specialty(rid, smap):
"""role의 sub-specialty. 매핑 우선, 없으면 role-id 자체(각 role=고유 전문분야), 빈값이면 미분화."""
n = _norm(rid)
if n in smap:
return smap[n]
if n:
return n.lower()
return "UNDIFFERENTIATED"
def evaluate(tier, roles):
"""(ok, info) — ok=위반 없음. info에 진단 담김."""
rl = role_lenses()
caps, smap = load_axis()
cap = caps.get(tier, DEFAULT_DISTINCT_CAP.get(tier))
unknown = []
# lens -> sub-specialty -> [원본 role 표기]
lens_ss = defaultdict(lambda: defaultdict(list))
for r in roles:
lenses = rl.get(_norm(r))
if lenses is None:
unknown.append(r)
continue # lens 판별 불가 → 어느 lens 그룹에도 안 넣음(위반 대상 아님)
ss = sub_specialty(r, smap)
for lens in lenses:
lens_ss[lens][ss].append(r)
dup = [] # (lens, sub-specialty, [roles]) — 같은 sub-specialty 2+
over = [] # (lens, distinct, cap, [sub-specialties]) — tier 상한 초과
for lens, ssmap in lens_ss.items():
for ss, rs in ssmap.items():
if len(rs) > 1:
dup.append((lens, ss, rs))
distinct = len(ssmap)
if cap is not None and distinct > cap:
over.append((lens, distinct, cap, sorted(ssmap.keys())))
return (not dup and not over), {
"cap": cap,
"unknown": unknown,
"lens_ss": lens_ss,
"dup": dup,
"over": over,
}
def main():
opt = {}
a = sys.argv[1:]
i = 0
while i < len(a):
if a[i].startswith("--"):
opt[a[i][2:]] = a[i + 1] if i + 1 < len(a) else ""
i += 2
else:
i += 1
tier = opt.get("tier", "standard")
roles = [r.strip() for r in (opt.get("roles", "")).split(",") if r.strip()]
if not roles:
sys.stderr.write("usage: lens_cap.py --tier T --roles r1,r2,...\n")
sys.exit(1)
if tier == "heavy":
print(f"[lens_cap] OK (heavy): 같은 렌즈 sub-angle 분화 전면 허용. roles={len(roles)}")
sys.exit(0)
ok, info = evaluate(tier, roles)
if not ok:
sys.stderr.write(
f"[lens_cap] BUDGET(sub-specialty) 위반 (tier={tier}): "
f"같은 렌즈+같은 전문분야 중복 또는 distinct 상한({info['cap']}) 초과.\n"
)
for lens, ss, rs in info["dup"]:
sys.stderr.write(
f" - {lens}: 같은 sub-specialty '{ss}'에 워커 {len(rs)}"
f"({', '.join(rs)}) -> 1명만 두거나 heavy tier로.\n"
)
for lens, distinct, cap, sslist in info["over"]:
sys.stderr.write(
f" - {lens}: distinct sub-specialty {distinct}개 > 상한 {cap} "
f"({', '.join(sslist)}) -> 범위를 좁히거나 heavy tier로.\n"
)
if info["unknown"]:
sys.stderr.write(f" (미확인 role, lens 판별 불가·무시: {', '.join(info['unknown'])})\n")
sys.exit(2)
nlens = len(info["lens_ss"])
ndistinct = sum(len(ssmap) for ssmap in info["lens_ss"].values())
msg = (
f"[lens_cap] OK ({tier}): 진짜 중복 없음, distinct sub-specialty 상한({info['cap']}) 이내. "
f"roles={len(roles)}, lenses={nlens}, sub-specialties={ndistinct}"
)
if info["unknown"]:
msg += f" (미확인 role 무시: {', '.join(info['unknown'])})"
print(msg)
sys.exit(0)
if __name__ == "__main__":
main()