Files
company-haness/.claude/hooks/compile_orgos_registry.py
T

302 lines
13 KiB
Python

#!/usr/bin/env python3
"""Compile human-owned Pack and contract sources into runtime registries and architecture docs.
Generated output lives under ``org-os/generated`` and must not be edited manually.
``--check`` fails when any generated file drifts from its sources.
"""
from __future__ import annotations
import hashlib
import os
import sys
from typing import Any
import yaml
ROOT = os.environ.get("CLAUDE_PROJECT_DIR") or os.path.dirname(
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)
PACK_INDEX = os.path.join(ROOT, "org-os", "packs", "pack-index.yaml")
REG = os.path.join(ROOT, "org-os", "00-role-registry")
WORK = os.path.join(ROOT, "org-os", "06-agent-work")
OUT = os.path.join(ROOT, "org-os", "generated")
def load(path: str) -> dict[str, Any]:
return yaml.safe_load(open(path, encoding="utf-8")) or {}
def sha(path: str) -> str:
digest = hashlib.sha256()
with open(path, "rb") as handle:
for chunk in iter(lambda: handle.read(65536), b""):
digest.update(chunk)
return digest.hexdigest()
def method_sources() -> list[str]:
directory = os.path.join(REG, "role-working-methods")
index = os.path.join(directory, "index.yaml")
includes = (load(index).get("role-method-contracts") or {}).get("includes", []) or []
return [index] + [os.path.join(directory, value) for value in includes]
def architecture_views(packs: dict[str, Any], source_hashes: dict[str, str]) -> dict[str, str]:
"""Build four generated views without duplicating the exact YAML registries."""
source_fingerprint = hashlib.sha256(
"\n".join(f"{name}:{digest}" for name, digest in sorted(source_hashes.items())).encode()
).hexdigest()
generated_header = (
"# generated by .claude/hooks/compile_orgos_registry.py — do not edit\n"
f"# source-sha256: {source_fingerprint}"
)
static_rows = [
generated_header,
"direction: right",
'sources: "Human-owned contracts" {',
' roles: "roles + profiles"',
' families: "family candidate pools"',
' packs: "pack index"',
' methods: "working methods"',
' artifacts: "artifact contracts"',
"}",
'compiler: "Org OS compiler"',
'generated: "Generated registries + architecture views"',
'runtime: "Common execution kernel" {',
' intake: "intake classifier"',
' planner: "role / budget planner"',
' context: "context package binder"',
' state: "event + state services"',
' observer: "usage observer"',
"}",
"sources -> compiler -> generated -> runtime",
'domain_packs: "Domain packs" {',
]
for pack_name, definition in packs["packs"].items():
node = "pack_" + pack_name.replace("-", "_")
label = f"{pack_name} [{definition['plane']}]\\n{len(definition.get('family-ids', []))} families"
static_rows.append(f' {node}: "{label}"')
static_rows += ["}", "domain_packs -> sources.packs", "runtime.planner -> domain_packs", ""]
runtime = generated_header + """
direction: right
request: "Request"
intake: "Deterministic intake\\nlight | substantial | strategic"
planner: "Minimum-sufficient role planner\\ncoverage + budget + independence"
package: "Immutable context package\\nrole + tools + paths + SHA"
agent: "Concrete role agent"
projection: "Projection-first report"
review: "Independent reviewer"
ledger: "Append-only event / usage ledgers"
request -> intake -> planner -> package -> agent -> projection
projection -> review: "when required"
intake -> ledger
planner -> ledger
package -> ledger
agent -> ledger
review -> ledger
"""
authority = generated_header + """
direction: down
control_plane: "Control plane" {
intake: "classify"
planner: "select / budget"
guard: "bind tools + paths"
state: "record truth"
}
decision_plane: "Decision plane" {
decider: "concrete decision role"
authority: "approve one-way-door decisions"
}
delivery_plane: "Design + delivery planes" {
producer: "concrete producer role"
artifact: "versioned artifact"
}
assurance_plane: "Assurance plane" {
reviewer: "different concrete reviewer role"
verdict: "evidence-backed verdict"
}
control_plane.planner -> delivery_plane.producer: "assign"
control_plane.guard -> delivery_plane.producer: "constrain"
delivery_plane.artifact -> assurance_plane.reviewer: "review"
assurance_plane.verdict -> decision_plane.decider: "escalate if authority needed"
decision_plane.authority -> control_plane.state: "immutable decision event"
"""
events = generated_header + """
direction: right
commands: "Commands" {
selection: "SelectionPlanCreated"
spawn: "SpawnBindingPending / Claimed"
artifact: "ArtifactSubmitted / Reviewed"
decision: "DecisionRecorded"
}
event_store: "Append-only event store"
materializer: "Deterministic materializer"
views: "Materialized views" {
workflow: "workflow state"
registry: "subagent registry"
usage: "token + context metrics"
}
rehydration: "Tiered rehydration\\nprojection -> evidence index -> source"
commands -> event_store -> materializer -> views -> rehydration
event_store -> materializer: "replay"
"""
return {
"static-components.d2": "\n".join(static_rows),
"runtime-sequence.d2": runtime,
"authority-swimlane.d2": authority,
"event-model.d2": events,
}
def compile_outputs() -> dict[str, str]:
packs = load(PACK_INDEX)["org-os-packs"]
roles_path = os.path.join(REG, "roles.yaml")
profiles_path = os.path.join(REG, "role-profiles.yaml")
families_path = os.path.join(REG, "capability-families.yaml")
artifacts_path = os.path.join(WORK, "generated", "artifact-registry.yaml")
contracts_path = os.path.join(WORK, "workflow-contracts.yaml")
roles = load(roles_path)["role-registry"]
profiles = load(profiles_path)["role-profiles"]
family_doc = load(families_path)["capability-families"]
artifacts = load(artifacts_path)["artifact-registry"]
contracts = load(contracts_path)["workflow-contracts"]
families = {item["family-id"]: item for item in family_doc["families"]}
role_map = {item["role-id"]: item for item in roles["roles"]}
profile_map = {item["role-id"]: item for item in profiles["profiles"]}
ownership: dict[str, dict[str, str]] = {}
for pack_name, definition in packs["packs"].items():
for family_id in definition.get("family-ids", []) or []:
if family_id in ownership:
raise ValueError(f"family belongs to multiple packs: {family_id}")
if family_id not in families:
raise ValueError(f"pack references unknown family: {family_id}")
ownership[family_id] = {"pack": pack_name, "plane": definition["plane"]}
missing = sorted(set(families) - set(ownership))
if missing:
raise ValueError(f"families missing from pack index: {missing}")
source_paths = [PACK_INDEX, roles_path, profiles_path, families_path, artifacts_path, contracts_path] + method_sources()
source_hashes = {os.path.relpath(path, ROOT): sha(path) for path in source_paths}
compiled_families = []
bound_roles = set()
for family_id, family in families.items():
entry = dict(family)
entry.update(ownership[family_id])
entry["execution-identity"] = "concrete-role-only"
entry["agent-card"] = None
compiled_families.append(entry)
for role_id in entry.get("member-role-ids", []) or []:
if role_id in bound_roles:
raise ValueError(f"role belongs to multiple families: {role_id}")
if role_id not in role_map or role_id not in profile_map:
raise ValueError(f"family role lacks role/profile: {role_id}")
bound_roles.add(role_id)
if bound_roles != set(role_map):
raise ValueError(f"family coverage mismatch: {sorted(set(role_map) ^ bound_roles)}")
role_entries = []
family_by_role = {role_id: family_id for family_id, family in families.items()
for role_id in family.get("member-role-ids", []) or []}
for role_id, role in role_map.items():
family_id = family_by_role[role_id]
role_entries.append({
**role,
"family-id": family_id,
"pack": ownership[family_id]["pack"],
"plane": ownership[family_id]["plane"],
"agent-card": f".claude/agents/{role_id.lower()}.md",
})
methods = {}
for path in method_sources()[1:]:
methods.update(load(path).get("role-working-methods", {}) or {})
if set(methods) != set(role_map):
raise ValueError("method registry must cover every concrete role exactly once")
commands = {}
stages = {}
for workflow_name, workflow in (contracts.get("workflows") or {}).items():
stages[workflow_name] = list((workflow.get("stages") or {}).keys())
for stage, definition in (workflow.get("stages") or {}).items():
command = definition.get("command") if isinstance(definition, dict) else None
if command:
commands.setdefault(command, []).append({"workflow": workflow_name, "stage": stage})
generated = {
"role-registry.yaml": {
"generated-role-registry": {"version": 1, "generated-by": ".claude/hooks/compile_orgos_registry.py",
"source-sha256": source_hashes, "role-count": len(role_entries), "roles": role_entries}},
"family-registry.yaml": {
"generated-family-registry": {"version": 1, "generated-by": ".claude/hooks/compile_orgos_registry.py",
"source-sha256": source_hashes, "family-count": len(compiled_families),
"families": compiled_families}},
"method-registry.yaml": {
"generated-method-registry": {"version": 1, "generated-by": ".claude/hooks/compile_orgos_registry.py",
"source-sha256": source_hashes, "role-count": len(methods), "roles": methods}},
"architecture-index.yaml": {
"architecture-index": {"version": 1, "generated-by": ".claude/hooks/compile_orgos_registry.py",
"source-sha256": source_hashes,
"counts": {"roles": len(role_entries), "families": len(compiled_families),
"agent-cards": len(role_entries),
"artifact-kinds": int(artifacts.get("artifact-kind-count") or len(artifacts.get("artifact-kinds", {}))),
"packs": len(packs["packs"]), "planes": len(packs["planes"])},
"packs": packs["packs"], "workflow-stages": stages, "command-map": commands}},
}
outputs = {name: yaml.safe_dump(doc, sort_keys=False, allow_unicode=True)
for name, doc in generated.items()}
index = generated["architecture-index.yaml"]["architecture-index"]
rows = ["# Generated architecture index", "", "이 파일은 `compile_orgos_registry.py`가 생성합니다. 수기 수정 금지.", "",
"| Registry | Count |", "|---|---:|",
f"| Concrete roles / agent cards | {index['counts']['roles']} |",
f"| Family metadata pools | {index['counts']['families']} |",
f"| Domain packs | {index['counts']['packs']} |",
f"| Responsibility planes | {index['counts']['planes']} |",
f"| Artifact kinds | {index['counts']['artifact-kinds']} |", "",
"## Packs", "", "| Pack | Plane | Families |", "|---|---|---|"]
for pack_name, definition in packs["packs"].items():
rows.append(f"| {pack_name} | {definition['plane']} | {', '.join(definition['family-ids'])} |")
rows += [
"", "## Architecture views", "",
"- `static-components.d2` — source, compiler, kernel, Pack 정적 구성",
"- `runtime-sequence.d2` — intake부터 projection/review까지의 실행 흐름",
"- `authority-swimlane.d2` — control/decision/delivery/assurance 권한 경계",
"- `event-model.d2` — append-only event와 materialized view 관계",
"", "Family는 actor가 아니며 `.claude/agents`에는 concrete role card만 생성됩니다.", "",
]
outputs["README.generated.md"] = "\n".join(rows)
outputs.update(architecture_views(packs, source_hashes))
return outputs
def main() -> int:
check = "--check" in sys.argv[1:]
try:
outputs = compile_outputs()
except Exception as exc:
sys.stderr.write(f"[orgos-registry] compile failed: {exc}\n")
return 2
drift = []
for name, expected in outputs.items():
path = os.path.join(OUT, name)
actual = open(path, encoding="utf-8").read() if os.path.exists(path) else None
if actual != expected:
drift.append(name)
if not check:
os.makedirs(OUT, exist_ok=True)
with open(path, "w", encoding="utf-8") as handle:
handle.write(expected)
if check and drift:
sys.stderr.write(f"[orgos-registry] generated drift: {', '.join(drift)}\n")
return 2
print(
"OK orgos registry: 4 registries + generated README + 4 architecture views "
f"({'checked' if check else 'written'})"
)
return 0
if __name__ == "__main__":
sys.exit(main())