#!/usr/bin/env python3 """Resolve and enforce registered Guanghu persona skills without granting authority.""" from __future__ import annotations import argparse import json import re from pathlib import Path DEFAULT_REGISTRY = Path(__file__).resolve().parents[1] / "references" / "persona-skill-registry.json" REQUIRED_SKILL_FIELDS = { "id", "hldp_skill", "gls_id", "title", "intents", "preferred_route", "forbidden_route_markers", "deprecated_route_markers", "evidence", "freshness", "authorization", } def load_registry(path: Path = DEFAULT_REGISTRY) -> dict: with path.open(encoding="utf-8") as handle: registry = json.load(handle) if registry.get("schema") != "guanghu.persona-skill-registry/v1": raise ValueError("unsupported persona skill registry schema") if not registry.get("version") or not isinstance(registry.get("skills"), list): raise ValueError("registry version or skills are missing") seen_ids: set[str] = set() seen_stable_numbers: dict[str, str] = {} for skill in registry["skills"]: missing = REQUIRED_SKILL_FIELDS.difference(skill) if missing: raise ValueError(f"{skill.get('id', '')} missing fields: {sorted(missing)}") skill_id = skill["id"] if skill_id in seen_ids: raise ValueError(f"duplicate skill id: {skill_id}") seen_ids.add(skill_id) match = re.match(r"^(GHS-\d{3})(?:-|$)", skill_id) if not match: raise ValueError(f"invalid stable skill id: {skill_id}") stable_number = match.group(1) if stable_number in seen_stable_numbers: raise ValueError( "duplicate stable skill number: " f"{stable_number} used by {seen_stable_numbers[stable_number]} and {skill_id}" ) seen_stable_numbers[stable_number] = skill_id return registry def _normalise(value: str) -> str: return "".join(value.lower().split()) def _marker_hits(text: str, markers: list[str]) -> list[str]: normalised = _normalise(text) return [marker for marker in markers if _normalise(marker) in normalised] def select_skill(registry: dict, intent: str) -> tuple[dict | None, int]: normalised = _normalise(intent) best_skill = None best_score = 0 for skill in registry["skills"]: score = sum(max(1, len(_normalise(term))) for term in skill["intents"] if _normalise(term) in normalised) if score > best_score: best_skill = skill best_score = score return best_skill, best_score def resolve(registry: dict, intent: str, proposed_route: str = "") -> dict: global_forbidden = [] global_deprecated = [] global_guard_ids = [] for candidate in registry["skills"]: if candidate.get("enforcement_scope") != "GLOBAL_PREREQUISITE": continue forbidden_hits = _marker_hits(proposed_route, candidate["forbidden_route_markers"]) deprecated_hits = _marker_hits(proposed_route, candidate["deprecated_route_markers"]) if forbidden_hits or deprecated_hits: global_guard_ids.append(candidate["id"]) global_forbidden.extend(forbidden_hits) global_deprecated.extend(deprecated_hits) if global_forbidden or global_deprecated: return { "decision": "BLOCK" if global_forbidden else "CORRECT", "matched_skill": global_guard_ids[0], "applied_global_guards": global_guard_ids, "hldp_skill": next( skill["hldp_skill"] for skill in registry["skills"] if skill["id"] == global_guard_ids[0] ), "gls_id": next( skill["gls_id"] for skill in registry["skills"] if skill["id"] == global_guard_ids[0] ), "confidence": 1.0, "preferred_route": next( skill["preferred_route"] for skill in registry["skills"] if skill["id"] == global_guard_ids[0] ), "forbidden_hits": global_forbidden, "deprecated_hits": global_deprecated, "correction": "A global persona-source prerequisite rejected the route before task-specific skill selection.", "evidence": next( skill["evidence"] for skill in registry["skills"] if skill["id"] == global_guard_ids[0] ), "freshness": next( skill["freshness"] for skill in registry["skills"] if skill["id"] == global_guard_ids[0] ), "authorization": next( skill["authorization"] for skill in registry["skills"] if skill["id"] == global_guard_ids[0] ), "recovery_route": next( skill.get("recovery_route", []) for skill in registry["skills"] if skill["id"] == global_guard_ids[0] ), "enforcement_level": "PROMOTED_SKILL", "authority_granted": False, "registry_version": registry["version"], } skill, score = select_skill(registry, intent) if skill is None: return { "decision": "NO_MATCH", "registry_id": registry["registry_id"], "registry_version": registry["version"], "correction": "Resolve the stable number through SYS-GLW-LTH-0001 and current REPO-012 main; do not invent a route.", "authority_granted": False, } forbidden = _marker_hits(proposed_route, skill["forbidden_route_markers"]) deprecated = _marker_hits(proposed_route, skill["deprecated_route_markers"]) if forbidden: decision = "BLOCK" correction = "Stop the proposed route and use the registered preferred route after live verification." elif deprecated: decision = "CORRECT" correction = "Replace the deprecated route with the registered preferred route and verify current evidence." else: decision = "ALLOW" correction = "Use the preferred route within the stated authorization boundary." return { "decision": decision, "matched_skill": skill["id"], "hldp_skill": skill["hldp_skill"], "gls_id": skill["gls_id"], "confidence": min(1.0, round(score / 20, 2)), "preferred_route": skill["preferred_route"], "forbidden_hits": forbidden, "deprecated_hits": deprecated, "correction": correction, "evidence": skill["evidence"], "freshness": skill["freshness"], "authorization": skill["authorization"], "recovery_route": skill.get("recovery_route", []), "enforcement_level": skill.get("enforcement_level", "UNSPECIFIED"), "experience_receipts": skill.get("experience_receipts", []), "authority_granted": False, "registry_version": registry["version"], } def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--intent", required=True) parser.add_argument("--proposed-route", default="") parser.add_argument("--registry", type=Path, default=DEFAULT_REGISTRY) parser.add_argument("--json", action="store_true") args = parser.parse_args() try: result = resolve(load_registry(args.registry), args.intent, args.proposed_route) except (OSError, ValueError, json.JSONDecodeError) as error: parser.error(str(error)) if args.json: print(json.dumps(result, ensure_ascii=False, indent=2)) else: print(result["decision"]) print(result["correction"]) for step in result.get("preferred_route", []): print(f"- {step}") return 0 if __name__ == "__main__": raise SystemExit(main())