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
+1
View File
@@ -0,0 +1 @@
"""Org OS runtime kernel modules."""
+5
View File
@@ -0,0 +1,5 @@
"""Deterministic intake, coverage, role, budget and task-graph planning."""
from .role_selector import select_minimum_sufficient_roles
__all__ = ["select_minimum_sufficient_roles"]
@@ -0,0 +1,67 @@
"""Token estimates and tier limits used by the executable role planner."""
from __future__ import annotations
import os
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.dirname(os.path.dirname(os.path.abspath(__file__)))))
)
KPI_PATH = os.path.join(ROOT, "org-os", "06-agent-work", "agent-operating-kpi.yaml")
TIER_ROLE_LIMITS = {"light": 2, "standard": 5, "heavy": 12}
TIER_ROLE_TOKENS = {
"light": {"input": 9000, "output": 2500},
"standard": {"input": 18000, "output": 4500},
"heavy": {"input": 32000, "output": 8000},
}
ROLE_TYPE_MULTIPLIER = {
"coordinator": 0.65,
"worker": 1.0,
"recommender": 0.9,
"reviewer": 0.85,
"auditor": 0.9,
"decider": 1.05,
}
def workflow_budget(tier: str, override: int | None = None) -> int:
if override is not None:
return max(0, int(override))
try:
data = yaml.safe_load(open(KPI_PATH, encoding="utf-8")) or {}
budgets = data["agent-operating-kpi"]["token-budgets"]["per-wave"]
return int(budgets[tier])
except Exception:
return {"light": 150000, "standard": 500000, "heavy": 2000000}.get(tier, 500000)
def estimate_role(role: dict[str, Any], tier: str, stage: str | None = None) -> dict[str, int]:
base = TIER_ROLE_TOKENS.get(tier, TIER_ROLE_TOKENS["standard"])
multiplier = ROLE_TYPE_MULTIPLIER.get(str(role.get("role-type") or "worker"), 1.0)
if stage in {"verification", "acceptance"}:
multiplier *= 0.8
input_tokens = int(base["input"] * multiplier)
output_tokens = int(base["output"] * multiplier)
return {"input": input_tokens, "output": output_tokens, "total": input_tokens + output_tokens}
def estimate_plan(selected_roles: list[dict[str, Any]], tier: str, stage: str | None = None) -> dict[str, int]:
input_tokens = output_tokens = 0
for role in selected_roles:
estimate = estimate_role(role, tier, stage)
input_tokens += estimate["input"]
output_tokens += estimate["output"]
synthesis = 0 if len(selected_roles) <= 1 else (3500 if tier == "light" else 7000) * (len(selected_roles) - 1)
return {
"input": input_tokens,
"output": output_tokens,
"synthesis": synthesis,
"total": input_tokens + output_tokens + synthesis,
}
def max_selected_roles(tier: str) -> int:
return TIER_ROLE_LIMITS.get(tier, TIER_ROLE_LIMITS["standard"])
@@ -0,0 +1,140 @@
"""Coverage vocabulary derived from role, family, artifact and workflow contracts."""
from __future__ import annotations
import re
from typing import Any
RISK_ROLE_HINTS = {
"security": {"SEC-ENGINEER", "SEC-APPSEC", "SEC-CHAMPION", "SEC-DEVSECOPS"},
"privacy": {"SEC-APPSEC", "GTM-LEGAL"},
"legal": {"GTM-LEGAL"},
"reliability": {"SRE", "INFRA-PLATFORM", "EXEC-VPENG"},
"quality": {"QA", "EXEC-VPENG"},
"financial": {"EXEC-CFO", "GTM-PRICING", "CONSULT-FIN"},
"user-harm": {"QA", "EXEC-CPO", "SEC-APPSEC"},
}
# Workload-profile.required-capabilities is an executable coverage contract.
# Keep the mapping concrete: a family label by itself must not satisfy a role
# specific need such as competitive intelligence.
CAPABILITY_ROLE_HINTS = {
"product": {"EXEC-CPO", "PROD-PM", "PROD-PO", "PROD-TPO", "PROD-PPO"},
"product-delivery": {"PROD-PM", "PROD-PO", "PROD-TPO", "PROD-PPO", "EXEC-VPENG"},
"customer-research": {"UX-RESEARCHER", "DATA-ANALYST"},
"competitive-intelligence": {"GTM-CI"},
"revenue": {"GTM-REVOPS", "GTM-PRICING", "GTM-SALES", "GTM-GROWTHPM"},
"gtm": {"GTM-CI", "GTM-PMM", "GTM-DEMANDGEN", "GTM-SALES", "GTM-REVOPS"},
"finance": {"EXEC-CFO", "CONSULT-FIN", "GTM-PRICING"},
"strategy": {"STR-ANALYST", "CONSULT-STRAT"},
"operations": {"EXEC-COO", "OPS-CH", "OPS-CREW", "CONSULT-OPS"},
"design": {"DES-DIRECTOR", "DES-PROD", "DES-PLATFORM", "DES-INTERNAL", "DES-VISUAL"},
"information-architecture": {"DOC-IA"},
"technical": {"EXEC-CTO", "EXEC-CPTO", "ARCH-TECH", "ARCH-SOLUTION"},
"architecture": {"ARCH-EA", "ARCH-SOLUTION", "ARCH-APP", "ARCH-TECH", "ARCH-SWAT"},
"engineering": {"EXEC-VPENG", "ENG-FE", "ENG-BE", "ENG-SW"},
"frontend": {"ENG-FE", "ENG-FEPLAT", "ENG-FEUX"},
"backend": {"ENG-BE", "ENG-BEGEN", "ENG-PRODSERVER", "ENG-PLATSERVER", "ENG-SW"},
"public-api": {"ARCH-APP", "ARCH-TECH", "ENG-BE", "ENG-PRODSERVER"},
"persistence": {"ARCH-DATA", "DATA-ENGINEER", "ENG-BE"},
"platform": {"INFRA-PLATFORM", "ENG-FEPLAT", "ENG-PLATSERVER", "PROD-PPO"},
"data": {"ARCH-DATA", "DATA-ENGINEER", "DATA-BIGDATA", "DATA-ANALYST"},
"security": {"SEC-ENGINEER", "SEC-APPSEC", "SEC-CHAMPION", "SEC-DEVSECOPS"},
"privacy": {"SEC-APPSEC", "GTM-LEGAL"},
"legal": {"GTM-LEGAL"},
"quality": {"QA", "EXEC-VPENG"},
"kpi-test": {"DATA-ANALYST", "QA"},
"infrastructure": {"INFRA-DEV", "INFRA-PLATFORM", "INFRA-DEVOPS", "SRE"},
"documentation": {"DOC-LEAD", "DOC-WRITER", "DOC-IA", "DOC-VISUAL", "DOC-EDU"},
}
def tokens(value: Any) -> set[str]:
if value is None:
return set()
if isinstance(value, dict):
value = " ".join(f"{key} {item}" for key, item in value.items())
elif isinstance(value, (list, tuple, set)):
value = " ".join(str(item) for item in value)
return {part for part in re.split(r"[^\w]+", str(value).lower(), flags=re.UNICODE)
if len(part) > 1 and part != "_"}
def artifact_maps(artifact_registry: dict[str, Any]) -> tuple[dict[str, set[str]], dict[str, set[str]]]:
producer: dict[str, set[str]] = {}
reviewer: dict[str, set[str]] = {}
for kind, definition in (artifact_registry.get("artifact-kinds") or {}).items():
for role in definition.get("producer-roles", []) or []:
producer.setdefault(str(role), set()).add(str(kind))
capability = definition.get("reviewer-capability")
if capability:
reviewer.setdefault(str(capability), set()).add(str(kind))
return producer, reviewer
def role_coverage(
role: dict[str, Any],
family: dict[str, Any],
profile: dict[str, Any] | None,
artifact_registry: dict[str, Any],
role_capabilities: dict[str, list[str]],
) -> set[str]:
role_id = str(role.get("role-id"))
family_id = str(family.get("family-id"))
producer, reviewer_kinds = artifact_maps(artifact_registry)
coverage = {"owner", f"family:{family_id}", f"owner:{family_id}", f"role:{role_id}"}
coverage |= {f"lens:{lens}" for lens in family.get("carries-lenses", []) or []}
if family.get("audit-capable"):
coverage.add("lens:LENS-CONTRARIAN")
coverage |= {f"artifact:{kind}" for kind in producer.get(role_id, set())}
for capability, roles in role_capabilities.items():
if role_id in set(roles or []):
coverage.add(f"capability:{capability}")
coverage |= {f"review:{kind}" for kind in reviewer_kinds.get(capability, set())}
for capability, role_ids in CAPABILITY_ROLE_HINTS.items():
if role_id in role_ids:
coverage.add(f"capability:{capability}")
if role.get("is-decision-maker"):
coverage.add("authority")
if role.get("role-type") in {"auditor", "reviewer"} or family.get("audit-capable"):
coverage.add("independent-review")
if role.get("is-execution-agent"):
coverage.add("implementation")
for risk, role_ids in RISK_ROLE_HINTS.items():
if role_id in role_ids:
coverage.add(f"risk:{risk}")
searchable = " ".join([
role_id,
str(role.get("role-name") or ""),
str((profile or {}).get("perspective") or ""),
str((profile or {}).get("scope") or ""),
" ".join((profile or {}).get("responsibilities", []) or []),
])
coverage |= {f"keyword:{token}" for token in tokens(searchable)}
return coverage
def required_coverage(profile: dict[str, Any], candidate_family_ids: list[str]) -> set[str]:
required = {str(item) for item in profile.get("required-coverage", []) or []}
required_families = profile.get("required-families", []) or []
if isinstance(required_families, str):
required_families = [required_families]
required |= {f"owner:{family_id}" for family_id in required_families}
if candidate_family_ids:
required.add("owner")
required |= {f"artifact:{kind}" for kind in profile.get("required-artifacts", []) or []}
capabilities = profile.get("required-capabilities", []) or []
if isinstance(capabilities, str):
capabilities = [capabilities]
required |= {f"capability:{str(capability).strip().lower()}"
for capability in capabilities if str(capability or "").strip()}
risks = profile.get("risks", []) or []
if isinstance(risks, dict):
risks = [key for key, value in risks.items() if value]
required |= {f"risk:{str(risk).lower()}" for risk in risks}
if profile.get("authority-required"):
required.add("authority")
if profile.get("implementation-required") or profile.get("workflow-stage") in {"build", "run"}:
required.add("implementation")
if profile.get("independent-review-required"):
required.add("independent-review")
return required
@@ -0,0 +1,56 @@
"""Deterministic request classifier: light operational, substantial, or strategic."""
from __future__ import annotations
from typing import Any
from .coverage_model import tokens
STRATEGIC = {
"strategy", "portfolio", "pricing", "budget", "roadmap", "acquisition", "partnership",
"compliance", "legal", "production", "customer", "revenue", "one-way", "irreversible",
"전략", "포트폴리오", "가격", "예산", "로드맵", "인수", "법무", "규제", "매출", "고객",
}
LIGHT = {
"typo", "spelling", "docs", "comment", "rename", "format", "config", "test", "small",
"오탈자", "문서", "주석", "이름", "포맷", "설정", "테스트", "작은",
}
SUBSTANTIAL = {
"feature", "refactor", "migration", "architecture", "api", "database", "security", "design",
"기능", "리팩터", "마이그레이션", "아키텍처", "데이터베이스", "보안", "설계",
}
def classify_request(request: str, facts: dict[str, Any] | None = None) -> dict[str, Any]:
facts = facts or {}
observed = tokens(request) | tokens(facts)
searchable = (request + " " + str(facts)).lower()
strategic_hits = sorted({term for term in STRATEGIC if term in observed or term in searchable})
substantial_hits = sorted({term for term in SUBSTANTIAL if term in observed or term in searchable})
light_hits = sorted({term for term in LIGHT if term in observed or term in searchable})
if facts.get("one-way-door") or facts.get("blast-radius") == "production-customer-revenue" or strategic_hits:
route = "strategic"
plan = "cascade"
executive = True
elif substantial_hits or facts.get("cross-team"):
route = "substantial"
plan = "cascade"
executive = False
else:
route = "light-operational"
plan = "light"
executive = False
return {
"classification": route,
"plan": plan,
"executive-required": executive,
"signals": {
"strategic": strategic_hits,
"substantial": substantial_hits,
"light": light_hits,
},
"reason": (
"decision authority or high-blast signal" if executive else
"substantial implementation/design signal" if route == "substantial" else
"reversible owner-scoped task"
),
}
+145
View File
@@ -0,0 +1,145 @@
"""Shared divergent-lens policy derived from the role registries.
The policy deliberately reasons about registered family/role capabilities. A
caller supplied lens label is never sufficient evidence that a role can carry
that lens.
"""
from __future__ import annotations
import os
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.dirname(os.path.dirname(os.path.abspath(__file__)))))
)
REG = os.path.join(ROOT, "org-os", "00-role-registry")
TIERS = os.path.join(ROOT, "org-os", "06-agent-work", "governance-tiers.yaml")
def _load(path: str) -> dict[str, Any]:
try:
with open(path, encoding="utf-8") as handle:
return yaml.safe_load(handle) or {}
except Exception:
return {}
def registries() -> tuple[dict[str, Any], dict[str, Any], list[str]]:
families_doc = _load(os.path.join(REG, "capability-families.yaml"))
lenses_doc = _load(os.path.join(REG, "lens-registry.yaml"))
families = {
str(item.get("family-id")): item
for item in (families_doc.get("capability-families", {}) or {}).get("families", []) or []
if isinstance(item, dict) and item.get("family-id")
}
lens_items = (lenses_doc.get("lens-registry", {}) or {}).get("lenses", []) or []
lenses = {
str(item.get("lens-id")): item
for item in lens_items if isinstance(item, dict) and item.get("lens-id")
}
order = [str(item.get("lens-id")) for item in lens_items
if isinstance(item, dict) and item.get("lens-id")]
return families, lenses, order
def normalize_family_ids(values: Any) -> list[str]:
if isinstance(values, str):
values = [values]
if not isinstance(values, list):
return []
return [str(value).strip().upper() for value in values if str(value or "").strip()]
def candidate_family_errors(values: Any, *, tier: str, mode: str,
enforce_lens_floor: bool = False) -> list[str]:
family_ids = normalize_family_ids(values)
families, _lenses, _order = registries()
errors: list[str] = []
if not family_ids:
return ["candidate-families는 비어 있지 않은 등록 family 목록이어야 한다"]
duplicates = sorted({value for value in family_ids if family_ids.count(value) > 1})
unknown = sorted(set(family_ids) - set(families))
if duplicates:
errors.append(f"candidate-families 중복: {duplicates}")
if unknown:
errors.append(f"candidate-families 미등록 family: {unknown}")
if errors or (str(mode).lower() != "divergent" and not enforce_lens_floor):
return errors
available = available_lenses(family_ids)
policy = divergent_policy(tier)
minimum = policy.get("min-distinct-lenses")
if isinstance(minimum, int) and len(available) < minimum:
errors.append(
f"candidate-families 이론 렌즈 커버리지 부족: {len(available)} < tier {tier} 최소 {minimum}"
)
if policy.get("contrarian-required") and "LENS-CONTRARIAN" not in available:
errors.append(
"candidate-families에 contrarian rotation을 맡을 audit-capable family가 없다"
)
return errors
def divergent_policy(tier: str) -> dict[str, Any]:
doc = _load(TIERS).get("governance-tiers", {}) or {}
return (((doc.get("tiers") or {}).get(str(tier).lower()) or {}).get("divergent") or {})
def available_lenses(family_ids: list[str]) -> set[str]:
"""Return lenses the candidate set can actually assign.
Contrarian is special: the registry intentionally has no fixed carrier. It
becomes available only when the candidate set contains an audit-capable
family that can be rotated in independently.
"""
families, lenses, _order = registries()
selected = {family_id for family_id in family_ids if family_id in families}
result: set[str] = set()
for lens_id, lens in lenses.items():
if set(lens.get("carrier-families", []) or []) & selected:
result.add(lens_id)
for family_id in selected:
result.update(families[family_id].get("carries-lenses", []) or [])
if any(families[family_id].get("audit-capable") for family_id in selected):
result.add("LENS-CONTRARIAN")
return result
def required_lenses(family_ids: list[str], *, tier: str, mode: str) -> set[str]:
if str(mode).lower() != "divergent":
return set()
available = available_lenses(family_ids)
policy = divergent_policy(tier)
minimum = policy.get("min-distinct-lenses")
contrarian = bool(policy.get("contrarian-required"))
if minimum == "all-relevant":
return available
count = int(minimum or 0)
required: list[str] = []
if contrarian and "LENS-CONTRARIAN" in available:
required.append("LENS-CONTRARIAN")
_families, _lenses, order = registries()
required.extend(lens for lens in order if lens in available and lens not in required)
return set(required[:count])
def family_for_role(role_id: str) -> tuple[str | None, dict[str, Any] | None]:
families, _lenses, _order = registries()
wanted = str(role_id or "").upper()
for family_id, family in families.items():
if wanted in {str(value).upper() for value in family.get("member-role-ids", []) or []}:
return family_id, family
return None, None
def role_can_carry_lens(role_id: str, lens_id: str) -> bool:
family_id, family = family_for_role(role_id)
if not family_id or not family:
return False
lens_id = str(lens_id or "").upper()
if lens_id == "LENS-CONTRARIAN":
return bool(family.get("audit-capable"))
return lens_id in available_lenses([family_id])
@@ -0,0 +1,316 @@
"""Minimum-sufficient concrete-role planner.
Families are candidate pools, never actors. The planner uses a deterministic greedy
set-cover with token and independence constraints and records both selected and skipped roles.
"""
from __future__ import annotations
import os
from typing import Any
import yaml
from .budget_planner import estimate_plan, estimate_role, max_selected_roles, workflow_budget
from .coverage_model import (
CAPABILITY_ROLE_HINTS,
RISK_ROLE_HINTS,
required_coverage,
role_coverage,
tokens,
)
from .lens_policy import candidate_family_errors, normalize_family_ids, required_lenses
from .task_graph import build_task_graph
ROOT = os.environ.get("CLAUDE_PROJECT_DIR") or os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
)
REG = os.path.join(ROOT, "org-os", "00-role-registry")
ARTIFACT_REGISTRY = os.path.join(ROOT, "org-os", "06-agent-work", "generated", "artifact-registry.yaml")
CONTRACTS = os.path.join(ROOT, "org-os", "06-agent-work", "workflow-contracts.yaml")
SCORECARD = os.path.join(REG, "role-selection-scorecard.yaml")
EXECUTION_POLICY = os.path.join(ROOT, "org-os", "06-agent-work", "execution-policy.yaml")
def _load(path: str) -> dict[str, Any]:
try:
return yaml.safe_load(open(path, encoding="utf-8")) or {}
except Exception:
return {}
def _registries() -> tuple[dict[str, Any], dict[str, Any], dict[str, Any], dict[str, Any], dict[str, list[str]]]:
roles_doc = _load(os.path.join(REG, "roles.yaml")).get("role-registry", {}) or {}
families_doc = _load(os.path.join(REG, "capability-families.yaml")).get("capability-families", {}) or {}
profiles_doc = _load(os.path.join(REG, "role-profiles.yaml")).get("role-profiles", {}) or {}
artifacts = _load(ARTIFACT_REGISTRY).get("artifact-registry", {}) or {}
contracts = _load(CONTRACTS).get("workflow-contracts", {}) or {}
roles = {str(item["role-id"]): item for item in roles_doc.get("roles", []) or [] if item.get("role-id")}
families = {str(item["family-id"]): item for item in families_doc.get("families", []) or [] if item.get("family-id")}
profiles = {str(item["role-id"]): item for item in profiles_doc.get("profiles", []) or [] if item.get("role-id")}
return roles, families, profiles, artifacts, contracts.get("role-capabilities", {}) or {}
def _candidate_families(profile: dict[str, Any], families: dict[str, Any]) -> list[str]:
has_explicit = "candidate-families" in profile or "candidate-family" in profile
explicit = profile.get("candidate-families") if "candidate-families" in profile else profile.get("candidate-family")
if has_explicit:
# Unknown ids are intentionally retained here and rejected by the caller;
# silently dropping them used to turn a malformed explicit plan into an
# unrelated inferred plan.
return normalize_family_ids(explicit)
signal_tokens = tokens(profile.get("signals")) | tokens(profile.get("objective"))
ranked = []
for family_id, family in families.items():
haystack = tokens(family.get("invocation-triggers")) | tokens(family_id)
overlap = len(signal_tokens & haystack)
if overlap:
ranked.append((-overlap, family_id))
return [family_id for _, family_id in sorted(ranked)[:5]]
def _score(role: dict[str, Any], family: dict[str, Any], coverage: set[str], required: set[str], profile: dict[str, Any]) -> dict[str, int]:
signal_tokens = tokens(profile.get("signals")) | tokens(profile.get("objective"))
keyword_hits = len({value.split(":", 1)[1] for value in coverage if value.startswith("keyword:")} & signal_tokens)
if ("owner" in required
and family.get("lead-role-id") == role.get("role-id")):
relevance = 3
elif f"owner:{family['family-id']}" in required:
relevance = 3
elif "owner" in required:
relevance = min(3, 1 + keyword_hits)
else:
relevance = min(3, keyword_hits)
risk_coverage = min(3, len({value for value in required & coverage if value.startswith("risk:")}))
evidence_need = 3 if any(value.startswith("artifact:") for value in required & coverage) else 0
decision_authority = 3 if "authority" in required & coverage else 0
implementation_impact = 3 if "implementation" in required & coverage else (1 if role.get("is-execution-agent") else 0)
evidence = profile.get("already-available-evidence", []) or profile.get("existing-evidence", []) or []
duplicate_penalty = 3 if f"role:{role['role-id']}" in set(evidence) else 0
total = relevance + risk_coverage + evidence_need + decision_authority + implementation_impact - duplicate_penalty
return {
"relevance": relevance,
"risk-coverage": risk_coverage,
"evidence-need": evidence_need,
"decision-authority": decision_authority,
"implementation-impact": implementation_impact,
"duplicate-penalty": duplicate_penalty,
"total": total,
}
def select_minimum_sufficient_roles(profile: dict[str, Any]) -> dict[str, Any]:
roles, families, profiles, artifacts, role_capabilities = _registries()
tier = str(profile.get("tier") or "standard").lower()
stage = str(profile.get("workflow-stage") or profile.get("stage") or "") or None
scorecard = _load(SCORECARD).get("role-selection-scorecard", {}) or {}
execution_policy = _load(EXECUTION_POLICY).get("execution-policy", {}) or {}
family_ids = _candidate_families(profile, families)
has_explicit = "candidate-families" in profile or "candidate-family" in profile
family_errors = (candidate_family_errors(
family_ids, tier=tier, mode=str(profile.get("mode") or "converge"))
if has_explicit else [])
if family_errors:
empty_estimate = estimate_plan([], tier, stage)
plan = {
"version": 1,
"workflow-id": profile.get("workflow-id"),
"tier": tier,
"workflow-stage": stage,
"candidate-families": family_ids,
"selected": {"owner": None, "contributors": [], "reviewers": []},
"skipped": [],
"coverage": {"required": [], "already-covered": [], "covered": [], "missing": []},
"estimated-tokens": empty_estimate,
"budget": {"max-total": workflow_budget(tier, profile.get("token-budget")),
"within-budget": True},
"status": "blocked",
"errors": family_errors,
}
plan["task-graph"] = build_task_graph(plan)
return {"selection-plan": plan}
if profile.get("auto-expand-candidates", True):
family_by_role = {role_id: family_id for family_id, family in families.items()
for role_id in family.get("member-role-ids", []) or []}
artifact_kinds = artifacts.get("artifact-kinds", {}) or {}
extra_roles = set()
for kind in profile.get("required-artifacts", []) or []:
extra_roles |= set((artifact_kinds.get(kind) or {}).get("producer-roles", []) or [])
risks = profile.get("risks", []) or []
if isinstance(risks, dict):
risks = [key for key, value in risks.items() if value]
for risk in risks:
extra_roles |= RISK_ROLE_HINTS.get(str(risk).lower(), set())
capabilities = profile.get("required-capabilities", []) or []
if isinstance(capabilities, str):
capabilities = [capabilities]
for capability in capabilities:
extra_roles |= CAPABILITY_ROLE_HINTS.get(str(capability).strip().lower(), set())
if profile.get("authority-required"):
extra_roles |= {role_id for role_id, role in roles.items() if role.get("is-decision-maker")}
for role_id in sorted(extra_roles):
family_id = family_by_role.get(role_id)
if family_id and family_id not in family_ids:
family_ids.append(family_id)
required = required_coverage(profile, family_ids)
required |= {f"lens:{lens}" for lens in required_lenses(
family_ids, tier=tier, mode=str(profile.get("mode") or "converge"))}
existing = {str(item) for item in profile.get("already-available-evidence", []) or profile.get("existing-evidence", []) or []}
# Evidence may discharge an evidence/artifact need, but it cannot stand in
# for assigning a concrete capability or divergent lens carrier.
non_delegable = {item for item in required
if item.startswith("capability:") or item.startswith("lens:")}
effective_existing = existing - non_delegable
uncovered = required - effective_existing
excluded_roles = {str(value).upper() for value in profile.get("excluded-role-ids", []) or []}
candidates = []
for family_id in family_ids:
family = families[family_id]
for role_id in family.get("member-role-ids", []) or []:
if str(role_id).upper() in excluded_roles:
continue
role = roles.get(str(role_id))
if not role:
continue
coverage = role_coverage(role, family, profiles.get(str(role_id)), artifacts, role_capabilities)
candidates.append({
"role": role,
"family": family,
"coverage": coverage,
"score": _score(role, family, coverage, required, profile),
})
selected: list[dict[str, Any]] = []
limit = int(profile.get("max-selected-roles") or max_selected_roles(tier))
budget = workflow_budget(tier, profile.get("token-budget"))
while uncovered and candidates and len(selected) < limit:
ranked = []
for candidate in candidates:
gain = candidate["coverage"] & uncovered
if not gain:
continue
cost = estimate_role(candidate["role"], tier, stage)["total"]
ranked.append((
-(len(gain) * 100000 + candidate["score"]["total"] * 1000 - cost),
candidate["role"]["role-id"], candidate, gain,
))
if not ranked:
break
_, _, chosen, gain = sorted(ranked, key=lambda value: (value[0], value[1]))[0]
tentative = selected + [chosen]
if estimate_plan([item["role"] for item in tentative], tier, stage)["total"] > budget:
break
selected.append(chosen)
candidates.remove(chosen)
uncovered -= gain
# Independent review is a relational constraint: a producer cannot review its own output.
producer_ids = {item["role"]["role-id"] for item in selected
if "implementation" in item["coverage"] or any(v.startswith("artifact:") for v in item["coverage"] & required)}
needs_review = "independent-review" in required
if needs_review and not any(item["role"]["role-id"] not in producer_ids and "independent-review" in item["coverage"] for item in selected):
reviewer_pool = []
for family_id, family in families.items():
if not family.get("audit-capable"):
continue
for role_id in family.get("member-role-ids", []) or []:
if role_id in producer_ids or role_id not in roles:
continue
coverage = role_coverage(roles[role_id], family, profiles.get(role_id), artifacts, role_capabilities)
risk_gain = len(coverage & required)
reviewer_pool.append((-risk_gain, role_id, {"role": roles[role_id], "family": family,
"coverage": coverage,
"score": _score(roles[role_id], family, coverage, required, profile)}))
if reviewer_pool and len(selected) < limit:
reviewer = sorted(reviewer_pool)[0][2]
if estimate_plan([item["role"] for item in selected + [reviewer]], tier, stage)["total"] <= budget:
selected.append(reviewer)
uncovered.discard("independent-review")
selected_ids = {item["role"]["role-id"] for item in selected}
owner_items = [item for item in selected if "owner" in item["coverage"] & required
or any(value.startswith("owner:") for value in item["coverage"] & required)]
owner = owner_items[0] if owner_items else (selected[0] if selected else None)
reviewers = [item for item in selected if item is not owner and "independent-review" in item["coverage"]]
contributors = [item for item in selected if item is not owner and item not in reviewers]
def public(item: dict[str, Any], assignment: str) -> dict[str, Any]:
return {
"role-id": item["role"]["role-id"],
"family-id": item["family"]["family-id"],
"assignment": assignment,
"coverage": sorted(item["coverage"] & required),
"score": item["score"],
"reason": "minimum marginal coverage under token and independence constraints",
}
all_candidate_ids = [role_id for family_id in family_ids
for role_id in families[family_id].get("member-role-ids", []) or []]
skipped = []
for role_id in all_candidate_ids:
if role_id in selected_ids:
continue
skipped.append({
"role-id": role_id,
"reason": "coverage already satisfied by a lower-cost or higher-gain concrete role",
})
estimate = estimate_plan([item["role"] for item in selected], tier, stage)
covered = required - uncovered
result = {
"selection-plan": {
"version": 1,
"workflow-id": profile.get("workflow-id"),
"tier": tier,
"workflow-stage": stage,
"candidate-families": family_ids,
"selected": {
"owner": public(owner, "owner") if owner else None,
"contributors": [public(item, "contributor") for item in contributors],
"reviewers": [public(item, "independent-reviewer") for item in reviewers],
},
"skipped": skipped,
"coverage": {
"required": sorted(required),
"already-covered": sorted(required & effective_existing),
"covered": sorted(covered),
"missing": sorted(uncovered),
},
"estimated-tokens": estimate,
"budget": {"max-total": budget, "within-budget": estimate["total"] <= budget},
"status": "ready" if not uncovered and estimate["total"] <= budget else "blocked",
"policy": {
"score-formula": scorecard.get("total-score-formula"),
"decision-thresholds": scorecard.get("decision-thresholds"),
"max-concurrent-role-agents": ((execution_policy.get("wave") or {}).get("max-concurrent-role-agents") or 5),
"sources": ["role-selection-scorecard.yaml", "execution-policy.yaml", "governance-tiers.yaml"],
},
}
}
result["selection-plan"]["task-graph"] = build_task_graph(result["selection-plan"])
return result
def resolve_family(family_id: str, signals: list[str] | None = None, tier: str = "standard") -> dict[str, Any] | None:
profile = {
"candidate-families": [family_id],
"signals": signals or [],
"tier": tier,
"independent-review-required": False,
"max-selected-roles": 1 if tier in {"light", "standard"} else 3,
}
plan = select_minimum_sufficient_roles(profile)["selection-plan"]
owner = (plan.get("selected") or {}).get("owner")
if not owner:
return None
selected = [owner] + (plan["selected"].get("contributors") or [])
workers = [item["role-id"] for item in selected]
_, families, _, _, _ = _registries()
family = families.get(family_id)
return {
"requested-family": family_id,
"resolved-workers": workers,
"primary-worker": workers[0],
"available-workers": list((family or {}).get("member-role-ids", []) or []),
"routing-reason": "minimum-sufficient-coverage",
"collaboration-default": (family or {}).get("collaboration-default"),
"selection-plan": plan,
}
@@ -0,0 +1,27 @@
"""Compile a role selection plan into a small dependency graph."""
from __future__ import annotations
from typing import Any
def build_task_graph(selection_plan: dict[str, Any]) -> dict[str, Any]:
selected = selection_plan.get("selected") or {}
producers = []
for group in ("owner", "contributors"):
value = selected.get(group)
items = value if isinstance(value, list) else ([value] if value else [])
for item in items:
producers.append(item["role-id"] if isinstance(item, dict) else str(item))
reviewers = [item["role-id"] if isinstance(item, dict) else str(item)
for item in selected.get("reviewers", []) or []]
nodes = []
for role_id in producers:
nodes.append({"task-id": f"produce:{role_id}", "role-id": role_id, "depends-on": []})
producer_nodes = [node["task-id"] for node in nodes]
for role_id in reviewers:
nodes.append({"task-id": f"review:{role_id}", "role-id": role_id,
"depends-on": producer_nodes, "independent": True})
if len(nodes) > 1:
nodes.append({"task-id": "synthesize", "role-id": selection_plan.get("synthesis-role") or "OPS-ORCH",
"depends-on": [node["task-id"] for node in nodes], "reads": "projection-first"})
return {"workflow-id": selection_plan.get("workflow-id"), "nodes": nodes}
+2
View File
@@ -0,0 +1,2 @@
"""Reusable state-kernel services behind the compatibility ``state_engine.py`` CLI."""
+94
View File
@@ -0,0 +1,94 @@
"""Append-only JSONL primitives for the Org OS state kernel."""
from __future__ import annotations
import json
import os
from contextlib import nullcontext
from typing import Any, Callable, ContextManager, Iterable
def read_jsonl(path: str | None, on_error: Callable[[str], None] | None = None) -> list[dict[str, Any]]:
if not path or not os.path.exists(path):
return []
rows: list[dict[str, Any]] = []
try:
with open(path, encoding="utf-8") as handle:
for line in handle:
try:
value = json.loads(line)
except Exception:
continue
if isinstance(value, dict):
rows.append(value)
except Exception as exc:
if on_error:
on_error(f"event 원장 읽기 실패({path}): {exc}")
return rows
def append_jsonl(
path: str | None,
event: dict[str, Any],
*,
file_lock: bool = False,
on_error: Callable[[str], None] | None = None,
) -> bool:
if not path:
return False
try:
with open(path, "a", encoding="utf-8") as handle:
if file_lock:
try:
import fcntl
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
except Exception:
pass
handle.write(json.dumps(event, ensure_ascii=False) + "\n")
handle.flush()
os.fsync(handle.fileno())
return True
except Exception as exc:
if on_error:
on_error(f"event append 실패({path}): {exc}")
return False
def atomic_append(
entries: Iterable[tuple[str | None, dict[str, Any]]],
*,
transaction_lock: ContextManager[Any] | None = None,
) -> None:
"""Append a group of events and truncate every participating tail on failure."""
normalized = list(entries)
handles: list[tuple[Any, int]] = []
with (transaction_lock or nullcontext()):
try:
for path, _event in normalized:
if not path:
raise OSError("event path 해석 실패")
handle = open(path, "a+", encoding="utf-8")
try:
import fcntl
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
except Exception:
pass
handle.seek(0, os.SEEK_END)
handles.append((handle, handle.tell()))
try:
for (handle, _offset), (_path, event) in zip(handles, normalized):
handle.write(json.dumps(event, ensure_ascii=False) + "\n")
handle.flush()
os.fsync(handle.fileno())
except Exception:
for handle, offset in handles:
handle.seek(offset)
handle.truncate()
handle.flush()
raise
finally:
for handle, _offset in handles:
try:
handle.close()
except Exception:
pass
+172
View File
@@ -0,0 +1,172 @@
"""Deterministic workflow materialized-view projection from canonical events."""
from __future__ import annotations
from typing import Any, Callable
PROTECTED_FIELDS = (
"quality_gate_status", "quality-gate-status",
"release_acceptance_status", "release-acceptance-status",
"unresolved_critical_risks", "unresolved-critical-risks",
"blocker-open", "blocker_open", "resume-condition",
"resume_condition_present", "resume-condition-satisfied",
"resume_condition_satisfied", "evidence-grade", "evidence_grade",
"human_gate_approved", "human-gate-approved",
"current-quality-event-id", "current-quality-event-ids",
"current-quality-artifact-id", "current-quality-artifact-sha256",
"current-completion-artifact-id", "current-completion-artifact-sha256",
"quality-panel-unmet",
)
def _latest_artifact_of_kind(artifacts: list[dict[str, Any]], kind: str) -> dict[str, Any] | None:
return next((artifact for artifact in reversed(artifacts or [])
if isinstance(artifact, dict) and artifact.get("artifact-kind") == kind), None)
def project_workflow(
ledger: dict[str, Any],
*,
trusted_artifacts: list[dict[str, Any]],
workflow_events: list[dict[str, Any]],
initial_stage: Callable[[str], str],
quality_panel_unmet: Callable[[str, list[dict[str, Any]], list[dict[str, Any]]], list[str]],
default_plan: str,
) -> dict[str, Any]:
"""Rebuild protected fields; caller-owned data is never accepted as runtime truth."""
projected = dict(ledger)
for key in PROTECTED_FIELDS:
projected.pop(key, None)
projected["artifacts"] = trusted_artifacts
quality_events: list[dict[str, Any]] = []
release_events: list[dict[str, Any]] = []
for event in workflow_events:
event_type = event.get("event-type")
if event_type == "workflow-initialized":
projected["stage"] = event.get("stage") or initial_stage(event.get("plan", default_plan))
projected["stage-status"] = "running"
projected["last-completed-stage"] = None
projected["completed-for-next-stage"] = None
projected.pop("blocked-from", None)
projected.pop("design-direction-approval", None)
projected.pop("experience-foundation-approval", None)
for key in (
"plan", "tier", "mode", "evidence-contract-version",
"parent-workflow-id", "product-decision-id",
"direction-input-brief-ref", "direction-input-brief-sha256",
):
if event.get(key) is not None:
projected[key] = event.get(key)
elif event_type == "state-transition":
projected["stage"] = event.get("to") or projected.get("stage")
projected["stage-status"] = "running"
projected["last-completed-stage"] = event.get("from")
projected["completed-for-next-stage"] = None
if event.get("to") == "blocked":
projected["blocked-from"] = event.get("from")
elif event.get("from") == "blocked":
projected.pop("blocked-from", None)
elif event_type == "stage-completed":
if event.get("stage") == projected.get("stage"):
projected["stage-status"] = "completed"
projected["last-completed-stage"] = event.get("stage")
projected["completed-for-next-stage"] = event.get("intended-next-stage")
elif event_type == "quality-gate-recorded":
quality_events.append(event)
elif event_type == "release-decision-recorded":
release_events.append(event)
elif event_type == "tier-escalated":
projected["tier"] = event.get("to-tier") or projected.get("tier")
elif event_type == "workflow-blocked":
projected["blocked-from"] = event.get("blocked-from") or projected.get("stage")
projected["stage"] = "blocked"
projected["stage-status"] = "running"
projected["completed-for-next-stage"] = None
projected["blocker-open"] = True
projected["resume-condition"] = event.get("resume-condition")
projected.pop("resume-condition-satisfied", None)
elif event_type == "workflow-resumed":
projected["stage"] = event.get("to") or projected.get("blocked-from") or projected.get("stage")
projected["stage-status"] = "running"
projected["completed-for-next-stage"] = None
projected.pop("blocked-from", None)
projected["blocker-open"] = False
projected["resume-condition-satisfied"] = True
elif event_type == "direction-approval-registered":
projected["design-direction-approval"] = {
"report-ref": event.get("report-ref"),
"report-sha256": event.get("report-sha256"),
"child-workflow-id": event.get("child-workflow-id"),
}
elif event_type == "experience-foundation-registered":
projected["experience-foundation-approval"] = {
"child-workflow-id": event.get("child-workflow-id"),
"product-decision-id": event.get("product-decision-id"),
"benchmark-id": event.get("benchmark-id"),
"benchmark-ref": event.get("benchmark-ref"),
"benchmark-sha256": event.get("benchmark-sha256"),
"strategy-id": event.get("strategy-id"),
"strategy-ref": event.get("strategy-ref"),
"strategy-sha256": event.get("strategy-sha256"),
"technical-id": event.get("technical-id"),
"technical-ref": event.get("technical-ref"),
"technical-sha256": event.get("technical-sha256"),
"operational-id": event.get("operational-id"),
"operational-ref": event.get("operational-ref"),
"operational-sha256": event.get("operational-sha256"),
"blueprint-id": event.get("blueprint-id"),
"blueprint-ref": event.get("blueprint-ref"),
"blueprint-sha256": event.get("blueprint-sha256"),
"wireframe-id": event.get("wireframe-id"),
"wireframe-ref": event.get("wireframe-ref"),
"wireframe-sha256": event.get("wireframe-sha256"),
}
completion = _latest_artifact_of_kind(trusted_artifacts, "completion-record")
if not completion:
return projected
completion_id = completion.get("artifact-id")
completion_sha = completion.get("artifact-sha256")
projected["current-completion-artifact-id"] = completion_id
projected["current-completion-artifact-sha256"] = completion_sha
current_quality = [
event for event in quality_events
if event.get("reviewed-artifact-id") == completion_id
and event.get("reviewed-artifact-sha256") == completion_sha
and any(
artifact.get("artifact-id") == event.get("review-artifact-id")
and artifact.get("artifact-sha256") == event.get("review-artifact-sha256")
for artifact in trusted_artifacts
)
]
latest_by_actor = {event.get("actor"): event for event in current_quality}
active_quality = list(latest_by_actor.values())
if active_quality:
failed = any(event.get("status") != "Passed" or event.get("blocker-open")
for event in active_quality)
projected["quality_gate_status"] = "Failed" if failed else "Passed"
projected["blocker-open"] = bool(projected.get("blocker-open")) or any(
bool(event.get("blocker-open")) for event in active_quality)
last_quality = active_quality[-1]
projected["current-quality-event-id"] = last_quality.get("workflow-event-id")
projected["current-quality-event-ids"] = sorted(
event.get("workflow-event-id") for event in active_quality if event.get("workflow-event-id")
)
projected["current-quality-artifact-id"] = last_quality.get("review-artifact-id")
projected["current-quality-artifact-sha256"] = last_quality.get("review-artifact-sha256")
panel_unmet = quality_panel_unmet(projected.get("tier"), trusted_artifacts, active_quality)
if panel_unmet:
projected["quality-panel-unmet"] = panel_unmet
projected.pop("quality_gate_status", None)
active_event_ids = projected.get("current-quality-event-ids") or []
for event in release_events:
if (event.get("reviewed-completion-artifact-id") != completion_id
or event.get("reviewed-completion-artifact-sha256") != completion_sha
or sorted(event.get("quality-event-set") or []) != active_event_ids):
continue
if event.get("status") == "Approved" and (
projected.get("quality_gate_status") != "Passed" or projected.get("blocker-open")):
continue
projected["release_acceptance_status"] = event.get("status")
projected["unresolved_critical_risks"] = bool(event.get("unresolved-critical-risks"))
return projected
@@ -0,0 +1,93 @@
"""Pure workflow transition evaluation; state I/O remains outside this module."""
from __future__ import annotations
from typing import Any, Callable
def structural_next(
current: str,
plan_stages: list[str],
transitions: list[dict[str, Any]],
blocked_from: str | None = None,
) -> list[str]:
destinations: set[str] = set()
if current in plan_stages:
index = plan_stages.index(current)
if index + 1 < len(plan_stages):
destinations.add(plan_stages[index + 1])
for transition in transitions:
source = transition.get("from")
destination = transition.get("to")
if source == current:
if destination == "<resume>":
if blocked_from:
destinations.add(blocked_from)
elif destination:
destinations.add(destination)
elif source == "*" and current not in ("blocked", "closed") and destination:
destinations.add(destination)
destinations.discard("<resume>")
destinations.discard("*")
return sorted(destinations)
def actor_allowed(
transition: dict[str, Any],
actor: str | None,
role_registry: dict[str, Any],
) -> tuple[bool, str | None]:
allowed = transition.get("allowed-by") or []
if not str(actor or "").strip():
return False, "전이 주체(--actor) 미지정 — 누가 전이하는지 명시해야 한다(권한/감사)."
if not allowed:
return True, None
normalized = str(actor).strip()
if normalized not in role_registry:
return False, f"actor '{normalized}'는 role registry에 없는 역할이다"
if isinstance(allowed, dict):
concrete = allowed.get("executor") or allowed.get("concrete-roles") or []
else:
concrete = [value for value in allowed
if isinstance(value, str) and not value.endswith("-role-agent")]
if normalized in concrete:
return True, None
if not concrete:
return False, f"allowed-by가 placeholder만 포함해 runtime 권한을 결정할 수 없다: {allowed}"
return False, f"actor '{actor}' 는 transition executor {concrete} 에 없다(권한 없음)."
def evaluate_transition(
transition: dict[str, Any],
*,
current: str,
destination: str,
plan: str,
plan_stages: set[str],
blocked_from: str | None,
facts: dict[str, Any],
condition_evaluator: Callable[[Any, dict[str, Any]], tuple[bool, str | None]],
actor: str | None = None,
role_registry: dict[str, Any] | None = None,
) -> list[str]:
universal = (destination == "blocked" or current == "blocked"
or destination == current or transition.get("from") == "*")
if plan_stages and not universal and destination not in plan_stages:
return [f"plan '{plan}' 시퀀스에 없는 stage 전이 금지: {current} -> {destination} "
f"(plan stages: {sorted(plan_stages)})"]
if actor:
permitted, reason = actor_allowed(transition, actor, role_registry or {})
if not permitted:
return [reason or "transition actor 권한 없음"]
if current == "blocked" and blocked_from and destination != blocked_from:
return [f"blocked 재개 대상은 {blocked_from} 여야 합니다(요청: {destination})"]
reasons: list[str] = []
for condition in transition.get("required-conditions") or []:
passed, reason = condition_evaluator(condition, facts)
if not passed:
reasons.append(reason or f"조건 미충족: {condition}")
for condition in transition.get("forbidden-if") or []:
passed, _reason = condition_evaluator(condition, facts)
if passed:
reasons.append(f"금지조건 충족: {condition}")
return reasons