init: company-haness 설계
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user