136 lines
6.8 KiB
Python
136 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Compile-time artifact registry invariants and no runtime auto-admission."""
|
|
import importlib.util
|
|
import os
|
|
import sys
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
COMPILER = os.path.join(ROOT, ".claude", "hooks", "compile_artifact_registry.py")
|
|
spec = importlib.util.spec_from_file_location("artifact_registry_compiler", COMPILER)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
sys.path.insert(0, os.path.join(ROOT, ".claude", "hooks"))
|
|
import artifact_contract as AC # noqa: E402
|
|
|
|
passed = failed = 0
|
|
|
|
|
|
def check(name, condition):
|
|
global passed, failed
|
|
if condition:
|
|
passed += 1
|
|
print(f" PASS {name}")
|
|
else:
|
|
failed += 1
|
|
print(f" FAIL {name}")
|
|
|
|
|
|
document, errors = module.compile_registry()
|
|
registry = (document or {}).get("artifact-registry", {})
|
|
check("registry compiles without invariant errors", errors == [])
|
|
check("runtime registry covers the controlled vocabulary", registry.get("artifact-kind-count") >= 180)
|
|
check("all compiled producer roles are concrete", all(
|
|
role in module._roles()
|
|
for definition in (registry.get("artifact-kinds") or {}).values()
|
|
for role in definition.get("producer-roles") or []
|
|
))
|
|
check("every reviewer capability is declared", all(
|
|
definition.get("reviewer-capability")
|
|
in module._load(module.WORKFLOW_PATH)["workflow-contracts"]["role-capabilities"]
|
|
for definition in (registry.get("artifact-kinds") or {}).values()
|
|
))
|
|
check("core aggregate bindings are compiled into the runtime registry",
|
|
registry["artifact-kinds"]["grounding-package"]["method-binding"]["role-methods"]
|
|
["STR-ANALYST"]["checkpoint-step-id"] == "diverge-options"
|
|
and registry["artifact-kinds"]["executive-decision-packet"]["method-binding"]
|
|
["role-methods"]["EXEC-CEO"]["checkpoint-step-id"] == "converge-decision")
|
|
check("venture bootstrap bindings support standard-tier submissions",
|
|
registry["artifact-kinds"]["opportunity-cluster"]["method-binding"]["mode"] == "stage-synthesis"
|
|
and registry["artifact-kinds"]["venture-validation"]["method-binding"]["mode"] == "stage-synthesis"
|
|
and registry["artifact-kinds"]["venture-validation"]["producer-roles"] == ["EXEC-CEO"]
|
|
and registry["artifact-kinds"]["venture-decision"]["method-binding"]
|
|
["role-methods"]["EXEC-CEO"]["checkpoint-step-id"] == "converge-decision")
|
|
broken_binding = {
|
|
"producer-roles": ["STR-ANALYST"], "required-payload-fields": ["options"],
|
|
"method-binding": {"mode": "aggregate", "role-methods": {"STR-ANALYST": {
|
|
"method-id": "strategy-analysis", "checkpoint-step-id": "diverge-options",
|
|
"embedded-outputs": {"option-set": ["options"]},
|
|
}}},
|
|
}
|
|
binding_errors = module._binding_errors(
|
|
"broken-grounding", broken_binding, module._methods())
|
|
check("aggregate binding cannot omit earlier craft outputs",
|
|
any("grounding-evidence" in error for error in binding_errors)
|
|
and any("analysis-synthesis" in error for error in binding_errors))
|
|
|
|
original = module._methods
|
|
try:
|
|
module._methods = lambda: {
|
|
"ENG-BE": {"methods": [{"method-id": "typo", "output-artifacts": ["api-contarct"]}]}
|
|
}
|
|
_document, typo_errors = module.compile_registry()
|
|
check("method typo cannot auto-create a legal artifact kind",
|
|
any("not explicitly admitted" in error and "api-contarct" in error for error in typo_errors))
|
|
finally:
|
|
module._methods = original
|
|
|
|
check("generated registry has no drift", module.main(["--check"]) == 0)
|
|
standard_minimal = {"tier": "standard", "artifact-kind": "executive-decision-packet",
|
|
"payload": {"recommendation": "A"}}
|
|
light_minimal = {"tier": "light", "artifact-kind": "executive-decision-packet",
|
|
"payload": {"recommendation": "A"}}
|
|
check("standard decision packet rejects recommendation-only payload",
|
|
any("selected-option-id" in error for error in AC._semantic_errors(
|
|
standard_minimal, "executive-decision-packet")))
|
|
check("light decision packet keeps proportional minimal contract",
|
|
AC._semantic_errors(light_minimal, "executive-decision-packet") == [])
|
|
_gates = [
|
|
"problem-intensity", "competition-alternatives", "willingness-to-pay",
|
|
"revenue-unit-economics", "tech-feasibility-moat", "operability",
|
|
"distribution", "founder-fit", "kill-criteria",
|
|
]
|
|
_option = {
|
|
"id": "V1", "customer": "developer", "painful-job": "understand mechanisms",
|
|
"current-alternative": "docs", "wedge": "guided lab", "monetization": "unknown",
|
|
"expected-price": "unknown", "reachable-customers": "unknown",
|
|
"rough-revenue-ceiling": "unknown", "acquisition-channel": "unknown",
|
|
"build-cost": "one flagship", "operation-cost": "unknown", "founder-fit": "conditional",
|
|
"defensibility": "learning design", "kill-criteria": ["no learning outcome"],
|
|
"unresolved-assumptions": ["demand"],
|
|
"validation-results": [
|
|
{"option-id": "V1", "gate": gate, "verdict": "unknown", "evidence": [], "dissent": []}
|
|
for gate in _gates
|
|
],
|
|
}
|
|
_validation_payload = {
|
|
"source-artifact-refs": [
|
|
{"artifact-id": "OC1", "artifact-sha256": "a" * 64},
|
|
{"artifact-id": "OC2", "artifact-sha256": "b" * 64},
|
|
],
|
|
"hypotheses": ["demand"], "experiments": ["user test"], "evidence": ["existing product"],
|
|
"option-evaluations": [_option, {**_option, "id": "V2", "validation-results": [
|
|
{**result, "option-id": "V2"} for result in _option["validation-results"]
|
|
]}],
|
|
"kill-criteria": ["no learning outcome"], "recommendation": "V1",
|
|
}
|
|
check("venture validation requires exactly one result for each of the 9 gates",
|
|
AC._semantic_errors({"tier": "standard", "payload": _validation_payload},
|
|
"venture-validation") == [])
|
|
_duplicate_gate_payload = {**_validation_payload, "option-evaluations": [
|
|
{**_option, "validation-results": [*_option["validation-results"][:-1],
|
|
_option["validation-results"][0]]},
|
|
_validation_payload["option-evaluations"][1],
|
|
]}
|
|
_duplicate_errors = AC._semantic_errors(
|
|
{"tier": "standard", "payload": _duplicate_gate_payload}, "venture-validation")
|
|
check("venture validation rejects duplicated gate standing in for a missing gate",
|
|
any("9-gate 누락" in error for error in _duplicate_errors)
|
|
and any("gate 중복" in error for error in _duplicate_errors))
|
|
compiled_kinds = AC.load_contract()["artifact-kinds"]
|
|
check("core gates use specialized reviewers",
|
|
compiled_kinds["overall-design"]["reviewer-capability"] == "architecture-auditor"
|
|
and compiled_kinds["threat-model"]["reviewer-capability"] == "security-auditor"
|
|
and compiled_kinds["api-contract"]["reviewer-capability"] == "technical-accuracy-auditor")
|
|
print(f"\n{passed} passed, {failed} failed")
|
|
raise SystemExit(1 if failed else 0)
|