#!/usr/bin/env python3
"""HTML layout helpers for the Skill Atlas report."""
import html
from typing import Any
SCRIPT_INTERFACE = "internal-module"
SCRIPT_INTERFACE_REASON = "Imported by build_skill_atlas.py to render the static Skill Atlas HTML report."
def render_html(payload: dict[str, Any]) -> str:
summary = payload["summary"]
rows = []
for skill in payload["catalog"]["skills"][:80]:
rows.append(
"
"
f"| {html.escape(skill['name'])} | "
f"{html.escape(skill['path'])} | "
f"{html.escape(skill.get('owner') or 'missing')} | "
f"{html.escape(skill.get('maturity') or 'unknown')} | "
f"{html.escape(skill.get('review_cadence') or 'missing')} | "
f"{html.escape(skill.get('atlas_scope') or 'release')} | "
"
"
)
blockers = (
payload["actionable_route_collisions"][:20]
+ payload["actionable_owner_review_gaps"][:20]
+ payload["actionable_stale_skills"][:20]
+ payload["actionable_drift_signals"][:20]
)
blocker_items = "".join(
f"{html.escape(item.get('name', item.get('skill_a', 'issue')))} {html.escape(item.get('reason', item.get('status', ', '.join(item.get('missing', item.get('signal_types', []))))))}"
for item in blockers
)
opportunity_items = "".join(
(
""
f"{html.escape(item.get('skill') or item.get('source_type', 'opportunity'))} "
f"{html.escape(item.get('note') or item.get('recommendation') or item.get('signal', 'no-route opportunity'))}"
f"
{html.escape(item.get('source', ''))}"
""
)
for item in payload["no_route_opportunities"][:20]
)
return f"""
Skill Atlas
Skill Atlas
Portfolio-level review for route overlap, stale ownership, shared resources, telemetry drift, and no-route opportunities.
Skills{summary['skill_count']}
Actionable{summary['actionable_skill_count']}
Route Collisions{summary['actionable_route_collision_count']}
Owner Gaps{summary['actionable_owner_gap_count']}
Stale Skills{summary['actionable_stale_count']}
Drift Signals{summary['actionable_drift_signal_count']}
No-Route Opportunities{summary['no_route_opportunity_count']}
Actionable Issues
{blocker_items or '- No blocking portfolio issues detected.
'}
No-Route Opportunities
Missed-trigger telemetry and explicit failure cases become candidate routing work without storing raw prompts or outputs.
{opportunity_items or '- No no-route opportunities detected.
'}
Full Portfolio Counts
All scanned skills remain visible: {summary['route_collision_count']} total route collisions, {summary['owner_gap_count']} total owner gaps, {summary['stale_count']} total stale signals, and {summary['drift_signal_count']} telemetry drift signals.
Catalog
| Name | Path | Owner | Maturity | Review | Scope |
{''.join(rows)}
"""