Add IR-first packaging and review studio
This commit is contained in:
+155
-2
@@ -7,6 +7,20 @@ from pathlib import Path
|
||||
import yaml
|
||||
|
||||
|
||||
def display_path(path: Path, root: Path) -> str:
|
||||
try:
|
||||
return str(path.resolve().relative_to(root.resolve()))
|
||||
except ValueError:
|
||||
return str(path.resolve())
|
||||
|
||||
|
||||
def read_json(path: Path) -> dict:
|
||||
if not path.exists():
|
||||
return {}
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
return payload if isinstance(payload, dict) else {}
|
||||
|
||||
|
||||
def read_simple_yaml(path: Path) -> dict:
|
||||
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
||||
|
||||
@@ -31,6 +45,23 @@ def read_interface(skill_dir: Path) -> dict:
|
||||
return raw
|
||||
|
||||
|
||||
def find_skill_ir(skill_dir: Path, name: str) -> tuple[dict, str]:
|
||||
candidates = [
|
||||
skill_dir / "reports" / "skill-ir.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{name}.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json",
|
||||
]
|
||||
seen = set()
|
||||
for path in candidates:
|
||||
if path in seen:
|
||||
continue
|
||||
seen.add(path)
|
||||
payload = read_json(path)
|
||||
if payload:
|
||||
return payload, display_path(path, skill_dir)
|
||||
return {}, "frontmatter-fallback"
|
||||
|
||||
|
||||
def require_fields(payload: dict, fields: list[str], label: str) -> None:
|
||||
missing = [field for field in fields if not payload.get(field)]
|
||||
if missing:
|
||||
@@ -46,8 +77,99 @@ def require_target_degradation(
|
||||
raise ValueError(f"Missing degradation entries for targets: {', '.join(missing)}")
|
||||
|
||||
|
||||
def count_list(payload: dict, key: str) -> int:
|
||||
value = payload.get(key, [])
|
||||
return len(value) if isinstance(value, list) else 0
|
||||
|
||||
|
||||
def resource_counts(resources: dict) -> dict:
|
||||
return {
|
||||
"references": count_list(resources, "references"),
|
||||
"scripts": count_list(resources, "scripts"),
|
||||
"assets": count_list(resources, "assets"),
|
||||
"reports": count_list(resources, "reports"),
|
||||
}
|
||||
|
||||
|
||||
def eval_counts(eval_plan: dict) -> dict:
|
||||
return {
|
||||
"trigger": count_list(eval_plan, "trigger"),
|
||||
"output": count_list(eval_plan, "output"),
|
||||
"adversarial": count_list(eval_plan, "adversarial"),
|
||||
"baseline": 1 if eval_plan.get("baseline") else 0,
|
||||
}
|
||||
|
||||
|
||||
def build_semantic_contract(
|
||||
*,
|
||||
skill_dir: Path,
|
||||
platform: str,
|
||||
frontmatter: dict,
|
||||
interface: dict,
|
||||
compatibility: dict,
|
||||
manifest: dict,
|
||||
ir: dict,
|
||||
ir_source: str,
|
||||
) -> dict:
|
||||
trigger_surface = ir.get("trigger_surface", {}) if isinstance(ir.get("trigger_surface"), dict) else {}
|
||||
workflow = ir.get("workflow", {}) if isinstance(ir.get("workflow"), dict) else {}
|
||||
resources = ir.get("resources", {}) if isinstance(ir.get("resources"), dict) else {}
|
||||
eval_plan = ir.get("eval_plan", {}) if isinstance(ir.get("eval_plan"), dict) else {}
|
||||
|
||||
frontmatter_name = str(frontmatter.get("name") or manifest.get("name") or skill_dir.name)
|
||||
frontmatter_description = str(frontmatter.get("description") or "")
|
||||
ir_name = str(ir.get("name") or "") if ir else ""
|
||||
ir_description = str(trigger_surface.get("description") or "") if ir else ""
|
||||
name = ir_name or frontmatter_name
|
||||
description = ir_description or frontmatter_description
|
||||
job = str(ir.get("job_to_be_done") or description)
|
||||
targets = ir.get("targets") if isinstance(ir.get("targets"), list) else compatibility.get("adapter_targets", [])
|
||||
target_values = [str(item) for item in targets]
|
||||
adapter_targets = [str(item) for item in compatibility.get("adapter_targets", [])]
|
||||
|
||||
semantic_contract = {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"job_to_be_done": job,
|
||||
"trigger_description": description,
|
||||
"should_trigger_count": count_list(trigger_surface, "should_trigger"),
|
||||
"should_not_trigger_count": count_list(trigger_surface, "should_not_trigger"),
|
||||
"edge_case_count": count_list(trigger_surface, "edge_cases"),
|
||||
"workflow_step_count": count_list(workflow, "steps"),
|
||||
"decision_point_count": count_list(workflow, "decision_points"),
|
||||
"failure_mode_count": count_list(workflow, "failure_modes"),
|
||||
"resource_counts": resource_counts(resources),
|
||||
"eval_counts": eval_counts(eval_plan),
|
||||
"risk": ir.get("risk", {}) if isinstance(ir.get("risk"), dict) else {},
|
||||
"governance": ir.get("governance", {}) if isinstance(ir.get("governance"), dict) else {},
|
||||
"targets": target_values,
|
||||
"source_files_count": count_list(ir, "source_files") if ir else 0,
|
||||
}
|
||||
semantic_parity = {
|
||||
"source": "skill-ir" if ir else "frontmatter-fallback",
|
||||
"ir_source": ir_source,
|
||||
"name_matches_ir": bool(ir) and frontmatter_name == name,
|
||||
"description_matches_ir": bool(ir) and frontmatter_description == description,
|
||||
"platform_declared_in_ir": platform in target_values
|
||||
or (platform == "generic" and "agent-skills-compatible" in target_values),
|
||||
"platform_declared_in_interface": platform in adapter_targets,
|
||||
"display_name_present": bool(interface.get("display_name")),
|
||||
"default_prompt_present": bool(interface.get("default_prompt")),
|
||||
}
|
||||
return {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"job_to_be_done": job,
|
||||
"ir_source": ir_source,
|
||||
"ir_schema_version": str(ir.get("schema_version") or "none"),
|
||||
"semantic_contract": semantic_contract,
|
||||
"semantic_parity": semantic_parity,
|
||||
}
|
||||
|
||||
|
||||
def build_manifest(skill_dir: Path, platform: str) -> dict:
|
||||
frontmatter = read_frontmatter(skill_dir / "SKILL.md")
|
||||
manifest = read_json(skill_dir / "manifest.json")
|
||||
interface_doc = read_interface(skill_dir)
|
||||
interface = interface_doc.get("interface", {})
|
||||
compatibility = interface_doc.get("compatibility", {})
|
||||
@@ -66,12 +188,28 @@ def build_manifest(skill_dir: Path, platform: str) -> dict:
|
||||
"compatibility.trust",
|
||||
)
|
||||
require_target_degradation(degradation, compatibility.get("adapter_targets", []))
|
||||
ir, ir_source = find_skill_ir(skill_dir, str(frontmatter.get("name") or manifest.get("name") or skill_dir.name))
|
||||
semantic = build_semantic_contract(
|
||||
skill_dir=skill_dir,
|
||||
platform=platform,
|
||||
frontmatter=frontmatter,
|
||||
interface=interface,
|
||||
compatibility=compatibility,
|
||||
manifest=manifest,
|
||||
ir=ir,
|
||||
ir_source=ir_source,
|
||||
)
|
||||
return {
|
||||
"name": frontmatter.get("name", skill_dir.name),
|
||||
"description": frontmatter.get("description", ""),
|
||||
"name": semantic["name"],
|
||||
"description": semantic["description"],
|
||||
"version": frontmatter.get("version", "1.0.0"),
|
||||
"platform": platform,
|
||||
"skill_root": skill_dir.name,
|
||||
"job_to_be_done": semantic["job_to_be_done"],
|
||||
"ir_source": semantic["ir_source"],
|
||||
"ir_schema_version": semantic["ir_schema_version"],
|
||||
"semantic_contract": semantic["semantic_contract"],
|
||||
"semantic_parity": semantic["semantic_parity"],
|
||||
"display_name": interface.get("display_name", skill_dir.name),
|
||||
"short_description": interface.get("short_description", ""),
|
||||
"default_prompt": interface.get("default_prompt", ""),
|
||||
@@ -108,6 +246,11 @@ PLATFORM_CONTRACTS = {
|
||||
"display_name",
|
||||
"short_description",
|
||||
"default_prompt",
|
||||
"job_to_be_done",
|
||||
"ir_source",
|
||||
"ir_schema_version",
|
||||
"semantic_contract",
|
||||
"semantic_parity",
|
||||
"canonical_metadata",
|
||||
"canonical_format",
|
||||
"activation_mode",
|
||||
@@ -135,6 +278,11 @@ PLATFORM_CONTRACTS = {
|
||||
"display_name",
|
||||
"short_description",
|
||||
"default_prompt",
|
||||
"job_to_be_done",
|
||||
"ir_source",
|
||||
"ir_schema_version",
|
||||
"semantic_contract",
|
||||
"semantic_parity",
|
||||
"canonical_metadata",
|
||||
"canonical_format",
|
||||
"activation_mode",
|
||||
@@ -162,6 +310,11 @@ PLATFORM_CONTRACTS = {
|
||||
"display_name",
|
||||
"short_description",
|
||||
"default_prompt",
|
||||
"job_to_be_done",
|
||||
"ir_source",
|
||||
"ir_schema_version",
|
||||
"semantic_contract",
|
||||
"semantic_parity",
|
||||
"canonical_metadata",
|
||||
"canonical_format",
|
||||
"activation_mode",
|
||||
|
||||
Reference in New Issue
Block a user