"""Tests for ctx_monitor — dashboard aggregation and HTML rendering.""" from __future__ import annotations import http.client import hashlib import gzip import json import os import sqlite3 import tarfile import threading import time import urllib.error import urllib.request import zlib from pathlib import Path import pytest import ctx_init as ci from ctx import dashboard_entities from ctx import dashboard_docs from ctx.monitor import testing as mt from ctx.monitor.pages import config as config_page from ctx.monitor.pages import graph as graph_page from ctx.monitor.pages import harness as harness_page from ctx.monitor.pages import home as home_page from ctx.monitor.pages import loaded as loaded_page from ctx.monitor.pages import manage as manage_page from ctx.monitor.pages import skills as skills_page from ctx.monitor.pages import skillspector as skillspector_page from ctx.monitor.pages import wiki as wiki_page from ctx.monitor.api import mutations as mutations_api from ctx.monitor.api import readonly as readonly_api from ctx.monitor import routes as monitor_routes from ctx.monitor import cli as monitor_cli from ctx.monitor.services import config as config_service from ctx.monitor.services import graph as graph_service from ctx.monitor.services import harness as harness_service from ctx.monitor.services import kpi as kpi_service from ctx.monitor.services import manifest as manifest_service from ctx.monitor.services import runtime as runtime_service from ctx.monitor.services import sidecars as sidecar_service from ctx.monitor.services import skillspector as skillspector_service from ctx.monitor.services import status as status_service from ctx.monitor.services import wiki as wiki_service from ctx.core.wiki import wiki_queue @pytest.fixture def fake_claude(tmp_path: Path, monkeypatch) -> Path: """Point monitor compatibility helpers at a throwaway ~/.claude tree.""" claude = tmp_path / ".claude" (claude / "skill-quality").mkdir(parents=True) monkeypatch.setattr(mt, "claude_dir", lambda: claude) monkeypatch.setattr(mt, "dashboard_graph_index_archives", lambda: []) graph_service.reset_caches() sidecar_service.reset_caches() kpi_service.reset_cache() monkeypatch.setattr(mt, "WIKI_RENDER_CACHE_KEY", None) monkeypatch.setattr(mt, "WIKI_RENDER_CACHE_VALUE", None) dashboard_docs.reset_docs_render_cache() return claude def _write_audit(claude: Path, records: list[dict]) -> None: path = claude / "ctx-audit.jsonl" path.write_text( "\n".join(json.dumps(r) for r in records) + "\n", encoding="utf-8", ) def _write_events(claude: Path, records: list[dict]) -> None: path = claude / "skill-events.jsonl" path.write_text( "\n".join(json.dumps(r) for r in records) + "\n", encoding="utf-8", ) def _write_runtime_events(path: Path, records: list[dict]) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text( "\n".join(json.dumps(r) for r in records) + "\n", encoding="utf-8", ) def _write_sidecar(claude: Path, slug: str, body: dict) -> None: (claude / "skill-quality" / f"{slug}.json").write_text( json.dumps(body), encoding="utf-8", ) def test_monitor_testing_facade_rejects_private_helper_names() -> None: with pytest.raises(AttributeError): getattr(mt, "_render_graph") with pytest.raises(AttributeError): setattr(mt, "_claude_dir", lambda: None) def _write_mcp_sidecar(claude: Path, slug: str, body: dict) -> None: mcp_dir = claude / "skill-quality" / "mcp" mcp_dir.mkdir(parents=True, exist_ok=True) (mcp_dir / f"{slug}.json").write_text(json.dumps(body), encoding="utf-8") def _write_graph_manifest(claude: Path, export_id: str) -> None: graph_dir = claude / "skill-wiki" / "graphify-out" graph_dir.mkdir(parents=True, exist_ok=True) (graph_dir / "graph-export-manifest.json").write_text( json.dumps({"version": 1, "export_id": export_id}), encoding="utf-8", ) def _write_skillspector_audit(claude: Path, records: list[dict]) -> Path: path = claude / "skill-wiki" / "security" / "skillspector-audit.jsonl.gz" path.parent.mkdir(parents=True, exist_ok=True) with gzip.open(path, "wt", encoding="utf-8", newline="\n") as f: for record in records: f.write(json.dumps(record, sort_keys=True)) f.write("\n") return path def test_read_jsonl_skips_non_object_lines(tmp_path: Path) -> None: path = tmp_path / "events.jsonl" path.write_text( "\n".join([ json.dumps({"event": "ok"}), json.dumps(["not", "an", "object"]), "not-json", json.dumps("scalar"), ]), encoding="utf-8", ) assert mt.read_jsonl(path) == [{"event": "ok"}] assert runtime_service.read_jsonl(path) == [{"event": "ok"}] def test_read_jsonl_limit_keeps_tail_without_full_slice(tmp_path: Path) -> None: path = tmp_path / "events.jsonl" path.write_text( "\n".join(json.dumps({"i": i}) for i in range(5)), encoding="utf-8", ) assert mt.read_jsonl(path, limit=2) == [{"i": 3}, {"i": 4}] assert runtime_service.read_jsonl(path, limit=2) == [{"i": 3}, {"i": 4}] def _post_json(port: int, path: str, body: dict, token: str | None = None) -> tuple[int, dict]: headers = {"Content-Type": "application/json"} if token is not None: headers["X-CTX-Monitor-Token"] = token request = urllib.request.Request( f"http://127.0.0.1:{port}{path}", data=json.dumps(body).encode("utf-8"), headers=headers, method="POST", ) try: with urllib.request.urlopen(request, timeout=5) as response: return response.status, json.loads(response.read().decode("utf-8")) except urllib.error.HTTPError as exc: return exc.code, json.loads(exc.read().decode("utf-8")) def _get_json(port: int, path: str) -> tuple[int, dict]: try: with urllib.request.urlopen(f"http://127.0.0.1:{port}{path}", timeout=5) as response: return response.status, json.loads(response.read().decode("utf-8")) except urllib.error.HTTPError as exc: return exc.code, json.loads(exc.read().decode("utf-8")) def _get_raw( port: int, path: str, *, headers: dict[str, str], ) -> tuple[int, dict]: conn = http.client.HTTPConnection("127.0.0.1", port, timeout=5) try: conn.putrequest("GET", path, skip_host=True) for key, value in headers.items(): conn.putheader(key, value) conn.endheaders() response = conn.getresponse() payload = json.loads(response.read().decode("utf-8")) return response.status, payload finally: conn.close() def _post_raw( port: int, path: str, *, headers: dict[str, str], body: bytes = b"", ) -> tuple[int, dict]: conn = http.client.HTTPConnection("127.0.0.1", port, timeout=5) try: conn.putrequest("POST", path, skip_host=True) if "Host" not in headers: conn.putheader("Host", f"127.0.0.1:{port}") for key, value in headers.items(): conn.putheader(key, value) conn.endheaders() if body: conn.send(body) response = conn.getresponse() payload = json.loads(response.read().decode("utf-8")) return response.status, payload finally: conn.close() def _serve_monitor( monkeypatch: pytest.MonkeyPatch, token: str = "test-token", host: str = "127.0.0.1", ): monkeypatch.setattr(mt, "MONITOR_TOKEN", token) server = mt.make_monitor_server(host, 0) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() return server, thread, server.server_port def test_summarize_sessions_merges_audit_and_events(fake_claude: Path) -> None: _write_audit(fake_claude, [ {"ts": "2026-04-19T10:00:00Z", "event": "skill.loaded", "subject_type": "skill", "subject": "python-patterns", "actor": "hook", "session_id": "S1"}, {"ts": "2026-04-19T10:05:00Z", "event": "skill.score_updated", "subject_type": "skill", "subject": "python-patterns", "actor": "hook", "session_id": "S1"}, {"ts": "2026-04-19T10:10:00Z", "event": "agent.loaded", "subject_type": "agent", "subject": "code-reviewer", "actor": "hook", "session_id": "S2"}, ]) _write_events(fake_claude, [ {"timestamp": "2026-04-19T10:01:00Z", "event": "load", "skill": "fastapi-pro", "session_id": "S1"}, {"timestamp": "2026-04-19T10:02:00Z", "event": "unload", "skill": "fastapi-pro", "session_id": "S1"}, ]) sessions = mt.summarize_sessions() by_id = {s["session_id"]: s for s in sessions} assert "S1" in by_id assert "S2" in by_id assert "python-patterns" in by_id["S1"]["skills_loaded"] assert "fastapi-pro" in by_id["S1"]["skills_loaded"] assert "fastapi-pro" in by_id["S1"]["skills_unloaded"] assert by_id["S1"]["score_updates"] == 1 assert "code-reviewer" in by_id["S2"]["agents_loaded"] def test_grade_distribution(fake_claude: Path) -> None: _write_sidecar(fake_claude, "a", {"slug": "a", "grade": "A", "raw_score": 0.9}) _write_sidecar(fake_claude, "b1", {"slug": "b1", "grade": "B", "raw_score": 0.7}) _write_sidecar(fake_claude, "b2", {"slug": "b2", "grade": "B", "raw_score": 0.6}) _write_sidecar(fake_claude, "f", {"slug": "f", "grade": "F", "raw_score": 0.1}) _write_mcp_sidecar(fake_claude, "mcp-one", { "slug": "mcp-one", "subject_type": "mcp-server", "grade": "C", "raw_score": 0.5, }) dist = mt.grade_distribution() assert dist["A"] == 1 assert dist["B"] == 2 assert dist["C"] == 1 assert dist["F"] == 1 def test_grade_distribution_skips_dotfiles_and_lifecycle(fake_claude: Path) -> None: _write_sidecar(fake_claude, "real", {"slug": "real", "grade": "C", "raw_score": 0.4}) (fake_claude / "skill-quality" / ".hook-state.json").write_text("{}", encoding="utf-8") (fake_claude / "skill-quality" / "real.lifecycle.json").write_text("{}", encoding="utf-8") dist = mt.grade_distribution() assert sum(dist.values()) == 1 # only "real.json" def test_session_detail_filters_by_session_id(fake_claude: Path) -> None: _write_audit(fake_claude, [ {"ts": "t1", "event": "skill.loaded", "subject_type": "skill", "subject": "x", "actor": "hook", "session_id": "A"}, {"ts": "t2", "event": "skill.loaded", "subject_type": "skill", "subject": "y", "actor": "hook", "session_id": "B"}, ]) _write_events(fake_claude, [ {"timestamp": "t3", "event": "load", "skill": "z", "session_id": "A"}, ]) detail = mt.session_detail("A") assert detail["session_id"] == "A" assert len(detail["audit_entries"]) == 1 assert detail["audit_entries"][0]["subject"] == "x" assert len(detail["load_events"]) == 1 assert detail["load_events"][0]["skill"] == "z" def test_render_home_has_grade_pills(fake_claude: Path) -> None: _write_sidecar(fake_claude, "s1", {"slug": "s1", "grade": "A", "raw_score": 0.9}) html = mt.render_home() assert "ctx monitor" in html assert "grade-A" in html assert "/sessions" in html def test_home_page_module_renders_stats_and_recent_activity() -> None: html = home_page.render_home( manifest={"load": [{"skill": "reviewer"}]}, sessions=[{ "session_id": "sess-1234567890", "last_seen": "2026-06-21T00:00:00Z", "skills_loaded": ["python"], "skills_unloaded": [], "agents_loaded": ["reviewer"], "score_updates": 2, }], wiki_stats={ "skills": 1000, "agents": 2, "mcps": 3, "harnesses": 4, "total": 1009, "split_known": True, }, graph_stats={"nodes": 1009, "edges": 2000}, runtime_summary={ "validations_total": 5, "validation_failures": 1, "open_escalations_total": 0, }, audit_lines=12, recent_audit=[{"ts": "2026-06-21T01:02:03Z", "event": "skill.loaded", "subject": "python"}], layout=lambda _title, body: body, format_count=lambda value: f"{value:,}", ) assert "ctx monitor" in html assert "1,009" in html assert "1,000 skills" in html assert "sess-1234567890" in html assert "skill.loaded" in html assert "home-sidecar-count" in html def test_render_session_detail_escapes_html(fake_claude: Path) -> None: hostile = "evil" _write_audit(fake_claude, [ {"ts": "t", "event": "skill.loaded", "subject_type": "skill", "subject": hostile, "actor": "hook", "session_id": "sess"}, ]) html = mt.render_session_detail("sess") assert "" not in html # HTML-escaped form must appear assert "</script>" in html or "<script>" in html def test_render_skills_sorts_grade_then_score(fake_claude: Path) -> None: _write_sidecar(fake_claude, "low", {"slug": "low", "grade": "D", "raw_score": 0.2}) _write_sidecar(fake_claude, "mid", {"slug": "mid", "grade": "B", "raw_score": 0.6}) _write_sidecar(fake_claude, "top", {"slug": "top", "grade": "A", "raw_score": 0.9}) html = mt.render_skills() # 'top' should appear before 'mid' before 'low' in the grade-sorted output idx_top = html.index("top") idx_mid = html.index("mid") idx_low = html.index("low") assert idx_top < idx_mid < idx_low def test_render_skills_includes_harness_filter_and_typed_links(fake_claude: Path) -> None: _write_sidecar(fake_claude, "langgraph-harness", { "slug": "langgraph", "subject_type": "harness", "grade": "A", "raw_score": 0.95, }) html = mt.render_skills() assert "class='type-filter'" in html assert "value='harness'" in html assert "/skill/langgraph?type=harness" in html assert "/wiki/langgraph?type=harness" in html assert "/graph?slug=langgraph&type=harness" in html def test_skills_page_module_renders_filters_cards_and_pagination() -> None: html_out = skills_page.render_skills( payload={ "items": [{ "slug": "reviewer", "grade": "A", "raw_score": 0.91, "subject_type": "agent", "hard_floor": "", }], "page": 2, "limit": 50, "total": 75, "catalog_total": 100, "filtered": True, "has_prev": True, "has_next": False, "pages": 2, "types": ["agent"], "grades": ["A"], "hide_floor": True, "q": "review", }, query_params={"q": "review", "type": "agent", "grade": "A", "hide_floor": "1"}, entity_types=("skill", "agent", "mcp-server", "harness"), layout=lambda _title, body: body, sidecar_entity_type=lambda sidecar: str(sidecar.get("subject_type", "skill")), ) assert "

Quality sidecars

" in html_out assert "Showing 51-75 of 75 matching sidecars" in html_out assert "class='skill-card'" in html_out assert "data-type='agent'" in html_out assert "class='type-filter'" in html_out assert "class='grade-filter'" in html_out assert "name='hide_floor' value='1' checked" in html_out assert "/skill/reviewer?type=agent" in html_out def test_read_manifest_empty_when_missing(fake_claude: Path) -> None: m = mt.read_manifest() assert m == {"load": [], "unload": [], "warnings": []} assert manifest_service.read_manifest( fake_claude / "skill-manifest.json", fake_claude, ) == m def test_read_manifest_reads_real_manifest(fake_claude: Path) -> None: (fake_claude / "skill-manifest.json").write_text( json.dumps({"load": [{"skill": "a"}], "unload": [{"skill": "b"}], "warnings": []}), encoding="utf-8", ) m = mt.read_manifest() direct = manifest_service.read_manifest( fake_claude / "skill-manifest.json", fake_claude, ) assert [e["skill"] for e in m["load"]] == ["a"] assert [e["skill"] for e in m["unload"]] == ["b"] assert direct == m def test_read_manifest_includes_installed_harness_records(fake_claude: Path) -> None: harness_dir = fake_claude / "harness-installs" harness_dir.mkdir() (harness_dir / "langgraph.json").write_text( json.dumps({ "slug": "langgraph", "status": "installed", "repo_url": "https://github.com/langchain-ai/langgraph", "target": str(fake_claude / "harnesses" / "langgraph"), "installed_at": "2026-05-01T00:00:00Z", }), encoding="utf-8", ) m = mt.read_manifest() direct = manifest_service.read_manifest( fake_claude / "skill-manifest.json", fake_claude, ) assert m["load"] == [{ "skill": "langgraph", "entity_type": "harness", "source": "ctx-harness-install", "command": str(fake_claude / "harnesses" / "langgraph"), "installed_at": "2026-05-01T00:00:00Z", "status": "installed", }] assert direct == m def test_queue_status_summarizes_worker_jobs(fake_claude: Path) -> None: wiki = fake_claude / "skill-wiki" db_path = wiki_queue.queue_db_path(wiki) first = wiki_queue.enqueue_maintenance_job( wiki, kind=wiki_queue.GRAPH_EXPORT_JOB, payload={"graph_only": True}, source="test", now=10.0, ) second = wiki_queue.enqueue_maintenance_job( wiki, kind=wiki_queue.TAR_REFRESH_JOB, payload={"catalog": "graph/skills-sh-catalog.json.gz"}, source="test", now=11.0, ) third = wiki_queue.enqueue_maintenance_job( wiki, kind=wiki_queue.CATALOG_REFRESH_JOB, payload={"catalog": "graph/skills-sh-catalog.json.gz"}, source="test", now=12.0, ) leased = wiki_queue.lease_next(db_path, worker_id="worker-a", now=12.0) assert leased is not None wiki_queue.mark_failed(db_path, leased.id, error="boom", retry=False, now=13.0) wiki_queue.cancel_job(db_path, third.id, reason="operator skipped", now=14.0) status = mt.queue_status() direct_status = status_service.queue_status(wiki) html_out = mt.render_status() assert status["available"] is True assert status["counts"] == { wiki_queue.STATUS_PENDING: 1, wiki_queue.STATUS_RUNNING: 0, wiki_queue.STATUS_SUCCEEDED: 0, wiki_queue.STATUS_FAILED: 1, wiki_queue.STATUS_CANCELLED: 1, } assert status["total"] == 3 assert [job["id"] for job in status["recent_jobs"]] == [third.id, second.id, first.id] assert status["recent_jobs"][0]["status"] == wiki_queue.STATUS_CANCELLED assert direct_status == status assert "cancelled: 1" in html_out def test_artifact_status_reads_promotion_metadata( fake_claude: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: import networkx as nx from ctx.core.graph.graph_packs import write_base_pack, write_overlay_pack from ctx.core.graph.graph_store import ensure_graph_store from ctx.core.wiki.wiki_packs import write_wiki_base_pack, write_wiki_overlay_pack graph_dir = fake_claude / "skill-wiki" / "graphify-out" graph_dir.mkdir(parents=True) graph = graph_dir / "graph.json" graph.write_text('{"nodes":[],"edges":[]}', encoding="utf-8") pack_graph = nx.Graph() pack_graph.add_node("skill:pack-skill", label="pack-skill", type="skill") write_base_pack( pack_dir=graph_dir / "packs" / "base-export-1", pack_id="base-export-1", base_export_id="export-1", config_hash="config-1", model_id="model-1", graph=pack_graph, ) write_overlay_pack( pack_dir=graph_dir / "packs" / "overlay-pack-skill", pack_id="overlay-pack-skill", base_export_id="export-1", parent_export_id="export-1", config_hash="config-1", model_id="model-1", nodes=[{"id": "skill:pack-skill", "label": "pack-skill", "type": "skill"}], edges=[], tombstones=[], ) write_wiki_base_pack( pack_dir=fake_claude / "skill-wiki" / "wiki-packs" / "base-export-1", pack_id="base-export-1", base_export_id="export-1", pages={"entities/skills/pack-skill.md": "# Pack Skill\n"}, ) write_wiki_overlay_pack( pack_dir=fake_claude / "skill-wiki" / "wiki-packs" / "overlay-pack-skill", pack_id="overlay-pack-skill", base_export_id="export-1", parent_export_id="export-1", pages={"entities/skills/pack-skill.md": "# Pack Skill\n\nUpdated.\n"}, tombstones=[], ) ensure_graph_store(graph_dir, graph_dir / "graph-store.sqlite3") repo_graph = tmp_path / "repo-graph" repo_graph.mkdir() (repo_graph / "wiki-graph.tar.gz").write_bytes(b"tar") runtime_catalog = ( fake_claude / "skill-wiki" / "external-catalogs" / "skills-sh" / "catalog.json" ) runtime_catalog.parent.mkdir(parents=True) runtime_catalog.write_text("{}", encoding="utf-8") monkeypatch.setattr(mt, "repo_graph_dir", lambda: repo_graph) (graph_dir / "graph.json.promotion.json").write_text( json.dumps({ "status": "promoted", "target": str(graph), "previous": {"sha256": "old", "size": 10}, "current": {"sha256": "new", "size": 22}, "promoted_at": "2026-05-04T00:00:00+00:00", }), encoding="utf-8", ) status = mt.artifact_status() direct_status = status_service.artifact_status( wiki_dir=fake_claude / "skill-wiki", claude_dir=fake_claude, repo_graph_dir=repo_graph, ) assert status["graph_json"]["exists"] is True assert status["graph_json"]["size"] == graph.stat().st_size assert status["graph_packs"]["exists"] is True assert status["graph_packs"]["pack_count"] == 2 assert status["graph_packs"]["base_count"] == 1 assert status["graph_packs"]["overlay_count"] == 1 assert status["wiki_packs"]["pack_count"] == 2 assert status["wiki_packs"]["base_count"] == 1 assert status["wiki_packs"]["overlay_count"] == 1 assert status["pack_compaction"]["needs_compaction"] is False assert status["pack_compaction"]["can_compact_now"] is True assert status["pack_compaction"]["max_overlay_count"] == 1 assert status["graph_store"]["exists"] is True assert status["graph_store"]["fresh"] is True assert status["graph_store"]["ok"] is True assert status["graph_store"]["nodes"] == 1 assert status["graph_store"]["edges"] == 0 assert status["wiki_graph_tar"]["path"] == str(repo_graph / "wiki-graph.tar.gz") assert status["skills_sh_catalog"]["path"] == str(runtime_catalog) assert status["promotion_count"] == 1 assert status["promotions"][0]["status"] == "promoted" assert status["promotions"][0]["current_sha256"] == "new" assert direct_status == status def test_status_page_and_api_show_queue_and_artifacts( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: wiki = fake_claude / "skill-wiki" wiki_queue.enqueue_maintenance_job( wiki, kind=wiki_queue.GRAPH_EXPORT_JOB, payload={"graph_only": True}, source="test", now=10.0, ) html_out = mt.render_status() assert "Queue state" in html_out assert "Artifact versions" in html_out assert "graph packs" in html_out assert "graph-store.sqlite3" in html_out assert "wiki packs" in html_out assert "pack compaction" in html_out assert "packs: 0 (base 0, overlay 0)" in html_out assert "compaction: not needed, 0 overlays / threshold" in html_out assert "store: stale or missing, 0 nodes, 0 edges" in html_out assert wiki_queue.GRAPH_EXPORT_JOB in html_out server, _thread, port = _serve_monitor(monkeypatch) try: with urllib.request.urlopen( f"http://127.0.0.1:{port}/api/status.json", timeout=5, ) as response: payload = json.loads(response.read().decode("utf-8")) assert payload["queue"]["total"] == 1 assert payload["artifacts"]["graph_json"]["path"].endswith("graph.json") finally: server.shutdown() server.server_close() def test_status_page_shows_queue_db_errors( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: db_path = wiki_queue.queue_db_path(fake_claude / "skill-wiki") db_path.parent.mkdir(parents=True) db_path.write_bytes(b"exists") def fail_count_jobs_by_status(_db_path: Path) -> dict[str, int]: raise RuntimeError("cannot read queue") monkeypatch.setattr( mt.wiki_queue, "count_jobs_by_status", fail_count_jobs_by_status, ) status = mt.queue_status() html_out = mt.render_status() assert status["available"] is False assert status["error"] == "cannot read queue" assert "Queue DB error" in html_out assert "cannot read queue" in html_out assert "Durable worker DB: error" in html_out def test_render_loaded_shows_manifest_entries(fake_claude: Path) -> None: (fake_claude / "skill-manifest.json").write_text( json.dumps({ "load": [{"skill": "python-patterns", "source": "user-approved", "priority": 7, "reason": "fuzzy match"}], "unload": [{"skill": "old-skill", "source": "stale"}], "warnings": [], }), encoding="utf-8", ) html = mt.render_loaded() assert "python-patterns" in html assert "old-skill" in html # Action buttons must be present for each row. assert "btn-unload" in html assert "btn-load" in html # Navigation must include new pages. assert "/loaded" in html assert "/logs" in html def test_render_loaded_shows_harness_install_without_unload_button(fake_claude: Path) -> None: harness_dir = fake_claude / "harness-installs" harness_dir.mkdir() (harness_dir / "langgraph.json").write_text( json.dumps({"slug": "langgraph", "status": "installed"}), encoding="utf-8", ) html = mt.render_loaded() assert "langgraph" in html assert "ctx-harness-install langgraph --uninstall --dry-run" in html assert "data-slug='langgraph'" not in html def test_loaded_page_module_renders_read_only_state() -> None: html = loaded_page.render_loaded( { "load": [{"skill": "reviewer", "entity_type": "agent", "source": "manual"}], "unload": [{"skill": "old-skill", "entity_type": "skill", "reason": "stale"}], }, mutations_enabled=False, monitor_token="secret-token", layout=lambda _title, body: body, ) assert "Read-only mode" in html assert "reviewer" in html assert "old-skill" in html assert "btn-unload" in html assert " disabled" in html assert "secret-token" not in html def test_runtime_lifecycle_summary_reads_validation_and_escalation_events( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: events = tmp_path / "runtime" / "events.jsonl" monkeypatch.setattr(mt, "runtime_lifecycle_path", lambda: events) _write_runtime_events(events, [ { "action": "validation", "session_id": "s-1", "check_name": "pytest", "status": "passed", "created_at": "2026-05-08T01:00:00Z", }, { "action": "validation", "session_id": "s-1", "check_name": "mypy", "status": "failed", "summary": "type gate failed", "created_at": "2026-05-08T01:05:00Z", }, { "action": "escalation", "session_id": "s-1", "trigger": "validation-failed", "reason": "mypy failed after retry", "status": "open", "severity": "blocking", "created_at": "2026-05-08T01:06:00Z", }, { "action": "escalation", "session_id": "s-2", "trigger": "user-review", "reason": "review completed", "status": "resolved", "severity": "info", "created_at": "2026-05-08T01:07:00Z", }, ]) summary = mt.runtime_lifecycle_summary() direct_summary = runtime_service.lifecycle_summary(events) assert summary["validations_total"] == 2 assert summary["validation_failures"] == 1 assert summary["open_escalations_total"] == 1 assert summary["latest_validation"]["check_name"] == "mypy" assert summary["open_escalations"][0]["trigger"] == "validation-failed" assert direct_summary == summary def test_runtime_lifecycle_summary_uses_full_history_for_open_state( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: events = tmp_path / "runtime" / "events.jsonl" monkeypatch.setattr(mt, "runtime_lifecycle_path", lambda: events) records = [{ "action": "escalation", "session_id": "s-1", "trigger": "validation-failed", "reason": "pytest failed", "status": "open", "severity": "blocking", "created_at": "2026-05-08T00:00:00Z", }] records.extend({ "action": "validation", "session_id": "s-1", "check_name": f"check-{idx}", "status": "passed", "created_at": f"2026-05-08T01:{idx % 60:02d}:00Z", } for idx in range(201)) _write_runtime_events(events, records) summary = mt.runtime_lifecycle_summary() assert summary["validations_total"] == 201 assert summary["open_escalations_total"] == 1 assert summary["open_escalations"][0]["trigger"] == "validation-failed" records.append({ "action": "escalation", "session_id": "s-1", "trigger": "validation-failed", "reason": "pytest failed", "status": "resolved", "severity": "blocking", "created_at": "2026-05-08T02:00:00Z", }) _write_runtime_events(events, records) summary = mt.runtime_lifecycle_summary() assert summary["open_escalations_total"] == 0 assert summary["escalations_total"] == 2 def test_render_runtime_lifecycle_surfaces_checks_and_open_escalations( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: events = tmp_path / "runtime" / "events.jsonl" monkeypatch.setattr(mt, "runtime_lifecycle_path", lambda: events) _write_runtime_events(events, [ { "action": "validation", "session_id": "s-1", "check_name": "mypy", "status": "failed", "summary": "", "created_at": "2026-05-08T01:05:00Z", }, { "action": "escalation", "session_id": "s-1", "trigger": "validation-failed", "reason": "", "status": "open", "severity": "blocking", "created_at": "2026-05-08T01:06:00Z", }, ]) html = mt.render_runtime_lifecycle() assert "Runtime lifecycle" in html assert "mypy" in html assert "validation-failed" in html assert "<type gate failed>" in html assert "<mypy failed>" in html def test_render_logs_filters_and_renders(fake_claude: Path) -> None: _write_audit(fake_claude, [ {"ts": "t1", "event": "skill.loaded", "subject_type": "skill", "subject": "s1", "actor": "hook", "session_id": "sess"}, {"ts": "t2", "event": "skill.score_updated", "subject_type": "skill", "subject": "s1", "actor": "hook", "session_id": "sess"}, ]) html = mt.render_logs() assert "skill.loaded" in html assert "skill.score_updated" in html # Filter input must be wired. assert "id='filter'" in html def test_perform_load_rejects_invalid_slug() -> None: ok, msg = mt.perform_load("../etc/passwd") assert ok is False assert "invalid slug" in msg helper_ok, helper_msg = dashboard_entities.perform_load( "../etc/passwd", deps=mt.entity_runtime_deps(), ) assert (helper_ok, helper_msg) == (ok, msg) def test_perform_load_runs_skill_security_scan_and_surfaces_output( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: from ctx.adapters.claude_code.install import skill_install calls: list[bool] = [] class Scan: output = "SkillSpector found no issues" class Result: status = "installed" message = "SkillSpector: passed" security_scan = Scan() def fake_install_skill(*args: object, **kwargs: object) -> Result: calls.append(bool(kwargs.get("security_scan"))) return Result() monkeypatch.setattr(skill_install, "install_skill", fake_install_skill) ok, msg = mt.perform_load("python-patterns", entity_type="skill") assert ok is True assert calls == [True] assert "SkillSpector: passed" in msg assert "SkillSpector report:" in msg assert "found no issues" in msg def test_perform_unload_rejects_invalid_slug() -> None: ok, msg = mt.perform_unload("../../hostile") assert ok is False assert "invalid slug" in msg def test_load_sidecar_rejects_unsafe_slug(fake_claude: Path) -> None: _write_sidecar(fake_claude, "python-patterns", {"slug": "python-patterns"}) assert mt.load_sidecar("../python-patterns") is None def test_load_sidecar_reads_mcp_quality_subdir(fake_claude: Path) -> None: _write_mcp_sidecar(fake_claude, "filesystem", { "slug": "filesystem", "grade": "A", "raw_score": 0.91, }) sidecar = mt.load_sidecar("filesystem") assert sidecar is not None assert sidecar["subject_type"] == "mcp-server" def test_skill_sidecar_api_reads_typed_sidecar( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: _write_mcp_sidecar(fake_claude, "filesystem", { "slug": "filesystem", "grade": "A", "raw_score": 0.91, }) server, thread, port = _serve_monitor(monkeypatch) try: status, payload = _get_json(port, "/api/skill/filesystem.json?type=mcp-server") finally: server.shutdown() server.server_close() thread.join(timeout=2) assert status == 200 assert payload["slug"] == "filesystem" assert payload["subject_type"] == "mcp-server" def test_load_sidecar_can_disambiguate_duplicate_slug(fake_claude: Path) -> None: _write_sidecar(fake_claude, "langgraph", { "slug": "langgraph", "subject_type": "skill", "grade": "D", }) harness_sidecar = { "slug": "langgraph", "subject_type": "harness", "grade": "A", } # Dashboard sidecars are flat for non-MCP entity types; duplicate slugs # are disambiguated by the subject_type inside the sidecar. (fake_claude / "skill-quality" / "langgraph-harness.json").write_text( json.dumps(harness_sidecar), encoding="utf-8", ) skill_sidecar = mt.load_sidecar("langgraph", entity_type="skill") assert skill_sidecar is not None assert skill_sidecar["grade"] == "D" harness = mt.load_sidecar("langgraph", entity_type="harness") assert harness is not None assert harness["grade"] == "A" def test_load_sidecar_typed_miss_does_not_build_global_index( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: _write_sidecar(fake_claude, "unrelated", {"slug": "unrelated"}) monkeypatch.setattr( sidecar_service, "sidecar_index", lambda *args, **kwargs: ( _ for _ in () ).throw(AssertionError("cold full sidecar scan")), ) assert mt.load_sidecar("missing", entity_type="skill") is None def test_monitor_post_requires_token( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: server, thread, port = _serve_monitor(monkeypatch) try: status, body = _post_json(port, "/api/load", {"slug": "python-patterns"}) assert status == 403 assert "token" in body["detail"] finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_monitor_post_accepts_valid_token( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: direct_calls: list[tuple[str, str, dict[str, str]]] = [] def direct_load( slug: str, entity_type: str, kwargs: dict[str, str], ) -> tuple[bool, str]: direct_calls.append((slug, entity_type, kwargs)) return True, "loaded" direct = mutations_api.handle_mutation_route( "api_load", {"slug": "python-patterns", "entity_type": "agent"}, mutations_api.MutationApiDeps( perform_load=direct_load, perform_unload=lambda _slug, _type: (False, "unused"), save_config_updates=lambda _updates: {"ok": False, "detail": "unused"}, upsert_wiki_entity=lambda _body: (False, "unused"), delete_wiki_entity=lambda _slug, _type: (False, "unused"), ), ) assert direct is not None assert direct.status == 200 assert direct.payload == {"ok": True, "detail": "loaded"} assert direct_calls == [("python-patterns", "agent", {})] calls: list[tuple[str, str]] = [] def fake_load(slug: str, entity_type: str = "skill") -> tuple[bool, str]: calls.append((slug, entity_type)) return True, "loaded" monkeypatch.setattr(mt, "perform_load", fake_load) server, thread, port = _serve_monitor(monkeypatch) try: status, body = _post_json( port, "/api/load", {"slug": "python-patterns", "entity_type": "agent"}, token="test-token", ) assert status == 200 assert body == {"ok": True, "detail": "loaded"} assert calls == [("python-patterns", "agent")] finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_monitor_post_unload_accepts_valid_token_without_mocking_runtime( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: manifest_path = fake_claude / "skill-manifest.json" manifest_path.write_text( json.dumps({ "load": [{"skill": "code-reviewer", "entity_type": "agent", "source": "test"}], "unload": [], "warnings": [], }), encoding="utf-8", ) server, thread, port = _serve_monitor(monkeypatch) try: status, body = _post_json( port, "/api/unload", {"slug": "code-reviewer", "entity_type": "agent"}, token="test-token", ) finally: server.shutdown() server.server_close() thread.join(timeout=2) assert status == 200 assert body == {"ok": True, "detail": "unloaded code-reviewer"} updated = json.loads(manifest_path.read_text(encoding="utf-8")) assert updated["load"] == [] assert updated["unload"] == [ {"skill": "code-reviewer", "entity_type": "agent", "source": "test"}, ] def test_monitor_load_forwards_mcp_command_and_json_config( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: calls: list[tuple[str, str, dict[str, str]]] = [] def fake_load( slug: str, entity_type: str = "skill", **kwargs: str, ) -> tuple[bool, str]: calls.append((slug, entity_type, kwargs)) return True, "loaded mcp" monkeypatch.setattr(mt, "perform_load", fake_load) server, thread, port = _serve_monitor(monkeypatch) try: status, body = _post_json( port, "/api/load", { "slug": "github", "entity_type": "mcp-server", "command": "npx -y @modelcontextprotocol/server-github", "json_config": '{"env":{"GITHUB_TOKEN":"${GITHUB_TOKEN}"}}', }, token="test-token", ) assert status == 200 assert body == {"ok": True, "detail": "loaded mcp"} assert calls == [ ( "github", "mcp-server", { "command": "npx -y @modelcontextprotocol/server-github", "json_config": '{"env":{"GITHUB_TOKEN":"${GITHUB_TOKEN}"}}', }, ) ] finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_monitor_post_rejects_cross_origin_with_valid_token( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: calls: list[str] = [] def fake_load(slug: str, entity_type: str = "skill") -> tuple[bool, str]: calls.append(slug) return True, f"loaded {entity_type}" monkeypatch.setattr(mt, "perform_load", fake_load) server, thread, port = _serve_monitor(monkeypatch) body = json.dumps({"slug": "python-patterns"}).encode("utf-8") try: status, payload = _post_raw( port, "/api/load", headers={ "Content-Type": "application/json", "Content-Length": str(len(body)), "X-CTX-Monitor-Token": "test-token", "Origin": "http://evil.example", }, body=body, ) assert status == 403 assert "cross-origin" in payload["detail"] assert calls == [] finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_monitor_post_rejects_rebound_host_with_valid_token( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: calls: list[str] = [] def fake_load(slug: str, entity_type: str = "skill") -> tuple[bool, str]: calls.append(slug) return True, f"loaded {entity_type}" monkeypatch.setattr(mt, "perform_load", fake_load) server, thread, port = _serve_monitor(monkeypatch) body = json.dumps({"slug": "python-patterns"}).encode("utf-8") try: status, payload = _post_raw( port, "/api/load", headers={ "Host": "evil.example", "Content-Type": "application/json", "Content-Length": str(len(body)), "X-CTX-Monitor-Token": "test-token", "Origin": "http://evil.example", }, body=body, ) assert status == 403 assert "cross-origin" in payload["detail"] assert calls == [] finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_monitor_get_rejects_rebound_host_in_loopback_mode( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: server, thread, port = _serve_monitor(monkeypatch) try: status, payload = _get_raw( port, "/api/status.json", headers={"Host": "evil.example"}, ) assert status == 403 assert "monitor read" in payload["detail"] finally: server.shutdown() server.server_close() thread.join(timeout=2) @pytest.mark.parametrize( ("length", "status", "detail"), [ ("nope", 400, "invalid Content-Length"), ("-1", 400, "invalid Content-Length"), (str(mt.MAX_POST_BODY_BYTES + 1), 413, "too large"), ], ) def test_monitor_post_rejects_bad_content_length_before_body_read( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, length: str, status: int, detail: str, ) -> None: server, thread, port = _serve_monitor(monkeypatch) try: code, payload = _post_raw( port, "/api/load", headers={ "Content-Type": "application/json", "Content-Length": length, "X-CTX-Monitor-Token": "test-token", }, ) assert code == status assert detail in payload["detail"] finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_monitor_post_rejects_non_object_json_body( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: server, thread, port = _serve_monitor(monkeypatch) body = b"[]" try: status, payload = _post_raw( port, "/api/load", headers={ "Content-Type": "application/json", "Content-Length": str(len(body)), "X-CTX-Monitor-Token": "test-token", }, body=body, ) assert status == 400 assert "object" in payload["detail"] finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_monitor_non_loopback_bind_is_read_only( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: calls: list[str] = [] def fake_load(slug: str, entity_type: str = "skill") -> tuple[bool, str]: calls.append(slug) return True, f"loaded {entity_type}" (fake_claude / "skill-manifest.json").write_text( json.dumps({"load": [], "unload": [], "warnings": []}), encoding="utf-8", ) monkeypatch.setattr(mt, "perform_load", fake_load) server, thread, port = _serve_monitor( monkeypatch, token="browser-token", host="0.0.0.0", ) try: with pytest.raises(urllib.error.HTTPError) as excinfo: urllib.request.urlopen(f"http://127.0.0.1:{port}/loaded", timeout=5) assert excinfo.value.code == 403 assert "monitor read token required" in excinfo.value.read().decode("utf-8") with urllib.request.urlopen( f"http://127.0.0.1:{port}/loaded?token=browser-token", timeout=5, ) as response: loaded_html = response.read().decode("utf-8") csp = response.headers.get("Content-Security-Policy", "") cookie = response.headers.get("Set-Cookie", "") assert "browser-token" not in loaded_html assert "Read-only mode" in loaded_html assert "script-src 'self' 'unsafe-inline'" in csp assert "ctx_monitor_read_token=browser-token" in cookie with pytest.raises(urllib.error.HTTPError) as excinfo: urllib.request.urlopen( f"http://127.0.0.1:{port}/api/manifest.json", timeout=5, ) assert excinfo.value.code == 403 body = json.loads(excinfo.value.read().decode("utf-8")) assert "read token required" in body["detail"] status, body = _get_raw( port, "/api/manifest.json", headers={ "Host": f"127.0.0.1:{port}", "Cookie": "ctx_monitor_read_token=browser-token", }, ) assert status == 200 assert body["load"] == [] status, body = _post_json( port, "/api/load", {"slug": "python-patterns"}, token="browser-token", ) assert status == 403 assert "disabled" in body["detail"] assert calls == [] finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_serve_generates_read_token_for_non_loopback( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], ) -> None: class FakeServer: _ctx_mutations_enabled = False def serve_forever(self) -> None: raise KeyboardInterrupt def server_close(self) -> None: return None monkeypatch.setattr(mt, "MONITOR_TOKEN", "") monkeypatch.setattr(mt, "make_monitor_server", lambda _host, _port: FakeServer()) monkeypatch.setattr(monitor_cli.secrets, "token_urlsafe", lambda _size: "lan-token") monkeypatch.setattr(monitor_cli.socket, "gethostname", lambda: "devbox") monkeypatch.setattr(monitor_cli.socket, "gethostbyname", lambda _name: "192.168.1.50") mt.serve(host="0.0.0.0", port=8765) assert mt.MONITOR_TOKEN == "lan-token" out = capsys.readouterr().out assert "http://192.168.1.50:8765/?token=lan-token" in out assert "http://0.0.0.0:8765" not in out assert "read token required" in out def test_host_allows_mutations_only_for_loopback() -> None: assert mt.host_allows_mutations("127.0.0.1") assert mt.host_allows_mutations("::1") assert mt.host_allows_mutations("localhost") assert not mt.host_allows_mutations("0.0.0.0") assert not mt.host_allows_mutations("::") assert not mt.host_allows_mutations("example.com") def test_monitor_route_inventory_covers_nav_and_api_routes() -> None: nav_hrefs = {href for _key, _label, href in monitor_routes.NAV_ROUTES} assert { "/", "/loaded", "/skills", "/skillspector", "/wiki", "/graph", "/manage", "/harness", "/docs", "/config", "/status", "/kpi", "/runtime", "/sessions", "/logs", "/events", } <= nav_hrefs assert nav_hrefs <= monitor_routes.PAGE_ROUTES assert "/catalog" in monitor_routes.PAGE_ROUTES assert "/api/graph/.json" in monitor_routes.GET_API_PATTERNS assert "/api/entities/search.json" in monitor_routes.GET_API_ROUTES assert "/api/entity/upsert" in monitor_routes.POST_API_ROUTES def test_monitor_route_matchers_cover_dynamic_and_mutation_routes() -> None: parsed = monitor_routes.parse_request_target( "/api/graph/github%2Fcli.json?type=mcp-server&limit=20", ) assert parsed.path == "/api/graph/github%2Fcli.json" assert parsed.query == {"type": "mcp-server", "limit": "20"} home = monitor_routes.match_get_route("/") catalog = monitor_routes.match_get_route("/catalog/") wiki_entity = monitor_routes.match_get_route("/wiki/langgraph") graph_api = monitor_routes.match_get_route("/api/graph/github%2Fcli.json") assert home is not None and home.name == "home" assert catalog is not None and catalog.name == "wiki_index" assert wiki_entity is not None and wiki_entity.params == {"slug": "langgraph"} assert graph_api is not None and graph_api.params == {"slug": "github/cli"} assert monitor_routes.match_get_route("/missing") is None entity_upsert = monitor_routes.match_post_route("/api/entity/upsert") assert entity_upsert is not None and entity_upsert.name == ( "api_entity_upsert" ) assert monitor_routes.match_post_route("/api/nope") is None def test_graph_api_invalid_params_return_400( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: server, thread, port = _serve_monitor(monkeypatch) try: with pytest.raises(urllib.error.HTTPError) as excinfo: urllib.request.urlopen( f"http://127.0.0.1:{port}/api/graph/langgraph.json?hops=abc", timeout=5, ) assert excinfo.value.code == 400 body = json.loads(excinfo.value.read().decode("utf-8")) assert body["detail"] == "hops and limit must be integers" finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_parse_frontmatter_basic() -> None: text = "---\nname: python-patterns\nuse_count: 3\n---\n# Body\n\nhello" meta, body = mt.parse_frontmatter(text) assert meta == {"name": "python-patterns", "use_count": "3"} assert body.startswith("# Body") def test_parse_frontmatter_missing_returns_empty_meta() -> None: text = "# No frontmatter\n\nBody only." meta, body = mt.parse_frontmatter(text) assert meta == {} assert body == text def test_parse_frontmatter_multiline_tags() -> None: text = "---\ntags:\n - python\n - api\n---\n# Body\n" meta, body = mt.parse_frontmatter(text) assert meta["tags"] == ["python", "api"] assert body == "# Body" def test_wiki_entity_path_rejects_unsafe_slug(fake_claude: Path) -> None: assert mt.wiki_entity_path("../../etc/passwd") is None assert mt.wiki_entity_path("path/with/slash") is None assert mt.wiki_entity_path("con.txt") is None # Absent slug returns None but doesn't raise. assert mt.wiki_entity_path("no-such-skill") is None @pytest.mark.parametrize("slug", ["python-patterns", "mcp.v2", "0-service"]) def test_monitor_slug_validator_accepts_safe_values(slug: str) -> None: assert mt.is_safe_slug(slug) @pytest.mark.parametrize("slug", ["con.txt", "nul.", "COM1", "LPT9.ini"]) def test_monitor_slug_validator_rejects_windows_reserved_names(slug: str) -> None: assert not mt.is_safe_slug(slug) def test_monitor_sse_stream_does_not_block_json_requests( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: _write_audit(fake_claude, [ {"ts": "t", "event": "skill.loaded", "subject": "python-patterns", "session_id": "s1"}, ]) server, thread, port = _serve_monitor(monkeypatch) stream = urllib.request.urlopen( f"http://127.0.0.1:{port}/api/events.stream", timeout=2 ) try: with urllib.request.urlopen( f"http://127.0.0.1:{port}/api/sessions.json", timeout=2, ) as response: assert response.status == 200 body = json.loads(response.read().decode("utf-8")) assert body[0]["session_id"] == "s1" finally: stream.close() server.shutdown() server.server_close() thread.join(timeout=2) def test_monitor_shutdown_signals_open_sse_workers( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: server, thread, port = _serve_monitor(monkeypatch) stream = urllib.request.urlopen( f"http://127.0.0.1:{port}/api/events.stream", timeout=2 ) try: server.shutdown() thread.join(timeout=2) assert not thread.is_alive() assert server._ctx_shutdown.is_set() finally: stream.close() server.server_close() def test_render_events_shows_recent_audit_backlog(fake_claude: Path) -> None: _write_audit(fake_claude, [ {"ts": "t1", "event": "skill.loaded", "subject": "python-patterns"}, {"ts": "t2", "event": "agent.loaded", "subject": "repo-reviewer"}, ]) html_out = mt.render_events() assert "Showing last 2 audit events" in html_out assert "skill.loaded" in html_out assert "python-patterns" in html_out assert "agent.loaded" in html_out assert "repo-reviewer" in html_out assert "id='stream-status'" in html_out def test_live_alias_renders_events_page( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: _write_audit(fake_claude, [ {"ts": "t1", "event": "skill.loaded", "subject": "python-patterns"}, ]) server, thread, port = _serve_monitor(monkeypatch) try: with urllib.request.urlopen(f"http://127.0.0.1:{port}/live", timeout=5) as response: html_out = response.read().decode("utf-8") assert response.status == 200 assert "Live events" in html_out assert "python-patterns" in html_out finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_wiki_entity_path_finds_skill_page(fake_claude: Path) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) target = skills_dir / "python-patterns.md" target.write_text("---\nname: python-patterns\n---\n# body\n", encoding="utf-8") assert mt.wiki_entity_path("python-patterns") == target def test_render_wiki_entity_with_real_page(fake_claude: Path) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) (skills_dir / "python-patterns.md").write_text( "---\nname: python-patterns\nuse_count: 2\n---\n# Python patterns\n\nBody text.", encoding="utf-8", ) html_out = mt.render_wiki_entity("python-patterns") assert "python-patterns" in html_out assert "Python patterns" in html_out or "Body text" in html_out assert "use_count" in html_out # frontmatter table assert "class='card wiki-body'" in html_out direct_html = wiki_page.render_wiki_entity_page( slug="python-patterns", entity_type="skill", meta={"name": "python-patterns", "use_count": 2}, md_body="# Python patterns\n\nBody text.", sidecar={"grade": "A", "raw_score": 0.9}, dashboard_entity_types=mt.DASHBOARD_ENTITY_TYPES, display_slug=mt.display_slug, frontmatter_text=mt.frontmatter_text, truncate_text=mt.truncate_text, extract_embedded_quality_block=mt.extract_embedded_quality_block, strip_duplicate_wiki_heading=mt.strip_duplicate_wiki_heading, render_entity_subgraph=lambda _slug, _entity_type: "
subgraph
", render_entity_tabs=mt.render_entity_tabs, render_quality_drilldown=mt.render_quality_drilldown, render_wiki_markdown=mt.render_wiki_markdown, layout=lambda _title, body: body, ) assert "Python patterns" in direct_html or "Body text" in direct_html assert "grade-A" in direct_html def test_render_wiki_entity_wrapper_matches_extracted_orchestration( fake_claude: Path, ) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) (skills_dir / "python-patterns.md").write_text( "---\nname: python-patterns\ntype: skill\nuse_count: 2\n---\n" "# Python patterns\n\nBody text.", encoding="utf-8", ) wrapper_html = mt.render_wiki_entity("python-patterns", entity_type="skill") direct_html = wiki_page.render_wiki_entity( "python-patterns", entity_type="skill", entity_path=mt.wiki_entity_path, read_entity_text=mt.read_wiki_entity_text, parse_frontmatter=mt.parse_frontmatter, load_sidecar=mt.load_sidecar, render_runtime_graph_entity=mt.render_runtime_graph_entity, dashboard_entity_types=mt.DASHBOARD_ENTITY_TYPES, display_slug=mt.display_slug, frontmatter_text=mt.frontmatter_text, truncate_text=mt.truncate_text, extract_embedded_quality_block=mt.extract_embedded_quality_block, strip_duplicate_wiki_heading=mt.strip_duplicate_wiki_heading, render_entity_subgraph=mt.render_entity_subgraph, render_entity_tabs=mt.render_entity_tabs, render_quality_drilldown=mt.render_quality_drilldown, render_wiki_markdown=mt.render_wiki_markdown, layout=mt.layout, ) assert wrapper_html == direct_html assert "Body text" in wrapper_html def test_render_wiki_entity_renders_markdown_and_wikilinks(fake_claude: Path) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) (skills_dir / "markdown-page.md").write_text( "---\n" "type: skill\n" "tags: [python, testing]\n" "---\n" "# Markdown Page\n\n" "Use `pytest` with [[entities/skills/find-skills]].\n\n" "Source: [Homepage](https://example.com/docs).\n\n" "Unsafe: [bad](javascript:alert(1)).\n\n" "Protocol relative: [offsite](//evil.example/x).\n\n" "- first item\n" "- [[entities/agents/reviewer|reviewer agent]]\n", encoding="utf-8", ) html_out = mt.render_wiki_entity("markdown-page", entity_type="skill") markdown_out = wiki_page.render_wiki_markdown( "Use `pytest` with [[entities/skills/find-skills]].\n\n" "Source: [Homepage](https://example.com/docs).", wiki_link_href=mt.wiki_link_href, ) assert "

Markdown Page

" not in html_out assert "pytest" in html_out assert "pytest" in markdown_out assert "href='/wiki/find-skills?type=skill'" in markdown_out assert "href='/wiki/find-skills?type=skill'" in html_out assert "href='/wiki/reviewer?type=agent'" in html_out assert "Homepage" in html_out assert "href='javascript:alert(1)'" not in html_out assert "href='//evil.example/x'" not in html_out assert "
  • first item
  • " in html_out assert "
    Subgraph" in html_out
        assert "data-testid='entity-subgraph-graph'" in html_out
        assert "data-testid='entity-subgraph-3d'" in html_out
        assert "data-testid=\"entity-subgraph-node\"" in html_out
        assert "data-testid=\"entity-subgraph-edge\"" in html_out
        assert "id='entity-subgraph-zoom-in'" in html_out
        assert "id='entity-subgraph-zoom-out'" in html_out
        assert "drag to rotate" in html_out
        assert "wheel to zoom" in html_out
        assert "data-testid='entity-subgraph-node-detail'" in html_out
        assert "data-testid='entity-subgraph-edge-detail'" in html_out
        assert "Open interactive graph view" not in html_out
        assert "href='/wiki/github-actions?type=skill'" in html_out
        assert "href='/wiki/repo-reviewer?type=agent'" in html_out
        assert "grade-A" in html_out
        assert "grade-C" in html_out
        assert "needs-usage" in html_out
        assert "

    Quality

    " in html_out assert "freshness" in html_out assert "No signal breakdown was recorded" in direct_quality assert "has_install" in html_out assert "missing-license" in html_out assert "
     None:
        entity_dir = fake_claude / "skill-wiki" / "entities" / subdir
        entity_dir.mkdir(parents=True)
        (entity_dir / f"{slug}.md").write_text(
            "---\n"
            f"type: {entity_type}\n"
            "---\n"
            f"# {slug}\n\n"
            "Overview body stays here.\n\n"
            "\n"
            "## Quality\n\n"
            "- **Grade:** C\n"
            "- **Score:** 0.45 (raw 0.45)\n"
            "- **Computed:** 2026-05-09T15:47:33+00:00\n\n"
            "| Signal | Score | Weight |\n"
            "| --- | --- | --- |\n"
            "| telemetry | 0.45 | 0.40 |\n"
            "\n\n"
            "Overview continues here.\n",
            encoding="utf-8",
        )
    
        html_out = mt.render_wiki_entity(slug, entity_type=entity_type)
    
        overview_start = html_out.index("data-entity-tab-panel='overview'")
        overview_end = html_out.index("data-entity-tab-panel='subgraph'")
        overview_html = html_out[overview_start:overview_end]
        quality_start = html_out.index("data-entity-tab-panel='quality'")
        quality_html = html_out[quality_start:]
    
        assert "data-entity-tab='overview'" in html_out
        assert "data-entity-tab='quality'" in html_out
        assert "Overview body stays here." in overview_html
        assert "Overview continues here." in overview_html
        assert "quality:begin" not in html_out
        assert "quality:end" not in html_out
        assert "**Grade:** C" not in overview_html
        assert "**Score:** 0.45" not in overview_html
        assert "Grade:" in quality_html
        assert "0.45 (raw 0.45)" in quality_html
        assert "No quality sidecar exists" not in quality_html
    
    
    def test_render_wiki_entity_missing_slug(fake_claude: Path) -> None:
        out = mt.render_wiki_entity("nope-not-here")
        assert "No wiki page" in out
    
    
    def test_render_wiki_entity_falls_back_to_runtime_graph_metadata(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setattr(mt, "MONITOR_MUTATIONS_ENABLED", True)
        monkeypatch.setattr(mt, "MONITOR_TOKEN", "runtime-token")
    
        def fake_graph(slug: str, **_kwargs) -> dict:
            assert slug == "github"
            return {
                "center": "mcp-server:github",
                "nodes": [{
                    "data": {
                        "id": "mcp-server:github",
                        "label": "GitHub",
                        "type": "mcp-server",
                        "tags": ["git", "issues", "pull-requests"],
                        "description": "Manage repositories, issues, and pull requests.",
                        "quality_score": 0.81,
                        "usage_score": 0.42,
                        "degree": 27,
                    },
                }],
                "edges": [],
            }
    
        monkeypatch.setattr(mt, "graph_neighborhood", fake_graph)
    
        html_out = mt.render_wiki_entity("github", entity_type="mcp-server")
    
        assert "Runtime graph entity" in html_out
        assert "Manage repositories, issues, and pull requests." in html_out
        assert "git" in html_out
        assert "0.810" in html_out
        assert "ctx-init --graph --graph-install-mode full" in html_out
        assert "data-entity-tab='overview'" in html_out
        assert "data-entity-tab='subgraph'" in html_out
        assert "data-entity-tab='quality'" in html_out
        assert "data-testid='runtime-entity-load'" in html_out
        assert "/api/load" in html_out
        assert "X-CTX-Monitor-Token" in html_out
        assert "payload.detail || payload.msg || response.status" in html_out
    
    
    def test_runtime_graph_entity_wrapper_matches_extracted_wiki_renderer(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setattr(mt, "MONITOR_MUTATIONS_ENABLED", True)
        monkeypatch.setattr(mt, "MONITOR_TOKEN", "runtime-token")
    
        def fake_graph(slug: str, **_kwargs) -> dict:
            assert slug == "github"
            return {
                "center": "mcp-server:github",
                "nodes": [{
                    "data": {
                        "id": "mcp-server:github",
                        "label": "GitHub",
                        "type": "mcp-server",
                        "tags": ["git"],
                        "description": "Manage GitHub.",
                        "quality_score": 0.81,
                        "usage_score": 0.42,
                        "degree": 27,
                    },
                }],
                "edges": [],
            }
    
        monkeypatch.setattr(mt, "graph_neighborhood", fake_graph)
    
        wrapper_html = mt.render_runtime_graph_entity("github", entity_type="mcp-server")
        direct_html = wiki_page.render_runtime_graph_entity(
            "github",
            entity_type="mcp-server",
            monitor_mutations_enabled=mt.MONITOR_MUTATIONS_ENABLED,
            monitor_token=mt.MONITOR_TOKEN,
            normalize_dashboard_entity_type=mt.normalize_dashboard_entity_type,
            graph_neighborhood=mt.graph_neighborhood,
            graph_slug_from_node_id=mt.graph_slug_from_node_id,
            graph_type_from_node_id=mt.graph_type_from_node_id,
            display_label=mt.display_label,
            display_slug=mt.display_slug,
            load_sidecar=mt.load_sidecar,
            render_quality_drilldown=mt.render_quality_drilldown,
            render_entity_subgraph=mt.render_entity_subgraph,
            render_entity_tabs=mt.render_entity_tabs,
            layout=mt.layout,
        )
    
        assert wrapper_html == direct_html
        assert wrapper_html is not None
        assert "Runtime graph entity" in wrapper_html
        assert "runtime-token" in wrapper_html
    
    
    def test_render_runtime_harness_entity_shows_install_commands(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        def fake_graph(slug: str, **_kwargs) -> dict:
            assert slug == "mirage"
            return {
                "center": "harness:mirage",
                "nodes": [{
                    "data": {
                        "id": "harness:mirage",
                        "label": "Mirage",
                        "type": "harness",
                        "tags": ["harness", "sandbox"],
                        "description": "Virtual filesystem harness for agent tools.",
                    },
                }],
                "edges": [],
            }
    
        monkeypatch.setattr(mt, "graph_neighborhood", fake_graph)
    
        html_out = mt.render_wiki_entity("mirage", entity_type="harness")
    
        assert "Install harness" in html_out
        assert "ctx-harness-install mirage --dry-run" in html_out
        assert "ctx-harness-install mirage" in html_out
        assert "data-testid='runtime-entity-load'" not in html_out
    
    
    def test_render_graph_uses_builtin_3d_mount(monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.setattr(
            mt,
            "graph_stats",
            lambda: {"nodes": 0, "edges": 0, "available": False},
        )
        monkeypatch.setattr(mt, "graph_match_default_min_percent", lambda: 7)
        html_out = mt.render_graph("python-patterns")
        direct_html = graph_page.render_graph(
            focus="python-patterns",
            graph_stats=lambda: {"nodes": 0, "edges": 0, "available": False},
            top_degree_seeds=lambda **_kwargs: [],
            default_focus_slug="github",
            json_for_script=mt.json_for_script,
            graph_match_default_min_percent=lambda: 7,
            format_count=mt.format_count,
            layout=lambda _title, body: body,
        )
        assert "id='cy'" in html_out
        assert "id='cy'" in direct_html
        assert "https://unpkg.com" not in html_out
        assert "data-testid=\"graph-renderer\"" in html_out
        assert "data-testid=\"graph-3d\"" in html_out
        assert "button id=\"graph-zoom-in\"" in html_out
        assert "button id=\"graph-zoom-out\"" in html_out
        assert "id='cy' class='graph-stage'" in html_out
        assert "height:75vh" not in html_out
        assert "data-testid=\"graph-edge-detail\"" in html_out
        assert "data-testid=\"graph-live-results\"" in html_out
        assert "data-testid=\"graph-inspector-resize\"" in html_out
        assert "id='match-filter-min'" in html_out
        assert "id='match-filter-max'" in html_out
        assert "class='card graph-match-card'" in html_out
        assert "data-testid=\"match-histogram\"" in html_out
        assert "data-testid=\"match-range-control\"" in html_out
        assert "id='match-filter-min-value'" in html_out
        assert "id='match-filter-max-value'" in html_out
        assert "id='match-filter-value'" not in html_out
        assert "value='7'" in html_out
        assert "max='100'" in html_out
        assert "99%+" not in html_out
        assert "100%" in html_out
        assert "function clampInspectorHeight" in html_out
        assert "ctx-monitor-graph-inspector-height" in html_out
        assert "ArrowUp" in html_out
        assert "ArrowDown" in html_out
        assert "data-testid=\"graph-node-detail-tree\"" in html_out
        assert "function renderNodeTree" in html_out
        assert "function scheduleLiveSearch" in html_out
        assert "function qualityText" in html_out
        assert "function nodeShapeSvg" in html_out
        assert "data-node-shape=" in html_out
        assert " {" not in html_out
        assert "graph-fallback-label" in html_out
        assert "class=\"graph-toolbar\"" in html_out
        assert "class=\"graph-inspector-grid\"" in html_out
        assert "graph-edge-detail-inline" in html_out
        assert "background:transparent" in html_out
        assert "fill=\"transparent\" pointer-events=\"all\"" in html_out
        assert "onmouseup=" not in html_out
        assert "onpointerup=" not in html_out
        assert "querySelectorAll('[data-testid=\"graph-svg-edge\"]')" in html_out
        assert "data-edge-weight=" in html_out
        assert "nodePassesMatch" in html_out
        assert "function currentMatchWindow" in html_out
        assert "function renderMatchRange" in html_out
        assert "function renderMatchHistogram" in html_out
        assert "function setMatchWindow" in html_out
        assert "data-match-bin-min" in html_out
        assert "aria-label=\"Filter match " in html_out
        assert "bar.addEventListener('click', useBin)" in html_out
        assert "function edgeInMatchWindow" in html_out
        assert "highPct >= 99 ? 1" not in html_out
        assert "max: highPct / 100" in html_out
        assert "document.getElementById('match-filter-min').addEventListener('input', applyFilters)" in html_out
        assert "document.getElementById('match-filter-max').addEventListener('input', applyFilters)" in html_out
        assert "No neighbors in this view." in html_out
        assert "graph neighbor" in html_out
        assert "match" in html_out
        assert "These links have no shared tags" not in html_out
        assert "unproven links filtered" not in html_out
        assert "Connections: " in html_out
        assert "raw score clamped" in html_out
        assert "graph score" not in html_out
        assert "renderFallback(g); return;" in html_out
        assert "renderFallback(g);\n  const list" not in html_out
        assert "data-testid=\"graph-list\" class=\"graph-list-panel\" hidden" in html_out
        assert "id='graph-explanation'" in html_out
        assert "g.explanations" in html_out
        assert "Graph renderer unavailable" not in html_out
        assert "Enter a slug to render the graph" in html_out
        # Initial slug must be embedded as JSON literal so the JS picks it up.
        assert "\"python-patterns\"" in html_out
    
    
    def test_render_graph_focus_controls_preserve_type(monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.setattr(
            mt,
            "graph_stats",
            lambda: {"nodes": 0, "edges": 0, "available": False},
        )
        html_out = mt.render_graph("langgraph", focus_type="harness")
        assert "id='focus-type'" in html_out
        assert "" in html_out
        assert "document.getElementById('go').addEventListener('click', () => load(focus.value.trim(), selectedFocusType()))" in html_out
        assert "focus.addEventListener('keydown', (ev) => { if (ev.key === 'Enter') load(ev.target.value.trim(), selectedFocusType()); })" in html_out
    
    
    def test_graph_neighborhood_rejects_unsafe_slug() -> None:
        result = mt.graph_neighborhood("../../evil")
        assert result == {"nodes": [], "edges": [], "center": None}
    
    
    def test_graph_neighborhood_supports_mcp_nodes(
        fake_claude: Path,
        monkeypatch,
    ) -> None:
        import networkx as nx
        import sys
    
        G = nx.Graph()
        G.add_node(
            "mcp-server:anthropic-python-sdk",
            label="anthropic-python-sdk",
            type="mcp-server",
            tags=["sdk"],
        )
        fake = type("M", (), {"load_graph": staticmethod(lambda _path=None: G)})
        monkeypatch.setitem(sys.modules, "ctx.core.graph.resolve_graph", fake)
        monkeypatch.setitem(sys.modules, "resolve_graph", fake)
    
        result = mt.graph_neighborhood("anthropic-python-sdk")
        assert result["center"] == "mcp-server:anthropic-python-sdk"
        assert result["nodes"][0]["data"]["type"] == "mcp-server"
    
    
    def test_graph_neighborhood_resolves_partial_slug(
        fake_claude: Path,
        monkeypatch,
    ) -> None:
        import networkx as nx
    
        G = nx.Graph()
        G.add_node(
            "skill:brainstorming",
            label="brainstorming",
            type="skill",
            tags=["creative", "planning"],
        )
        G.add_node(
            "skill:multi-agent-brainstorming",
            label="multi-agent-brainstorming",
            type="skill",
            tags=["creative", "agents"],
        )
        G.add_edge(
            "skill:brainstorming",
            "skill:multi-agent-brainstorming",
            weight=0.89,
            shared_tags=["creative"],
        )
        monkeypatch.setattr(mt, "load_dashboard_graph", lambda: G)
    
        result = mt.graph_neighborhood("brainstorm", entity_type="skill")
    
        assert result["center"] == "skill:brainstorming"
        assert result["resolved"]["query"] == "brainstorm"
        assert result["resolved"]["slug"] == "brainstorming"
        assert "brainstorming" in result["suggestions"]
    
    
    def test_graph_neighborhood_sizes_nodes_by_score_usage_and_popularity(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        import networkx as nx
    
        G = nx.Graph()
        G.add_node(
            "skill:hub",
            label="hub",
            type="skill",
            tags=["python"],
            quality_score=0.95,
            usage_score=0.8,
        )
        G.add_node(
            "skill:leaf-low",
            label="leaf-low",
            type="skill",
            tags=["python"],
            quality_score=0.1,
            usage_score=0.0,
        )
        G.add_node(
            "skill:leaf-high",
            label="leaf-high",
            type="skill",
            tags=["python"],
            quality_score=0.85,
            usage_score=0.7,
        )
        G.add_edge("skill:hub", "skill:leaf-low", weight=0.2)
        G.add_edge("skill:hub", "skill:leaf-high", weight=0.9)
        monkeypatch.setattr(mt, "load_dashboard_graph", lambda: G)
    
        result = mt.graph_neighborhood("hub", entity_type="skill")
        sizes = {
            node["data"]["id"]: node["data"]["node_size"]
            for node in result["nodes"]
        }
    
        assert sizes["skill:hub"] > sizes["skill:leaf-high"] > sizes["skill:leaf-low"]
        assert 8 <= sizes["skill:leaf-low"] <= 24
        assert 8 <= sizes["skill:hub"] <= 24
        hub = next(node["data"] for node in result["nodes"] if node["data"]["id"] == "skill:hub")
        assert hub["size_signal"] > 0
        assert "quality 0.950" in hub["size_reason"]
        assert "usage 0.800" in hub["size_reason"]
        assert "popularity" in hub["size_reason"]
    
    
    def test_dashboard_score_payload_clamps_raw_outliers() -> None:
        payload = mt.dashboard_score_payload("quality_score", 18.0)
    
        assert payload == {"quality_score": 1.0, "quality_score_raw": 18.0}
        assert mt.dashboard_score_payload("quality_score", None) == {"quality_score": None}
    
    
    def test_graph_neighborhood_uses_direct_sidecar_scores_without_global_index(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        import networkx as nx
    
        sidecar_service.reset_caches()
        sidecar_dir = fake_claude / "skill-quality"
        for i in range(6):
            (sidecar_dir / f"node-{i}.json").write_text(
                json.dumps({
                    "slug": f"node-{i}",
                    "subject_type": "skill",
                    "score": 0.5,
                    "signals": {"telemetry": {"score": 0.1}},
                }),
                encoding="utf-8",
            )
    
        G = nx.Graph()
        G.add_node("skill:center", label="center", type="skill", tags=["x"])
        for i in range(6):
            G.add_node(f"skill:node-{i}", label=f"node-{i}", type="skill", tags=["x"])
            G.add_edge("skill:center", f"skill:node-{i}", weight=1.0)
        monkeypatch.setattr(mt, "load_dashboard_graph", lambda: G)
    
        def fail_index(*args: object, **kwargs: object) -> dict:
            raise AssertionError("graph rendering should not build the global sidecar index")
    
        monkeypatch.setattr(sidecar_service, "sidecar_index", fail_index)
    
        result = mt.graph_neighborhood("center", entity_type="skill")
    
        assert result["center"] == "skill:center"
        assert all(
            node["data"]["size_reason"].startswith("quality ")
            for node in result["nodes"]
        )
    
    
    def test_graph_helpers_reuse_graph_loaded_from_same_file(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        import networkx as nx
        import sys
    
        graph_file = fake_claude / "skill-wiki" / "graphify-out" / "graph.json"
        graph_file.parent.mkdir(parents=True)
        graph_file.write_text("{}", encoding="utf-8")
        G = nx.Graph()
        G.add_edge("skill:python-patterns", "skill:fastapi-pro", weight=2)
        G.nodes["skill:python-patterns"]["label"] = "python-patterns"
    
        calls: list[Path | None] = []
    
        def _load_graph(path: Path | None = None):
            calls.append(path)
            return G
    
        fake = type("M", (), {"load_graph": staticmethod(_load_graph)})
        monkeypatch.setitem(sys.modules, "ctx.core.graph.resolve_graph", fake)
        graph_service.reset_caches()
    
        assert mt.graph_stats()["nodes"] == 2
        assert mt.top_degree_seeds(limit=1)[0]["slug"] == "python-patterns"
        assert mt.graph_neighborhood("python-patterns")["center"] == "skill:python-patterns"
        assert calls == [graph_file]
    
    
    def test_dashboard_graph_cache_reuses_pack_only_graph(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        import networkx as nx
        import sys
    
        from ctx.core.graph.graph_packs import write_base_pack, write_overlay_pack
    
        graph_out = fake_claude / "skill-wiki" / "graphify-out"
        graph_file = graph_out / "graph.json"
        pack_graph = nx.Graph()
        pack_graph.add_node("skill:python-patterns", label="python-patterns", type="skill")
        write_base_pack(
            pack_dir=graph_out / "packs" / "base-export-1",
            pack_id="base-export-1",
            base_export_id="export-1",
            config_hash="config-1",
            model_id="model-1",
            graph=pack_graph,
        )
        assert not graph_file.exists()
    
        G = nx.Graph()
        G.add_node("skill:python-patterns", label="python-patterns", type="skill")
        calls: list[tuple[Path | None, dict[str, object]]] = []
    
        def _load_graph(path: Path | None = None, **kwargs: object):
            calls.append((path, kwargs))
            return G
    
        fake = type("M", (), {"load_graph": staticmethod(_load_graph)})
        monkeypatch.setitem(sys.modules, "ctx.core.graph.resolve_graph", fake)
        graph_service.reset_caches()
    
        assert mt.load_dashboard_graph() is G
        assert mt.load_dashboard_graph() is G
        assert calls == [(graph_file, {"apply_runtime_filter": False})]
    
        write_overlay_pack(
            pack_dir=graph_out / "packs" / "overlay-pack-target",
            pack_id="overlay-pack-target",
            base_export_id="export-1",
            parent_export_id="export-1",
            config_hash="config-1",
            model_id="model-1",
            nodes=[{"id": "skill:pack-target", "label": "pack-target", "type": "skill"}],
            edges=[],
            tombstones=[],
        )
    
        assert mt.load_dashboard_graph() is G
        assert calls == [
            (graph_file, {"apply_runtime_filter": False}),
            (graph_file, {"apply_runtime_filter": False}),
        ]
    
    
    def test_graph_service_cache_invalidates_when_pack_overlay_changes(fake_claude: Path) -> None:
        import networkx as nx
    
        from ctx.core.graph.graph_packs import write_base_pack, write_overlay_pack
    
        graph_out = fake_claude / "skill-wiki" / "graphify-out"
        graph_file = graph_out / "graph.json"
        base_graph = nx.Graph()
        base_graph.add_node("skill:python-patterns", label="python-patterns", type="skill")
        write_base_pack(
            pack_dir=graph_out / "packs" / "base-export-1",
            pack_id="base-export-1",
            base_export_id="export-1",
            config_hash="config-1",
            model_id="model-1",
            graph=base_graph,
        )
    
        loaded = [nx.Graph(), nx.Graph()]
        calls: list[Path | None] = []
    
        def load_graph(path: Path | None = None, **_kwargs: object):
            calls.append(path)
            return loaded[min(len(calls) - 1, len(loaded) - 1)]
    
        graph_service.reset_caches()
        first = graph_service.load_dashboard_graph(fake_claude / "skill-wiki", load_graph)
        second = graph_service.load_dashboard_graph(fake_claude / "skill-wiki", load_graph)
        assert first is second
        assert calls == [graph_file]
    
        write_overlay_pack(
            pack_dir=graph_out / "packs" / "overlay-pack-target",
            pack_id="overlay-pack-target",
            base_export_id="export-1",
            parent_export_id="export-1",
            config_hash="config-1",
            model_id="model-1",
            nodes=[{"id": "skill:pack-target", "label": "pack-target", "type": "skill"}],
            edges=[],
            tombstones=[],
        )
    
        refreshed = graph_service.load_dashboard_graph(fake_claude / "skill-wiki", load_graph)
        assert refreshed is loaded[1]
        assert calls == [graph_file, graph_file]
    
    
    def test_graph_neighborhood_uses_dashboard_index_without_full_graph_load(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        _write_graph_manifest(fake_claude, "test-export")
        index_path = fake_claude / "skill-wiki" / "graphify-out" / "dashboard-neighborhoods.sqlite3"
        index_path.parent.mkdir(parents=True, exist_ok=True)
        conn = sqlite3.connect(index_path)
        try:
            conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)")
            conn.execute(
                "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT,"
                "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)"
            )
            conn.execute(
                "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT,"
                "PRIMARY KEY(slug,type,node_id))"
            )
            conn.execute("CREATE TABLE neighbors(source TEXT PRIMARY KEY, payload BLOB NOT NULL)")
            conn.executemany(
                "INSERT INTO meta VALUES(?,?)",
                [
                    ("max_degree", "10"),
                    ("export_id", json.dumps("test-export")),
                    ("nodes_count", "2"),
                    ("edges_count", "1"),
                ],
            )
            conn.executemany(
                "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)",
                [
                    ("skill:python-patterns", "python-patterns", "skill", '["python"]', "", 0.9, 0.1, 10),
                    ("skill:fastapi-pro", "fastapi-pro", "skill", '["python","api"]', "", 0.8, 0.0, 4),
                ],
            )
            conn.executemany(
                "INSERT INTO slug_index VALUES(?,?,?)",
                [
                    ("python-patterns", "skill", "skill:python-patterns"),
                    ("fastapi-pro", "skill", "skill:fastapi-pro"),
                ],
            )
            payload = zlib.compress(json.dumps([
                {
                    "target": "skill:fastapi-pro",
                    "weight": 0.8,
                    "shared_tags": ["python"],
                    "reasons": ["semantic"],
                }
            ]).encode("utf-8"))
            conn.execute("INSERT INTO neighbors VALUES(?,?)", ("skill:python-patterns", payload))
            conn.commit()
        finally:
            conn.close()
    
        monkeypatch.setattr(
            mt,
            "load_dashboard_graph",
            lambda: (_ for _ in ()).throw(AssertionError("full graph loaded")),
        )
    
        result = mt.graph_neighborhood("python-patterns", entity_type="skill")
    
        assert result["center"] == "skill:python-patterns"
        assert [node["data"]["id"] for node in result["nodes"]] == [
            "skill:python-patterns",
            "skill:fastapi-pro",
        ]
        assert result["edges"][0]["data"]["shared_tags"] == ["python"]
        assert result["schema"] == {
            "name": "ctx.dashboard.graph.neighborhood",
            "version": 1,
        }
        assert result["layout"] == {
            "kind": "radial-3d",
            "node_size_field": "node_size",
            "node_size_min": 8.0,
            "node_size_max": 24.0,
            "edge_weight_field": "weight",
        }
        assert result["insights"] == {
            "source": "dashboard-index",
            "node_count": 2,
            "edge_count": 1,
            "by_type": {"skill": 2, "agent": 0, "mcp-server": 0, "harness": 0},
            "max_degree": 10,
            "center_degree": 10,
        }
        assert "cached dashboard index" in result["explanations"]["source"]
        assert "exact or normalized slug" in result["explanations"]["search"]
        assert "quality, usage, and graph degree" in result["explanations"]["layout"]
        assert "shared_tags" in result["explanations"]["edges"]
        assert mt.graph_stats() == {"nodes": 2, "edges": 1, "available": True}
        assert mt.wiki_stats() == {
            "skills": 2,
            "agents": 0,
            "mcps": 0,
            "harnesses": 0,
            "total": 2,
            "split_known": True,
        }
    
    
    def test_graph_service_resolves_dashboard_index_center() -> None:
        conn = sqlite3.connect(":memory:")
        conn.row_factory = sqlite3.Row
        try:
            conn.execute(
                "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT,"
                "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)"
            )
            conn.execute(
                "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT,"
                "PRIMARY KEY(slug,type,node_id))"
            )
            conn.executemany(
                "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)",
                [
                    (
                        "skill:brainstorming",
                        "Brainstorming",
                        "skill",
                        '["creative","planning"]',
                        "",
                        0.9,
                        0.1,
                        12,
                    ),
                    (
                        "skill:multi-agent-brainstorming",
                        "Multi Agent Brainstorming",
                        "skill",
                        '["creative","agents"]',
                        "",
                        0.8,
                        0.0,
                        4,
                    ),
                ],
            )
            conn.executemany(
                "INSERT INTO slug_index VALUES(?,?,?)",
                [
                    ("brainstorming", "skill", "skill:brainstorming"),
                    (
                        "multi-agent-brainstorming",
                        "skill",
                        "skill:multi-agent-brainstorming",
                    ),
                ],
            )
            conn.commit()
    
            center, resolved, suggestions = graph_service.resolve_index_center(
                conn,
                "brainstorming",
                "skill",
            )
            assert center == "skill:brainstorming"
            assert resolved is None
            assert suggestions == ["brainstorming"]
    
            center, resolved, suggestions = graph_service.resolve_index_center(
                conn,
                "multi brainstorm",
                "skill",
            )
            assert center == "skill:multi-agent-brainstorming"
            assert resolved == {
                "query": "multi brainstorm",
                "slug": "multi-agent-brainstorming",
                "id": "skill:multi-agent-brainstorming",
            }
            assert suggestions == ["multi-agent-brainstorming"]
    
            assert graph_service.resolve_index_center(conn, "../brainstorming", "skill") == (
                None,
                None,
                [],
            )
        finally:
            conn.close()
    
    
    def test_graph_service_builds_dashboard_index_neighborhood() -> None:
        conn = sqlite3.connect(":memory:")
        conn.row_factory = sqlite3.Row
        try:
            conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)")
            conn.execute(
                "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT,"
                "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)"
            )
            conn.execute(
                "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT,"
                "PRIMARY KEY(slug,type,node_id))"
            )
            conn.execute("CREATE TABLE neighbors(source TEXT PRIMARY KEY, payload BLOB NOT NULL)")
            conn.executemany(
                "INSERT INTO meta VALUES(?,?)",
                [("max_degree", "10"), ("top_k", "40")],
            )
            conn.executemany(
                "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)",
                [
                    ("skill:python-patterns", "python-patterns", "skill", '["python"]', "", 0.9, 0.1, 10),
                    ("skill:fastapi-pro", "fastapi-pro", "skill", '["python","api"]', "", 0.8, 0.0, 4),
                ],
            )
            conn.executemany(
                "INSERT INTO slug_index VALUES(?,?,?)",
                [
                    ("python-patterns", "skill", "skill:python-patterns"),
                    ("fastapi-pro", "skill", "skill:fastapi-pro"),
                ],
            )
            payload = zlib.compress(json.dumps([{
                "target": "skill:fastapi-pro",
                "weight": 0.8,
                "shared_tags": ["python"],
                "reasons": ["semantic"],
            }]).encode("utf-8"))
            conn.execute("INSERT INTO neighbors VALUES(?,?)", ("skill:python-patterns", payload))
            conn.commit()
    
            result = graph_service.dashboard_index_neighborhood(
                conn,
                "python-patterns",
                hops=1,
                limit=40,
                entity_type="skill",
                node_size=lambda **_: {
                    "node_size": 12.0,
                    "size_signal": 0.25,
                    "size_reason": "test",
                },
                score_payload=lambda field, value: {field: value},
            )
        finally:
            conn.close()
    
        assert result is not None
        assert result["center"] == "skill:python-patterns"
        assert [node["data"]["id"] for node in result["nodes"]] == [
            "skill:python-patterns",
            "skill:fastapi-pro",
        ]
        assert result["edges"][0]["data"]["shared_tags"] == ["python"]
        assert result["schema"]["name"] == "ctx.dashboard.graph.neighborhood"
        assert result["insights"]["source"] == "dashboard-index"
    
    
    def test_graph_service_reads_dashboard_index_stats(tmp_path: Path) -> None:
        report = tmp_path / "graph-report.md"
        report.write_text("> Nodes: 1,234 | Edges: 5,678 | Communities: 9\n", encoding="utf-8")
        assert graph_service.graph_report_stats(report) == {
            "nodes": 1234,
            "edges": 5678,
            "available": True,
        }
    
        index_path = tmp_path / "dashboard-neighborhoods.sqlite3"
        conn = sqlite3.connect(index_path)
        try:
            conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)")
            conn.execute(
                "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT,"
                "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)"
            )
            conn.executemany(
                "INSERT INTO meta VALUES(?,?)",
                [("nodes_count", "3"), ("edges_count", "2")],
            )
            conn.executemany(
                "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)",
                [
                    ("skill:python-patterns", "python-patterns", "skill", "[]", "", 1.0, 0.0, 2),
                    ("agent:reviewer", "reviewer", "agent", "[]", "", 0.7, 0.0, 1),
                    ("mcp-server:github", "github", "mcp-server", "[]", "", 0.8, 0.0, 1),
                ],
            )
            conn.commit()
        finally:
            conn.close()
    
        assert graph_service.dashboard_index_graph_stats(index_path) == {
            "nodes": 3,
            "edges": 2,
            "available": True,
        }
        assert graph_service.dashboard_index_wiki_stats(
            index_path,
            index_matches_manifest=lambda path: path == index_path,
        ) == {
            "skills": 1,
            "agents": 1,
            "mcps": 1,
            "harnesses": 0,
            "total": 3,
            "split_known": True,
        }
        assert graph_service.top_degree_seeds_from_index(index_path, limit=2) == [
            {
                "slug": "python-patterns",
                "type": "skill",
                "degree": 2,
                "label": "python-patterns",
            },
            {
                "slug": "reviewer",
                "type": "agent",
                "degree": 1,
                "label": "reviewer",
            },
        ]
    
    
    def test_graph_service_reads_graph_store_neighborhood(tmp_path: Path) -> None:
        import networkx as nx
        from networkx.readwrite import node_link_data
    
        from ctx.core.graph.graph_store import ensure_graph_store
    
        graph_dir = tmp_path / "graphify-out"
        graph_dir.mkdir(parents=True)
        graph = nx.Graph()
        graph.add_node(
            "skill:python-patterns",
            label="python-patterns",
            type="skill",
            tags=["python"],
        )
        graph.add_node(
            "skill:fastapi-pro",
            label="fastapi-pro",
            type="skill",
            tags=["python", "api"],
        )
        graph.add_edge(
            "skill:python-patterns",
            "skill:fastapi-pro",
            weight=0.8,
            shared_tags=["python"],
            reasons=["semantic"],
        )
        (graph_dir / "graph.json").write_text(
            json.dumps(node_link_data(graph, edges="edges")),
            encoding="utf-8",
        )
        ensure_graph_store(graph_dir, graph_dir / "graph-store.sqlite3")
    
        result = graph_service.graph_store_neighborhood(
            graph_dir,
            "python-patterns",
            hops=1,
            limit=40,
            entity_type="skill",
            node_size=lambda *_args, **_kwargs: {
                "node_size": 12.0,
                "size_signal": 0.25,
                "size_reason": "test",
            },
            score_payload=lambda field, value: {field: value},
        )
    
        assert result is not None
        assert result["center"] == "skill:python-patterns"
        assert result["insights"]["source"] == "graph-store"
        assert [node["data"]["id"] for node in result["nodes"]] == [
            "skill:python-patterns",
            "skill:fastapi-pro",
        ]
        assert result["edges"][0]["data"]["shared_tags"] == ["python"]
    
    
    def test_graph_neighborhood_uses_fresh_graph_store_without_full_graph_load(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        import networkx as nx
        from networkx.readwrite import node_link_data
    
        from ctx.core.graph.graph_store import ensure_graph_store
    
        graph_dir = fake_claude / "skill-wiki" / "graphify-out"
        graph_dir.mkdir(parents=True, exist_ok=True)
        graph = nx.Graph()
        graph.add_node(
            "skill:python-patterns",
            label="python-patterns",
            type="skill",
            tags=["python"],
        )
        graph.add_node(
            "skill:fastapi-pro",
            label="fastapi-pro",
            type="skill",
            tags=["python", "api"],
        )
        graph.add_edge(
            "skill:python-patterns",
            "skill:fastapi-pro",
            weight=0.8,
            shared_tags=["python"],
            reasons=["semantic"],
        )
        (graph_dir / "graph.json").write_text(
            json.dumps(node_link_data(graph, edges="edges")),
            encoding="utf-8",
        )
        ensure_graph_store(graph_dir, graph_dir / "graph-store.sqlite3")
        monkeypatch.setattr(
            mt,
            "load_dashboard_graph",
            lambda: (_ for _ in ()).throw(AssertionError("full graph loaded")),
        )
    
        result = mt.graph_neighborhood("python-patterns", entity_type="skill")
    
        assert result["center"] == "skill:python-patterns"
        assert result["insights"]["source"] == "graph-store"
        assert [node["data"]["id"] for node in result["nodes"]] == [
            "skill:python-patterns",
            "skill:fastapi-pro",
        ]
        assert result["edges"][0]["data"]["shared_tags"] == ["python"]
    
    
    def test_skillspector_payload_filters_by_tag_and_graph_family(
        fake_claude: Path,
    ) -> None:
        _write_graph_manifest(fake_claude, "audit-export")
        graph_dir = fake_claude / "skill-wiki" / "graphify-out"
        index_path = graph_dir / "dashboard-neighborhoods.sqlite3"
        conn = sqlite3.connect(index_path)
        try:
            conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)")
            conn.execute(
                "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT,"
                "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)"
            )
            conn.executemany(
                "INSERT INTO meta VALUES(?,?)",
                [
                    ("export_id", json.dumps("audit-export")),
                    ("max_degree", "10"),
                ],
            )
            conn.executemany(
                "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)",
                [
                    (
                        "skill:danger-skill",
                        "Danger Skill",
                        "skill",
                        '["security","shell"]',
                        "Runs shell checks for exfiltration review.",
                        0.2,
                        0.0,
                        9,
                    ),
                    (
                        "skill:safe-skill",
                        "Safe Skill",
                        "skill",
                        '["python"]',
                        "Safe Python helper.",
                        0.9,
                        0.1,
                        2,
                    ),
                ],
            )
            conn.commit()
        finally:
            conn.close()
        (graph_dir / "communities.json").write_text(
            json.dumps({
                "communities": {
                    "7": {
                        "label": "Security Tools",
                        "members": ["skill:danger-skill"],
                    },
                    "8": {
                        "label": "Python Tools",
                        "members": ["skill:safe-skill"],
                    },
                },
            }),
            encoding="utf-8",
        )
        _write_skillspector_audit(fake_claude, [
            {
                "schema_version": 1,
                "slug": "danger-skill",
                "status": "blocked",
                "risk_score": 99,
                "risk_severity": "CRITICAL",
                "recommendation": "BLOCK",
                "issues": 2,
                "components": 1,
                "content_sha256": "a" * 64,
                "scanned_at": "2026-06-18T00:00:00+00:00",
                "scanner": "NVIDIA SkillSpector",
                "scanner_repo": "https://github.com/NVIDIA/SkillSpector",
                "scanner_version": "2.2.3",
                "mode": "static-no-llm",
                "llm_requested": False,
                "issue_rules": ["exfiltration-risk"],
            },
            {
                "schema_version": 1,
                "slug": "safe-skill",
                "status": "passed",
                "risk_score": 0,
                "risk_severity": "LOW",
                "recommendation": "SAFE",
                "issues": 0,
                "components": 1,
                "content_sha256": "b" * 64,
                "scanned_at": "2026-06-18T00:00:00+00:00",
                "scanner": "NVIDIA SkillSpector",
                "scanner_repo": "https://github.com/NVIDIA/SkillSpector",
                "scanner_version": "2.2.3",
                "mode": "static-no-llm",
                "llm_requested": False,
                "issue_rules": [],
            },
        ])
    
        payload = mt.skillspector_audit_payload({
            "q": "exfiltration",
            "status": "blocked",
            "severity": "CRITICAL",
            "tag": "security",
            "family": "Security Tools",
            "limit": "10",
        })
    
        assert payload["summary"]["total"] == 2
        assert payload["summary"]["problematic"] == 1
        assert payload["summary"]["visible"] == 1
        assert payload["records"][0]["slug"] == "danger-skill"
        assert payload["records"][0]["family"] == "Security Tools"
        assert payload["records"][0]["tags"] == ["security", "shell"]
        assert {"value": "security", "count": 1} in payload["filters"]["tags"]
        assert {"value": "Security Tools", "count": 1} in payload["filters"]["families"]
    
    
    def test_skillspector_service_payload_uses_dashboard_metadata(
        fake_claude: Path,
    ) -> None:
        _write_graph_manifest(fake_claude, "service-audit-export")
        graph_dir = fake_claude / "skill-wiki" / "graphify-out"
        index_path = graph_dir / "dashboard-neighborhoods.sqlite3"
        conn = sqlite3.connect(index_path)
        try:
            conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)")
            conn.execute(
                "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT,"
                "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)"
            )
            conn.execute(
                "INSERT INTO meta VALUES(?,?)",
                ("export_id", json.dumps("service-audit-export")),
            )
            conn.execute(
                "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)",
                (
                    "skill:review-skill",
                    "Review Skill",
                    "skill",
                    '["review","python"]',
                    "Reviews Python changes.",
                    0.8,
                    0.2,
                    4,
                ),
            )
            conn.commit()
        finally:
            conn.close()
        _write_skillspector_audit(fake_claude, [{
            "schema_version": 1,
            "slug": "review-skill",
            "status": "findings",
            "risk_score": 80,
            "risk_severity": "HIGH",
            "recommendation": "REVIEW",
            "issues": 1,
            "components": 1,
            "content_sha256": "c" * 64,
            "scanned_at": "2026-06-18T00:00:00+00:00",
            "scanner": "NVIDIA SkillSpector",
            "scanner_repo": "https://github.com/NVIDIA/SkillSpector",
            "scanner_version": "2.2.3",
            "mode": "static-no-llm",
            "llm_requested": False,
            "issue_rules": ["prompt-injection"],
        }])
    
        payload = skillspector_service.audit_payload(
            fake_claude / "skill-wiki",
            mt.repo_graph_dir(),
            index_path,
            lambda _path: True,
            {"q": "python", "limit": "1"},
        )
    
        assert payload["audit_available"] is True
        assert payload["records"][0]["slug"] == "review-skill"
        assert payload["records"][0]["tags"] == ["review", "python"]
        assert payload["audit_path"].endswith("skillspector-audit.jsonl.gz")
    
    
    def test_skillspector_page_and_api_route_render_audit(
        fake_claude: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        _write_skillspector_audit(fake_claude, [{
            "schema_version": 1,
            "slug": "review-skill",
            "status": "findings",
            "risk_score": 80,
            "risk_severity": "HIGH",
            "recommendation": "REVIEW",
            "issues": 1,
            "components": 1,
            "content_sha256": "c" * 64,
            "scanned_at": "2026-06-18T00:00:00+00:00",
            "scanner": "NVIDIA SkillSpector",
            "scanner_repo": "https://github.com/NVIDIA/SkillSpector",
            "scanner_version": "2.2.3",
            "mode": "static-no-llm",
            "llm_requested": False,
            "issue_rules": ["prompt-injection"],
        }])
    
        html_out = mt.render_skillspector({"q": "prompt", "severity": "HIGH"})
        direct_html = skillspector_page.render_skillspector(
            mt.skillspector_audit_payload({"q": "prompt", "severity": "HIGH"}),
            layout=lambda _title, body: body,
        )
    
        assert "SkillSpector audit" in html_out
        assert "/api/skillspector.json" in html_out
        assert "review-skill" in html_out
        assert "prompt-injection" in html_out
        assert "review-skill" in direct_html
        assert "prompt-injection" in direct_html
        assert "/skillspector" in mt.layout("test", "

    body

    ") server, _thread, port = _serve_monitor(monkeypatch) try: status, payload = _get_json(port, "/api/skillspector.json?severity=HIGH&q=prompt") finally: server.shutdown() server.server_close() assert status == 200 assert payload["summary"]["visible"] == 1 assert payload["records"][0]["slug"] == "review-skill" def test_graph_neighborhood_uses_dashboard_index_when_overlay_is_already_indexed( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: _write_graph_manifest(fake_claude, "test-export") graph_dir = fake_claude / "skill-wiki" / "graphify-out" index_path = graph_dir / "dashboard-neighborhoods.sqlite3" overlay_path = graph_dir / "entity-overlays.jsonl" graph_dir.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(index_path) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.execute( "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT," "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)" ) conn.execute( "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT," "PRIMARY KEY(slug,type,node_id))" ) conn.execute("CREATE TABLE neighbors(source TEXT PRIMARY KEY, payload BLOB NOT NULL)") conn.executemany( "INSERT INTO meta VALUES(?,?)", [ ("max_degree", "10"), ("top_k", "40"), ("export_id", json.dumps("test-export")), ("nodes_count", "2"), ("edges_count", "1"), ], ) conn.executemany( "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)", [ ("harness:mirage", "mirage", "harness", '["sandbox"]', "", 0.82, 0.0, 10), ("skill:codex-review", "codex-review", "skill", '["review"]', "", 0.7, 0.0, 5), ], ) conn.executemany( "INSERT INTO slug_index VALUES(?,?,?)", [ ("mirage", "harness", "harness:mirage"), ("codex-review", "skill", "skill:codex-review"), ], ) conn.execute( "INSERT INTO neighbors VALUES(?,?)", ( "harness:mirage", zlib.compress(json.dumps([ { "target": "skill:codex-review", "weight": 0.18, "shared_tags": ["sandbox"], }, ]).encode("utf-8")), ), ) conn.commit() finally: conn.close() overlay_path.write_text( json.dumps({ "overlay_id": "mirage-overlay", "nodes": [{"id": "harness:mirage", "type": "harness"}], "edges": [{"source": "harness:mirage", "target": "skill:codex-review"}], }) + "\n", encoding="utf-8", ) monkeypatch.setattr(mt, "dashboard_overlay_matches_known_release", lambda overlay: False) monkeypatch.setattr( mt, "load_dashboard_graph", lambda: (_ for _ in ()).throw(AssertionError("full graph loaded")), ) result = mt.graph_neighborhood("mirage", entity_type="harness") assert result["center"] == "harness:mirage" assert [node["data"]["id"] for node in result["nodes"]] == [ "harness:mirage", "skill:codex-review", ] def test_dashboard_overlay_release_hash_normalizes_crlf( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: overlay = tmp_path / "entity-overlays.jsonl" overlay.write_bytes(b'{"overlay_id":"release"}\r\n') expected = hashlib.sha256(b'{"overlay_id":"release"}\n').hexdigest() monkeypatch.setattr(ci, "_GRAPH_ENTITY_OVERLAY_SHA256", expected) assert graph_service.dashboard_overlay_matches_known_release(overlay) assert mt.dashboard_overlay_matches_known_release(overlay) def test_graph_neighborhood_bypasses_index_for_local_overlay_even_when_node_exists( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: import networkx as nx _write_graph_manifest(fake_claude, "test-export") graph_dir = fake_claude / "skill-wiki" / "graphify-out" index_path = graph_dir / "dashboard-neighborhoods.sqlite3" overlay_path = graph_dir / "entity-overlays.jsonl" graph_dir.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(index_path) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.execute( "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT," "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)" ) conn.execute( "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT," "PRIMARY KEY(slug,type,node_id))" ) conn.execute("CREATE TABLE neighbors(source TEXT PRIMARY KEY, payload BLOB NOT NULL)") conn.executemany( "INSERT INTO meta VALUES(?,?)", [ ("max_degree", "10"), ("top_k", "40"), ("export_id", json.dumps("test-export")), ("nodes_count", "1"), ("edges_count", "0"), ], ) conn.execute( "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)", ("skill:existing", "existing", "skill", "[]", "", 0.7, 0.0, 0), ) conn.execute( "INSERT INTO slug_index VALUES(?,?,?)", ("existing", "skill", "skill:existing"), ) conn.execute( "INSERT INTO neighbors VALUES(?,?)", ("skill:existing", zlib.compress(b"[]")), ) conn.commit() finally: conn.close() overlay_path.write_text( json.dumps({ "kind": "ann_attach", "nodes": [{"id": "skill:existing", "type": "skill"}], "edges": [{"source": "skill:existing", "target": "skill:new-runtime-edge"}], }) + "\n", encoding="utf-8", ) G = nx.Graph() G.add_node("skill:existing", label="existing", type="skill", tags=[]) graph_loads = 0 def load_graph(): nonlocal graph_loads graph_loads += 1 return G monkeypatch.setattr(mt, "load_dashboard_graph", load_graph) result = mt.graph_neighborhood("existing", entity_type="skill") assert result["center"] == "skill:existing" assert graph_loads == 1 def test_graph_index_honors_requested_type_on_exact_slug( fake_claude: Path, ) -> None: _write_graph_manifest(fake_claude, "test-export") index_path = fake_claude / "skill-wiki" / "graphify-out" / "dashboard-neighborhoods.sqlite3" index_path.parent.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(index_path) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.execute( "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT," "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)" ) conn.execute( "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT," "PRIMARY KEY(slug,type,node_id))" ) conn.execute("CREATE TABLE neighbors(source TEXT PRIMARY KEY, payload BLOB NOT NULL)") conn.executemany( "INSERT INTO meta VALUES(?,?)", [("max_degree", "1"), ("top_k", "40"), ("export_id", json.dumps("test-export"))], ) conn.executemany( "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)", [ ("skill:github", "github", "skill", "[]", "", None, None, 0), ("mcp-server:github", "github", "mcp-server", "[]", "", None, None, 0), ], ) conn.executemany( "INSERT INTO slug_index VALUES(?,?,?)", [ ("github", "skill", "skill:github"), ("github", "mcp-server", "mcp-server:github"), ], ) conn.executemany( "INSERT INTO neighbors VALUES(?,?)", [ ("skill:github", zlib.compress(b"[]")), ("mcp-server:github", zlib.compress(b"[]")), ], ) conn.commit() finally: conn.close() result = mt.graph_neighborhood("github", entity_type="skill") assert result["center"] == "skill:github" def test_graph_index_matches_fuzzy_slug_resolution( fake_claude: Path, ) -> None: _write_graph_manifest(fake_claude, "test-export") index_path = fake_claude / "skill-wiki" / "graphify-out" / "dashboard-neighborhoods.sqlite3" index_path.parent.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(index_path) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.execute( "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT," "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)" ) conn.execute( "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT," "PRIMARY KEY(slug,type,node_id))" ) conn.execute("CREATE TABLE neighbors(source TEXT PRIMARY KEY, payload BLOB NOT NULL)") conn.executemany( "INSERT INTO meta VALUES(?,?)", [("max_degree", "1"), ("top_k", "40"), ("export_id", json.dumps("test-export"))], ) conn.execute( "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)", ("skill:github-actions", "GitHub Actions", "skill", '["ci"]', "", None, None, 0), ) conn.execute( "INSERT INTO slug_index VALUES(?,?,?)", ("github-actions", "skill", "skill:github-actions"), ) conn.execute("INSERT INTO neighbors VALUES(?,?)", ("skill:github-actions", zlib.compress(b"[]"))) conn.commit() finally: conn.close() result = mt.graph_neighborhood("git hub", entity_type="skill") assert result["center"] == "skill:github-actions" assert result["resolved"]["slug"] == "github-actions" def test_entity_subgraph_script_json_escapes_script_end_tag() -> None: graph_html = mt.render_entity_subgraph_svg( center="skill:evil", node_by_id={ "skill:evil": { "label": "", "type": "skill", }, }, edges=[], sidecar_by_id={}, ) assert "" not in graph_html assert "<\\/script>" in graph_html def test_entity_subgraph_wrapper_matches_extracted_wiki_renderer() -> None: node_by_id = { "skill:github": {"label": "GitHub", "type": "skill", "degree": 7}, "mcp-server:github-api": {"label": "GitHub API", "type": "mcp-server", "degree": 3}, } edges = [ { "data": { "source": "skill:github", "target": "mcp-server:github-api", "weight": 0.82, "shared": ["github"], }, }, ] wrapper_html = mt.render_entity_subgraph_svg( node_by_id=node_by_id, edges=edges, center="skill:github", sidecar_by_id={}, ) direct_html = wiki_page.render_entity_subgraph_svg( node_by_id=node_by_id, edges=edges, center="skill:github", sidecar_by_id={}, graph_type_from_node_id=mt.graph_type_from_node_id, graph_slug_from_node_id=mt.graph_slug_from_node_id, display_label=mt.display_label, display_slug=mt.display_slug, entity_wiki_href=mt.entity_wiki_href, json_for_script=mt.json_for_script, ) assert wrapper_html == direct_html assert "/wiki/github-api?type=mcp-server" in wrapper_html assert "mcp-server" in wrapper_html def test_entity_subgraph_page_renderer_uses_injected_graph_dependencies() -> None: graph = { "center": "skill:github", "nodes": [ {"data": {"id": "skill:github", "label": "GitHub", "type": "skill"}}, { "data": { "id": "mcp-server:github-api", "label": "GitHub API", "type": "mcp-server", }, }, ], "edges": [ { "data": { "source": "skill:github", "target": "mcp-server:github-api", "weight": 0.82, "shared_tags": ["github"], }, }, ], } html_out = wiki_page.render_entity_subgraph( "github", "skill", graph_neighborhood=lambda *_args, **_kwargs: graph, graph_type_from_node_id=mt.graph_type_from_node_id, graph_slug_from_node_id=mt.graph_slug_from_node_id, subgraph_sidecar=lambda slug, _entity_type: { "grade": "A", "raw_score": 0.91, } if slug == "github-api" else None, display_label=mt.display_label, display_slug=mt.display_slug, entity_wiki_href=mt.entity_wiki_href, json_for_script=mt.json_for_script, ) assert "1-hop neighborhood" in html_out assert "/wiki/github-api?type=mcp-server" in html_out assert "github" in html_out assert "grade-A" in html_out def test_graph_page_initial_query_escapes_script_end_tag() -> None: html_out = mt.render_graph("") assert "const initial = \"<\\/script>" in html_out assert "const initial = \"" not in html_out def test_graph_neighborhood_extracts_missing_dashboard_index_from_archive( fake_claude: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: seed = tmp_path / "dashboard-neighborhoods.sqlite3" conn = sqlite3.connect(seed) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.execute( "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT," "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)" ) conn.execute( "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT," "PRIMARY KEY(slug,type,node_id))" ) conn.execute("CREATE TABLE neighbors(source TEXT PRIMARY KEY, payload BLOB NOT NULL)") conn.executemany( "INSERT INTO meta VALUES(?,?)", [ ("max_degree", "10"), ("export_id", json.dumps("archive-export")), ("nodes_count", "1"), ("edges_count", "0"), ("top_k", "40"), ], ) conn.execute( "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)", ("skill:python-patterns", "python-patterns", "skill", '["python"]', "", 0.9, 0.1, 10), ) conn.execute( "INSERT INTO slug_index VALUES(?,?,?)", ("python-patterns", "skill", "skill:python-patterns"), ) conn.execute( "INSERT INTO neighbors VALUES(?,?)", ("skill:python-patterns", zlib.compress(b"[]")), ) conn.commit() finally: conn.close() manifest = fake_claude / "skill-wiki" / "graphify-out" / "graph-export-manifest.json" manifest.parent.mkdir(parents=True) manifest.write_text( json.dumps({"version": 1, "export_id": "archive-export"}), encoding="utf-8", ) archive = tmp_path / "wiki-graph-runtime.tar.gz" with tarfile.open(archive, "w:gz") as tar: tar.add(seed, arcname="./graphify-out/dashboard-neighborhoods.sqlite3") monkeypatch.setattr(mt, "dashboard_graph_index_archives", lambda: [archive]) monkeypatch.setattr(mt, "packaged_graph_export_id", lambda: "archive-export") monkeypatch.setattr( mt, "load_dashboard_graph", lambda: (_ for _ in ()).throw(AssertionError("full graph loaded")), ) result = mt.graph_neighborhood("python-patterns", entity_type="skill") assert result["center"] == "skill:python-patterns" assert (fake_claude / "skill-wiki" / "graphify-out" / "dashboard-neighborhoods.sqlite3").is_file() def test_dashboard_index_extraction_skips_archive_export_mismatch( fake_claude: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: graph_dir = fake_claude / "skill-wiki" / "graphify-out" graph_dir.mkdir(parents=True) (graph_dir / "graph-export-manifest.json").write_text( json.dumps({"version": 1, "export_id": "local-export"}), encoding="utf-8", ) manifest = tmp_path / "graph-export-manifest.json" manifest.write_text( json.dumps({"version": 1, "export_id": "archive-export"}), encoding="utf-8", ) seed = tmp_path / "dashboard-neighborhoods.sqlite3" seed.write_bytes(b"should-not-be-extracted") archive = tmp_path / "wiki-graph-runtime.tar.gz" with tarfile.open(archive, "w:gz") as tar: tar.add(manifest, arcname="./graphify-out/graph-export-manifest.json") tar.add(seed, arcname="./graphify-out/dashboard-neighborhoods.sqlite3") assert graph_service.archive_graph_export_id(archive) == "archive-export" monkeypatch.setattr(mt, "dashboard_graph_index_archives", lambda: [archive]) monkeypatch.setattr(mt, "packaged_graph_export_id", lambda: None) monkeypatch.setattr( mt, "dashboard_index_matches_manifest", lambda path: (_ for _ in ()).throw( AssertionError(f"should not validate extracted index: {path}"), ), ) assert mt.ensure_dashboard_graph_index() is None assert not (graph_dir / "dashboard-neighborhoods.sqlite3").exists() def test_dashboard_index_extraction_skips_packaged_export_mismatch( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: graph_dir = fake_claude / "skill-wiki" / "graphify-out" graph_dir.mkdir(parents=True) (graph_dir / "graph-export-manifest.json").write_text( json.dumps({"version": 1, "export_id": "old-local-export"}), encoding="utf-8", ) monkeypatch.setattr(mt, "packaged_graph_export_id", lambda: "new-packaged-export") monkeypatch.setattr( mt, "dashboard_graph_index_archives", lambda: (_ for _ in ()).throw(AssertionError("archive scan should be skipped")), ) assert mt.ensure_dashboard_graph_index() is None def test_graph_neighborhood_uses_local_graph_on_packaged_export_mismatch( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: import networkx as nx graph_dir = fake_claude / "skill-wiki" / "graphify-out" graph_dir.mkdir(parents=True) (graph_dir / "graph-export-manifest.json").write_text( json.dumps({"version": 1, "export_id": "old-local-export"}), encoding="utf-8", ) monkeypatch.setattr(mt, "packaged_graph_export_id", lambda: "new-packaged-export") G = nx.Graph() G.add_node("mcp-server:github", label="GitHub", type="mcp-server", tags=["git"]) monkeypatch.setattr(mt, "load_dashboard_graph", lambda: G) result = mt.graph_neighborhood("github", entity_type="mcp-server") assert result["center"] == "mcp-server:github" assert result["nodes"][0]["data"]["type"] == "mcp-server" def test_dashboard_index_extraction_works_with_installed_graph_report( fake_claude: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: graph_dir = fake_claude / "skill-wiki" / "graphify-out" graph_dir.mkdir(parents=True) (graph_dir / "graph-export-manifest.json").write_text( json.dumps({"version": 1, "export_id": "local-export"}), encoding="utf-8", ) (graph_dir / "graph-report.md").write_text( "> Nodes: 12 | Edges: 34 | Communities: 2\n", encoding="utf-8", ) seed = tmp_path / "dashboard-neighborhoods.sqlite3" conn = sqlite3.connect(seed) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.execute("INSERT INTO meta VALUES(?,?)", ("export_id", json.dumps("local-export"))) conn.commit() finally: conn.close() archive_manifest = tmp_path / "graph-export-manifest.json" archive_manifest.write_text( json.dumps({"version": 1, "export_id": "local-export"}), encoding="utf-8", ) archive = tmp_path / "wiki-graph-runtime.tar.gz" with tarfile.open(archive, "w:gz") as tar: tar.add(archive_manifest, arcname="./graphify-out/graph-export-manifest.json") tar.add(seed, arcname="./graphify-out/dashboard-neighborhoods.sqlite3") monkeypatch.setattr(mt, "dashboard_graph_index_archives", lambda: [archive]) monkeypatch.setattr(mt, "packaged_graph_export_id", lambda: "local-export") wiki_dir = fake_claude / "skill-wiki" assert graph_service.dashboard_graph_index_path(wiki_dir) == ( graph_dir / "dashboard-neighborhoods.sqlite3" ) assert graph_service.dashboard_graph_manifest_export_id(wiki_dir) == "local-export" assert graph_service.dashboard_index_matches_manifest(seed, wiki_dir) assert mt.ensure_dashboard_graph_index() == graph_dir / "dashboard-neighborhoods.sqlite3" def test_graph_neighborhood_bypasses_archive_index_when_runtime_overlays_exist( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: import networkx as nx overlay = fake_claude / "skill-wiki" / "graphify-out" / "entity-overlays.jsonl" overlay.parent.mkdir(parents=True) overlay.write_text('{"source":"test"}\n', encoding="utf-8") monkeypatch.setattr( mt, "dashboard_graph_index_archives", lambda: (_ for _ in ()).throw(AssertionError("archive index should be bypassed")), ) G = nx.Graph() G.add_node("skill:local-overlay", label="local-overlay", type="skill", tags=["local"]) monkeypatch.setattr(mt, "load_dashboard_graph", lambda: G) result = mt.graph_neighborhood("local-overlay", entity_type="skill") assert result["center"] == "skill:local-overlay" def test_graph_neighborhood_rejects_stale_dashboard_index( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: import networkx as nx graph_dir = fake_claude / "skill-wiki" / "graphify-out" graph_dir.mkdir(parents=True) (graph_dir / "graph-export-manifest.json").write_text( json.dumps({"version": 1, "export_id": "new-export"}), encoding="utf-8", ) index_path = graph_dir / "dashboard-neighborhoods.sqlite3" conn = sqlite3.connect(index_path) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.execute( "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT," "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)" ) conn.execute( "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT," "PRIMARY KEY(slug,type,node_id))" ) conn.execute("CREATE TABLE neighbors(source TEXT PRIMARY KEY, payload BLOB NOT NULL)") conn.executemany("INSERT INTO meta VALUES(?,?)", [("export_id", json.dumps("old-export"))]) conn.commit() finally: conn.close() G = nx.Graph() G.add_node("skill:fallback", label="fallback", type="skill", tags=[]) monkeypatch.setattr(mt, "packaged_graph_export_id", lambda: None) monkeypatch.setattr(mt, "load_dashboard_graph", lambda: G) result = mt.graph_neighborhood("fallback", entity_type="skill") assert result["center"] == "skill:fallback" assert not index_path.exists() def test_graph_neighborhood_rejects_orphan_dashboard_index_without_manifest( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: import networkx as nx graph_dir = fake_claude / "skill-wiki" / "graphify-out" graph_dir.mkdir(parents=True) index_path = graph_dir / "dashboard-neighborhoods.sqlite3" conn = sqlite3.connect(index_path) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.executemany("INSERT INTO meta VALUES(?,?)", [("export_id", json.dumps("old-export"))]) conn.commit() finally: conn.close() G = nx.Graph() G.add_node("skill:fallback", label="fallback", type="skill", tags=[]) monkeypatch.setattr(mt, "load_dashboard_graph", lambda: G) result = mt.graph_neighborhood("fallback", entity_type="skill") assert result["center"] == "skill:fallback" assert not index_path.exists() def test_graph_index_node_size_uses_live_sidecar_when_scores_missing( fake_claude: Path, ) -> None: _write_graph_manifest(fake_claude, "test-export") _write_sidecar(fake_claude, "python-patterns", { "slug": "python-patterns", "subject_type": "skill", "grade": "A", "score": 1.0, "signals": {"telemetry": {"score": 1.0}}, }) index_path = fake_claude / "skill-wiki" / "graphify-out" / "dashboard-neighborhoods.sqlite3" index_path.parent.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(index_path) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.execute( "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT," "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)" ) conn.execute( "CREATE TABLE slug_index(slug TEXT,type TEXT,node_id TEXT," "PRIMARY KEY(slug,type,node_id))" ) conn.execute("CREATE TABLE neighbors(source TEXT PRIMARY KEY, payload BLOB NOT NULL)") conn.executemany( "INSERT INTO meta VALUES(?,?)", [("max_degree", "1"), ("top_k", "40"), ("export_id", json.dumps("test-export"))], ) conn.execute( "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)", ("skill:python-patterns", "python-patterns", "skill", "[]", "", None, None, 0), ) conn.execute( "INSERT INTO slug_index VALUES(?,?,?)", ("python-patterns", "skill", "skill:python-patterns"), ) conn.execute("INSERT INTO neighbors VALUES(?,?)", ("skill:python-patterns", zlib.compress(b"[]"))) conn.commit() finally: conn.close() result = mt.graph_neighborhood("python-patterns", entity_type="skill") node = result["nodes"][0]["data"] assert node["size_signal"] > 0.7 assert "quality 1.000" in node["size_reason"] assert "usage 1.000" in node["size_reason"] def test_graph_neighborhood_empty_when_graph_absent( fake_claude: Path, monkeypatch, ) -> None: # Force load_graph to raise so the helper returns the empty shape # deterministically, independent of whether the user's graph is built. def _bad(*_a, **_k): raise RuntimeError("no graph") # resolve_graph may be imported lazily inside _graph_neighborhood — force # the import to yield a stub that raises. import sys fake = type("M", (), {"load_graph": _bad}) # ctx.monitor.compat lazy-imports 'from ctx.core.graph.resolve_graph import load_graph' # at call time; inject at the canonical dotted path so the lazy import # resolves to our stub. Also populate the legacy shim path belt-and- # braces in case a downstream path still routes through it. monkeypatch.setitem(sys.modules, "ctx.core.graph.resolve_graph", fake) monkeypatch.setitem(sys.modules, "resolve_graph", fake) result = mt.graph_neighborhood("python-patterns") assert result == {"nodes": [], "edges": [], "center": None} def test_render_home_shows_stat_grid_even_with_no_sessions(fake_claude: Path) -> None: """The previous home page was near-blank when the user had no sessions. The rc11 layout must show the six stat cards + grade pills + empty-state messages so the page never feels empty.""" html_out = mt.render_home() # Six stat-card titles must all be present regardless of data volume. for label in ("Currently loaded", "Sidecars", "Wiki entities", "Knowledge graph", "Audit events", "Sessions"): assert label in html_out # Links into each section. for href in ("/loaded", "/skills", "/graph", "/logs", "/events", "/sessions"): assert href in html_out # Grade pills always render (values may be zero). for grade in ("A", "B", "C", "D", "F"): assert f"grade-{grade}" in html_out # Empty-state copy kicks in when there are no sessions / audit entries. assert "No sessions recorded" in html_out or "Recent sessions" in html_out def test_render_home_formats_large_counts_with_commas( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: monkeypatch.setattr(mt, "summarize_sessions", lambda: []) monkeypatch.setattr(mt, "read_manifest", lambda: {"load": [None] * 10000}) monkeypatch.setattr(mt, "read_jsonl", lambda *_args, **_kwargs: []) monkeypatch.setattr(mt, "graph_stats", lambda: { "nodes": 79958, "edges": 1778069, "available": True, }) monkeypatch.setattr(mt, "wiki_stats", lambda: { "skills": 68494, "agents": 467, "mcps": 10790, "harnesses": 207, "total": 79958, "split_known": True, }) monkeypatch.setattr(mt, "runtime_lifecycle_summary", lambda: { "validations_total": 10000, "validation_failures": 1000, "open_escalations_total": 100, }) html_out = mt.render_home() assert ">10,000" in html_out assert ">79,958" in html_out assert "1,778,069 edges" in html_out assert "68,494 skills" in html_out assert "10,790 MCPs" in html_out assert "10000" not in html_out assert "100000" not in html_out def test_render_home_defers_sidecar_grade_scan( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: def fail_scan() -> dict[str, int]: raise AssertionError("home page should not synchronously scan sidecars") monkeypatch.setattr(mt, "grade_distribution", fail_scan) html_out = mt.render_home() assert "/api/grades.json" in html_out assert "home-sidecar-count" in html_out assert "data-home-grade='A'" in html_out def test_api_grades_reuses_kpi_summary_without_sidecar_scan() -> None: class Summary: def to_dict(self) -> dict[str, object]: return {"grade_counts": {"A": 2, "B": 1, "F": 4}} def fail_scan() -> dict[str, object]: raise AssertionError("grades API should reuse the cached KPI summary") response = readonly_api.handle_readonly_route( "api_grades", {}, {}, readonly_api.ReadOnlyApiDeps( summarize_sessions=lambda: {}, read_manifest=lambda: {}, status_payload=lambda: {}, kpi_summary=lambda: Summary(), grade_distribution_payload=fail_scan, sidecar_page_payload=lambda _qs: {}, runtime_lifecycle_summary=lambda: {}, skillspector_audit_payload=lambda _qs: {}, effective_config_payload=lambda: {}, search_wiki_entities=lambda _q, _type, _limit: [], wiki_entity_detail=lambda _slug, _type: None, load_sidecar=lambda _slug, _type: None, graph_neighborhood=lambda _slug, _hops, _limit, _type: {}, normalize_dashboard_entity_type=lambda raw: raw, ), ) assert response is not None assert response.payload == { "grades": {"A": 2, "B": 1, "C": 0, "D": 0, "F": 4}, "total": 7, } def test_render_skills_emits_sidebar_filters(fake_claude: Path) -> None: _write_sidecar(fake_claude, "a", {"slug": "a", "grade": "A", "raw_score": 0.9, "subject_type": "skill"}) _write_sidecar(fake_claude, "b", {"slug": "b", "grade": "F", "raw_score": 0.1, "subject_type": "agent", "hard_floor": "intake_fail"}) html_out = mt.render_skills() # Sidebar must expose text search plus grade/type filters. assert "id='skill-search'" in html_out assert "class='grade-filter'" in html_out assert "class='type-filter'" in html_out # Cards, not a legacy table-row element. assert "class='skill-card'" in html_out # Per-card links to sidecar/wiki/graph drill-downs. assert ">sidecar" in html_out assert ">wiki" in html_out assert ">graph" in html_out def test_render_skills_paginates_sidecar_page(fake_claude: Path) -> None: for slug in ("a", "b", "c"): _write_sidecar(fake_claude, slug, { "slug": slug, "grade": "A", "raw_score": 0.9, "subject_type": "skill", }) html_out = mt.render_skills({"limit": "2"}) assert "Showing 1-2 of 3 sidecars" in html_out assert "next" in html_out assert "a" in html_out assert "b" in html_out assert "c" not in html_out def test_sidecar_page_payload_searches_full_catalog(fake_claude: Path) -> None: for slug in ("alpha-review", "beta-build", "gamma-review"): _write_sidecar(fake_claude, slug, { "slug": slug, "grade": "A", "raw_score": 0.9, "subject_type": "skill", }) payload = mt.sidecar_page_payload({"q": "review", "limit": "1"}) assert payload["total"] == 2 assert payload["pages"] == 2 assert [item["slug"] for item in payload["items"]] == ["alpha-review"] def test_sidecar_page_payload_filters_type_grade_and_floor(fake_claude: Path) -> None: _write_sidecar(fake_claude, "agent-a", { "slug": "agent-a", "grade": "A", "raw_score": 0.9, "subject_type": "agent", }) _write_sidecar(fake_claude, "skill-a", { "slug": "skill-a", "grade": "A", "raw_score": 0.9, "subject_type": "skill", }) _write_sidecar(fake_claude, "agent-floored", { "slug": "agent-floored", "grade": "A", "raw_score": 0.9, "subject_type": "agent", "hard_floor": "intake_fail", }) payload = mt.sidecar_page_payload({ "type": "agent", "grade": "A", "hide_floor": "1", }) assert payload["total"] == 1 assert payload["items"][0]["slug"] == "agent-a" def test_sidecar_page_payload_reuses_cached_search_records( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: for slug in ("alpha-review", "beta-review"): _write_sidecar(fake_claude, slug, { "slug": slug, "grade": "A", "raw_score": 0.9, "subject_type": "skill", }) original_read = sidecar_service.read_sidecar_file reads = 0 def counting_read(path: Path) -> dict | None: nonlocal reads reads += 1 return original_read(path) monkeypatch.setattr(sidecar_service, "read_sidecar_file", counting_read) first = mt.sidecar_page_payload({"q": "review"}) reads_after_first_search = reads second = mt.sidecar_page_payload({"q": "review"}) assert [item["slug"] for item in first["items"]] == ["alpha-review", "beta-review"] assert [item["slug"] for item in second["items"]] == ["alpha-review", "beta-review"] assert reads_after_first_search == 2 assert reads == reads_after_first_search _write_sidecar(fake_claude, "delta-review", { "slug": "delta-review", "grade": "A", "raw_score": 0.8, "subject_type": "skill", }) refreshed = mt.sidecar_page_payload({"q": "delta"}) assert [item["slug"] for item in refreshed["items"]] == ["delta-review"] assert reads > reads_after_first_search def test_sidecar_page_payload_cache_invalidates_on_file_rewrite( fake_claude: Path, ) -> None: path = fake_claude / "skill-quality" / "alpha-review.json" path.write_text( json.dumps({ "slug": "alpha-review", "grade": "A", "raw_score": 0.9, "subject_type": "skill", }), encoding="utf-8", ) first = mt.sidecar_page_payload({"q": "review"}) assert first["items"][0]["grade"] == "A" path.write_text( json.dumps({ "slug": "alpha-review", "grade": "F", "raw_score": 0.1, "subject_type": "skill", "notes": "changed content to force a different cache signature size", }), encoding="utf-8", ) second = mt.sidecar_page_payload({"q": "review"}) assert second["items"][0]["grade"] == "F" def test_sidecars_api_applies_route_filters_and_limit_bounds( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: _write_sidecar(fake_claude, "review-agent-a", { "slug": "review-agent-a", "grade": "A", "raw_score": 0.9, "subject_type": "agent", }) _write_sidecar(fake_claude, "review-agent-floored", { "slug": "review-agent-floored", "grade": "A", "raw_score": 0.8, "subject_type": "agent", "hard_floor": "never_load_stale", }) _write_sidecar(fake_claude, "review-skill", { "slug": "review-skill", "grade": "A", "raw_score": 0.7, "subject_type": "skill", }) _write_sidecar(fake_claude, "review-agent-b", { "slug": "review-agent-b", "grade": "B", "raw_score": 0.95, "subject_type": "agent", }) direct = readonly_api.handle_readonly_route( "api_sidecars", {}, {"q": "review", "type": "agent", "grade": "A", "hide_floor": "1", "limit": "1"}, readonly_api.ReadOnlyApiDeps( summarize_sessions=lambda: {}, read_manifest=lambda: {}, status_payload=lambda: {}, kpi_summary=lambda: None, grade_distribution_payload=lambda: {}, sidecar_page_payload=mt.sidecar_page_payload, runtime_lifecycle_summary=lambda: {}, skillspector_audit_payload=lambda _qs: {}, effective_config_payload=lambda: {}, search_wiki_entities=lambda _q, _type, _limit: [], wiki_entity_detail=lambda _slug, _type: None, load_sidecar=lambda _slug, _type: None, graph_neighborhood=lambda _slug, _hops, _limit, _type: {}, normalize_dashboard_entity_type=lambda raw: raw, ), ) assert direct is not None assert direct.status == 200 assert direct.payload["limit"] == 1 assert [item["slug"] for item in direct.payload["items"]] == ["review-agent-a"] server, thread, port = _serve_monitor(monkeypatch) try: status, payload = _get_json( port, "/api/sidecars.json?q=review&type=agent&grade=A&hide_floor=1&limit=1", ) assert status == 200 assert payload["total"] == 1 assert payload["limit"] == 1 assert payload["filtered"] is True assert payload["types"] == ["agent"] assert payload["grades"] == ["A"] assert payload["hide_floor"] is True assert [item["slug"] for item in payload["items"]] == ["review-agent-a"] status, floor_payload = _get_json(port, "/api/sidecars.json?limit=0") assert status == 200 assert floor_payload["limit"] == 1 assert len(floor_payload["items"]) == 1 status, capped_payload = _get_json(port, "/api/sidecars.json?limit=9999") assert status == 200 assert capped_payload["limit"] == 500 finally: server.shutdown() thread.join(timeout=5) server.server_close() def test_render_wiki_index_lists_entities(fake_claude: Path) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" agents_dir = fake_claude / "skill-wiki" / "entities" / "agents" skills_dir.mkdir(parents=True) agents_dir.mkdir(parents=True) (skills_dir / "python-patterns.md").write_text( "---\nname: python-patterns\ntype: skill\n" "description: Idiomatic Python patterns\ntags: [python, patterns]\n" "---\n# body\n", encoding="utf-8", ) (agents_dir / "code-reviewer.md").write_text( "---\nname: code-reviewer\ntype: agent\n" "description: Reviews code for issues\ntags: [review, quality]\n" "---\n# body\n", encoding="utf-8", ) html_out = mt.render_wiki_index() direct_out = wiki_page.render_wiki_index_page( entries=[{ "slug": "python-patterns", "display_slug": "python-patterns", "type": "skill", "description": "Idiomatic Python patterns", "tags": ["python", "patterns"], "search_tags": ["python", "patterns"], }], selected_type=None, initial_query="python", total_available=1, type_counts={"skill": 1, "agent": 0, "mcp-server": 0, "harness": 0}, grade_by_key={("python-patterns", "skill"): "A"}, dashboard_entity_types=mt.DASHBOARD_ENTITY_TYPES, layout=lambda _title, body: body, ) # Both entities render as cards with their slug. assert "python-patterns" in html_out assert "python-patterns" in direct_out assert "grade-A" in direct_out assert "code-reviewer" in html_out # Descriptions surface on the card. assert "Idiomatic Python patterns" in html_out assert "Reviews code for issues" in html_out # Search + type filter must be present. assert "id='wiki-search'" in html_out assert "class='wiki-type-filter'" in html_out # Cards link to the typed per-entity wiki page so duplicate slugs can # disambiguate skill/agent/MCP/harness pages. assert "href='/wiki/python-patterns?type=skill'" in html_out assert "href='/wiki/code-reviewer?type=agent'" in html_out def test_render_wiki_index_wrapper_matches_extracted_orchestration( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) (skills_dir / "python-patterns.md").write_text( "---\nname: python-patterns\ntype: skill\n" "description: Idiomatic Python patterns\ntags: [python, patterns]\n" "---\n# body\n", encoding="utf-8", ) monkeypatch.setattr(mt, "wiki_render_cache_key", lambda _type, _query: None) wrapper_html = mt.render_wiki_index(query="python") direct_html = wiki_page.render_wiki_index( query="python", normalize_dashboard_entity_type=mt.normalize_dashboard_entity_type, wiki_render_cache_key=mt.wiki_render_cache_key, read_memory_cache=mt.wiki_render_memory_cache_get, write_memory_cache=mt.wiki_render_memory_cache_set, disk_cache_token=mt.cache_service.disk_cache_token, read_html_disk_cache=mt.cache_service.read_html_disk_cache, write_html_disk_cache=mt.cache_service.write_html_disk_cache, wiki_render_disk_cache_path=mt.wiki_render_disk_cache_path, wiki_index_entries=mt.wiki_index_entries, search_wiki_entities=mt.search_wiki_entities, wiki_stats=mt.wiki_stats, load_sidecar=mt.load_sidecar, dashboard_entity_types=mt.DASHBOARD_ENTITY_TYPES, layout=mt.layout, ) assert wrapper_html == direct_html assert "python-patterns" in wrapper_html def test_wiki_index_entries_use_dashboard_index_without_markdown_pages( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: _write_graph_manifest(fake_claude, "test-export") graph_dir = fake_claude / "skill-wiki" / "graphify-out" graph_dir.mkdir(parents=True, exist_ok=True) index_path = graph_dir / "dashboard-neighborhoods.sqlite3" conn = sqlite3.connect(index_path) try: conn.execute("CREATE TABLE meta(key TEXT PRIMARY KEY, value TEXT NOT NULL)") conn.execute( "CREATE TABLE nodes(id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT," "description TEXT,quality_score REAL,usage_score REAL,degree INTEGER)" ) conn.executemany( "INSERT INTO meta VALUES(?,?)", [ ("export_id", json.dumps("test-export")), ("nodes_count", "2"), ("edges_count", "0"), ], ) conn.executemany( "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)", [ ( "skill:python-patterns", "python-patterns", "skill", '["python","patterns"]', "Idiomatic Python patterns", 0.9, 0.1, 5, ), ( "agent:code-reviewer", "code-reviewer", "agent", '["review","quality"]', "Reviews code for issues", 0.8, 0.1, 4, ), ], ) conn.commit() finally: conn.close() entries = mt.wiki_index_entries(limit_per_type=10) slugs = {entry["slug"] for entry in entries} assert slugs == {"python-patterns", "code-reviewer"} assert entries[0]["description"] == "Idiomatic Python patterns" assert wiki_service.index_entries( fake_claude / "skill-wiki", index_path, limit_per_type=10, index_matches_manifest=lambda path: path == index_path, ) == entries assert wiki_service.search_entities_from_index( index_path, "python", "skill", limit=5, index_matches_manifest=lambda path: path == index_path, ) == [{ "slug": "python-patterns", "display_slug": "python-patterns", "type": "skill", "title": "python-patterns", "description": "Idiomatic Python patterns", "tags": ["python", "patterns"], "path": "", "href": "/wiki/python-patterns?type=skill", "quality_score": 0.9, "usage_score": 0.1, "degree": 5, }] def fail_sidecar_probe(*args: object, **kwargs: object) -> object: raise AssertionError("index-backed wiki catalog should not probe sidecars") monkeypatch.setattr(mt, "load_sidecar", fail_sidecar_probe) html_out = mt.render_wiki_index(query="python") assert "href='/wiki/python-patterns?type=skill'" in html_out assert "Idiomatic Python patterns" in html_out assert "grade-A" in html_out assert (fake_claude / ".ctx-monitor-wiki-cache.json").is_file() monkeypatch.setattr(mt, "WIKI_RENDER_CACHE_KEY", None) monkeypatch.setattr(mt, "WIKI_RENDER_CACHE_VALUE", None) def fail_entry_rebuild(*args: object, **kwargs: object) -> object: raise AssertionError("fresh process should read the rendered wiki cache") monkeypatch.setattr(mt, "wiki_index_entries", fail_entry_rebuild) assert mt.render_wiki_index(query="python") == html_out def test_render_wiki_index_query_uses_bounded_search( monkeypatch: pytest.MonkeyPatch, ) -> None: def fail_catalog_rebuild(*args: object, **kwargs: object) -> object: raise AssertionError("queried catalog should use bounded search results") monkeypatch.setattr(mt, "wiki_index_entries", fail_catalog_rebuild) monkeypatch.setattr( mt, "search_wiki_entities", lambda query, entity_type=None, limit=80: [{ "slug": "code-reviewer", "display_slug": "code-reviewer", "type": entity_type or "agent", "description": f"{query} result", "tags": ["review"], "search_tags": ["review"], }], ) html_out = mt.render_wiki_index(entity_type="agent", query="review") assert "code-reviewer" in html_out assert "review result" in html_out assert "href='/wiki/code-reviewer?type=agent'" in html_out def test_render_wiki_index_supports_type_query_and_autocomplete(fake_claude: Path) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" agents_dir = fake_claude / "skill-wiki" / "entities" / "agents" skills_dir.mkdir(parents=True) agents_dir.mkdir(parents=True) (skills_dir / "python-patterns.md").write_text( "---\ntype: skill\ndescription: Python work\ntags: [python]\n---\n# body\n", encoding="utf-8", ) (agents_dir / "code-reviewer.md").write_text( "---\ntype: agent\ndescription: Review work\ntags: [review]\n---\n# body\n", encoding="utf-8", ) html_out = mt.render_wiki_index(entity_type="agent", query="review") assert "id='wiki-entity-suggestions'" in html_out assert "value='code-reviewer'" in html_out assert 'wsearch.value = "review";' in html_out assert "href='/wiki?type=agent'" in html_out assert "value='agent' checked" in html_out assert "value='skill' checked" not in html_out def test_catalog_route_serves_searchable_wiki_catalog( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: agents_dir = fake_claude / "skill-wiki" / "entities" / "agents" agents_dir.mkdir(parents=True) (agents_dir / "code-reviewer.md").write_text( "---\ntype: agent\ndescription: Review work\ntags: [review]\n---\n# body\n", encoding="utf-8", ) server, thread, port = _serve_monitor(monkeypatch) try: with urllib.request.urlopen( f"http://127.0.0.1:{port}/catalog?type=agent&q=review", timeout=5, ) as response: body = response.read().decode("utf-8") assert response.status == 200 assert "id='wiki-search'" in body assert "code-reviewer" in body assert "href='/wiki/code-reviewer?type=agent'" in body finally: server.shutdown() server.server_close() thread.join(timeout=2) def test_render_wiki_index_hides_legacy_skill_source_prefix(fake_claude: Path) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) (skills_dir / "skills-sh-owner-repo-brainstorming.md").write_text( "---\nname: skills-sh-owner-repo-brainstorming\ntype: skill\n" "description: Brainstorm with constraints\ntags: [brainstorming]\n" "---\n# body\n", encoding="utf-8", ) html_out = mt.render_wiki_index() assert "owner-repo-brainstorming" in html_out assert "href='/wiki/skills-sh-owner-repo-brainstorming?type=skill'" in html_out def test_render_wiki_index_does_not_bleed_grade_across_duplicate_slugs( fake_claude: Path, ) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" harnesses_dir = fake_claude / "skill-wiki" / "entities" / "harnesses" skills_dir.mkdir(parents=True) harnesses_dir.mkdir(parents=True) (skills_dir / "langgraph.md").write_text( "---\nname: langgraph\ntype: skill\n---\n# body\n", encoding="utf-8", ) (harnesses_dir / "langgraph.md").write_text( "---\nname: langgraph\ntype: harness\n---\n# body\n", encoding="utf-8", ) _write_sidecar(fake_claude, "langgraph", { "slug": "langgraph", "subject_type": "skill", "grade": "D", "raw_score": 0.2, }) html_out = mt.render_wiki_index() harness_start = html_out.index("href='/wiki/langgraph?type=harness'") harness_card = html_out[harness_start:html_out.index("", harness_start)] assert "grade-D" not in harness_card assert "harness" in harness_card def test_render_wiki_index_uses_visible_sidecars_without_full_scan( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) (skills_dir / "python-patterns.md").write_text( "---\nname: python-patterns\ntype: skill\n---\n# body\n", encoding="utf-8", ) _write_sidecar(fake_claude, "python-patterns", { "slug": "python-patterns", "subject_type": "skill", "grade": "A", "raw_score": 0.9, }) def fail_full_sidecar_scan() -> list[dict]: raise AssertionError("_render_wiki_index should not scan every sidecar") monkeypatch.setattr(mt, "all_sidecars", fail_full_sidecar_scan) html_out = mt.render_wiki_index() assert "href='/wiki/python-patterns?type=skill'" in html_out assert "grade-A" in html_out def test_render_wiki_index_empty_when_no_entities(fake_claude: Path) -> None: html_out = mt.render_wiki_index() # No entities → still renders the chrome + a helpful empty state. assert "

    Wiki

    " in html_out assert "No wiki entities found" in html_out def test_render_wiki_index_rejects_unsafe_filenames(fake_claude: Path) -> None: """A file on disk with a slug that doesn't pass the allowlist must not appear in the index, even if glob returns it.""" skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) # Valid entry alongside an uppercase-first filename (fails ^[a-z0-9]). (skills_dir / "python-patterns.md").write_text( "---\nname: python-patterns\n---\n", encoding="utf-8", ) (skills_dir / "Bad-Start.md").write_text( "---\nname: Bad-Start\n---\n", encoding="utf-8", ) entries = mt.wiki_index_entries() slugs = {e["slug"] for e in entries} assert "python-patterns" in slugs assert "Bad-Start" not in slugs def test_render_manage_includes_crud_and_upload_wizard(fake_claude: Path) -> None: html_out = mt.render_manage(mutations_enabled=True) direct_html = manage_page.render_manage( mutations_enabled=True, token="token", initial_results_json=mt.json_for_script([]), entity_types=("skill", "agent", "mcp-server", "harness"), inline_script=lambda _name: "", layout=lambda _title, body: body, ) assert "

    Manage catalog

    " in html_out assert "id='manage-search'" in html_out assert "id='entity-editor-form'" in html_out assert "data-testid='entity-delete-button'" in html_out assert "Add or update entity" in html_out assert "window.CTX_MONITOR_MANAGE" in html_out assert mt.monitor_asset_text("monitor-manage.js").strip() in html_out assert "id='manage-search'" in direct_html assert "window.CTX_MONITOR_MANAGE" in direct_html assert "mcp-server" in direct_html def test_entity_search_and_detail_apis_support_edit_flow( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) (skills_dir / "python-patterns.md").write_text( "---\n" "title: Python Patterns\n" "type: skill\n" "description: Idiomatic Python patterns\n" "tags: [python, patterns]\n" "---\n" "# Python Patterns\n\nUse dataclasses and context managers.\n", encoding="utf-8", ) server, thread, port = _serve_monitor(monkeypatch) try: status, payload = _get_json( port, "/api/entities/search.json?q=patterns&type=skill", ) assert status == 200 assert payload["results"][0]["slug"] == "python-patterns" assert payload["results"][0]["type"] == "skill" status, detail = _get_json( port, "/api/entity/python-patterns.json?type=skill", ) assert status == 200 assert detail["slug"] == "python-patterns" assert detail["frontmatter"]["description"] == "Idiomatic Python patterns" assert "Use dataclasses" in detail["body"] finally: server.shutdown() thread.join(timeout=5) server.server_close() def test_entity_search_uses_dashboard_index_for_live_graph_search( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: index_path = fake_claude / "skill-wiki" / "graphify-out" / "dashboard-neighborhoods.sqlite3" index_path.parent.mkdir(parents=True) conn = sqlite3.connect(index_path) try: conn.execute( "CREATE TABLE nodes(" "id TEXT PRIMARY KEY,label TEXT,type TEXT,tags TEXT,description TEXT," "quality_score REAL,usage_score REAL,degree INTEGER)", ) conn.execute( "INSERT INTO nodes VALUES(?,?,?,?,?,?,?,?)", ( "skill:brainstorming", "brainstorming", "skill", json.dumps(["planning", "creative"]), "Generate better project options before implementation.", 0.91, 0.4, 17, ), ) conn.commit() finally: conn.close() monkeypatch.setattr(mt, "dashboard_graph_index_path", lambda: index_path) monkeypatch.setattr(mt, "dashboard_index_matches_manifest", lambda _path: True) results = mt.search_wiki_entities("brainstorm", "skill", limit=5) assert results[0]["slug"] == "brainstorming" assert results[0]["type"] == "skill" assert results[0]["tags"] == ["planning", "creative"] def test_entity_search_api_rejects_bad_type_and_clamps_limit( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: skills_dir = fake_claude / "skill-wiki" / "entities" / "skills" skills_dir.mkdir(parents=True) for i in range(205): (skills_dir / f"bulk-{i:03d}.md").write_text( "---\n" "type: skill\n" "description: Bulk search boundary entity\n" "tags: [bulk]\n" "---\n" "# Bulk\n", encoding="utf-8", ) server, thread, port = _serve_monitor(monkeypatch) try: status, bad_type = _get_json( port, "/api/entities/search.json?q=bulk&type=bad", ) assert status == 400 assert "unsupported entity_type" in bad_type["detail"] status, capped = _get_json( port, "/api/entities/search.json?q=bulk&type=skill&limit=9999", ) assert status == 200 assert capped["total"] == 200 assert len(capped["results"]) == 200 assert capped["results"][0]["slug"] == "bulk-000" status, floored = _get_json( port, "/api/entities/search.json?q=bulk&type=skill&limit=0", ) assert status == 200 assert floored["total"] == 1 assert [item["slug"] for item in floored["results"]] == ["bulk-000"] finally: server.shutdown() thread.join(timeout=5) server.server_close() def test_entity_upsert_api_writes_wiki_page_and_queues_graph_refresh( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: server, thread, port = _serve_monitor(monkeypatch) try: status, payload = _post_json( port, "/api/entity/upsert", { "slug": "custom-reviewer", "entity_type": "agent", "title": "Custom Reviewer", "description": "Reviews Python changes with local policy.", "tags": "python, review, policy", "source_url": "https://example.com/custom-reviewer", "body": "# Custom Reviewer\n\nUse this agent before merging Python changes.\n", }, token="test-token", ) assert status == 200 assert payload["ok"] is True assert payload["detail"].startswith("saved agent:custom-reviewer") entity_path = ( fake_claude / "skill-wiki" / "entities" / "agents" / "custom-reviewer.md" ) text = entity_path.read_text(encoding="utf-8") assert "title: Custom Reviewer" in text assert "type: agent" in text assert "tags: [python, review, policy]" in text assert "Use this agent before merging Python changes." in text jobs = wiki_queue.list_recent_jobs( wiki_queue.queue_db_path(fake_claude / "skill-wiki"), limit=10, ) assert [job.kind for job in jobs[:2]] == [ wiki_queue.GRAPH_EXPORT_JOB, wiki_queue.ENTITY_UPSERT_JOB, ] finally: server.shutdown() thread.join(timeout=5) server.server_close() def test_entity_upsert_api_requires_confirmation_for_existing_page( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: entity_path = ( fake_claude / "skill-wiki" / "entities" / "agents" / "custom-reviewer.md" ) entity_path.parent.mkdir(parents=True) entity_path.write_text( "---\ntitle: Custom Reviewer\ntype: agent\ntags: [python]\n" "---\n# Custom Reviewer\n\nOriginal body.\n", encoding="utf-8", ) server, thread, port = _serve_monitor(monkeypatch) try: status, payload = _post_json( port, "/api/entity/upsert", { "slug": "custom-reviewer", "entity_type": "agent", "title": "Custom Reviewer", "body": "# Custom Reviewer\n\nReplacement body.\n", }, token="test-token", ) assert status == 400 assert payload["ok"] is False assert "confirm_update=true" in payload["detail"] assert "Original body." in entity_path.read_text(encoding="utf-8") status, payload = _post_json( port, "/api/entity/upsert", { "slug": "custom-reviewer", "entity_type": "agent", "title": "Custom Reviewer", "body": "# Custom Reviewer\n\nReplacement body.\n", "confirm_update": "true", }, token="test-token", ) assert status == 200 assert payload["ok"] is True assert "Replacement body." in entity_path.read_text(encoding="utf-8") finally: server.shutdown() thread.join(timeout=5) server.server_close() def test_entity_delete_api_removes_wiki_page_and_queues_graph_refresh( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: harness_dir = fake_claude / "skill-wiki" / "entities" / "harnesses" harness_dir.mkdir(parents=True) entity_path = harness_dir / "local-harness.md" entity_path.write_text( "---\ntitle: Local Harness\ntype: harness\ntags: [local, llm]\n" "---\n# Local Harness\n", encoding="utf-8", ) server, thread, port = _serve_monitor(monkeypatch) try: status, payload = _post_json( port, "/api/entity/delete", {"slug": "local-harness", "entity_type": "harness"}, token="test-token", ) assert status == 200 assert payload == { "ok": True, "detail": "deleted harness:local-harness and queued graph refresh", } assert not entity_path.exists() jobs = wiki_queue.list_recent_jobs( wiki_queue.queue_db_path(fake_claude / "skill-wiki"), limit=10, ) assert [job.kind for job in jobs] == [wiki_queue.ENTITY_UPSERT_JOB] assert jobs[0].payload["action"] == "delete" finally: server.shutdown() thread.join(timeout=5) server.server_close() def test_entity_delete_unloads_live_entity_before_removing_page( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: assert dashboard_entities.normalize_entity_tags(["Code Review"]) == ["code-review"] skill_dir = fake_claude / "skill-wiki" / "entities" / "skills" skill_dir.mkdir(parents=True) entity_path = skill_dir / "python-patterns.md" entity_path.write_text( "---\ntitle: Python Patterns\ntype: skill\n---\n# Python Patterns\n", encoding="utf-8", ) calls: list[tuple[str, str]] = [] monkeypatch.setattr( mt, "read_manifest", lambda: {"load": [{"skill": "python-patterns", "entity_type": "skill"}]}, ) def fake_unload(slug: str, entity_type: str = "skill") -> tuple[bool, str]: calls.append((slug, entity_type)) return True, "unloaded" monkeypatch.setattr(mt, "perform_unload", fake_unload) ok, detail = mt.delete_wiki_entity("python-patterns", "skill") assert ok is True assert "deleted skill:python-patterns" in detail assert calls == [("python-patterns", "skill")] assert not entity_path.exists() def test_entity_delete_keeps_page_when_live_unload_fails( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: harness_dir = fake_claude / "skill-wiki" / "entities" / "harnesses" harness_dir.mkdir(parents=True) entity_path = harness_dir / "local-harness.md" entity_path.write_text( "---\ntitle: Local Harness\ntype: harness\n---\n# Local Harness\n", encoding="utf-8", ) monkeypatch.setattr( mt, "read_manifest", lambda: {"load": [{"skill": "local-harness", "entity_type": "harness"}]}, ) monkeypatch.setattr( mt, "perform_unload", lambda slug, entity_type="skill": (False, "use ctx-harness-install"), ) ok, detail = mt.delete_wiki_entity("local-harness", "harness") assert ok is False assert "is loaded" in detail assert entity_path.exists() def test_sidecar_cache_invalidates_on_file_rewrite(fake_claude: Path) -> None: sidecar_service.reset_caches() path = fake_claude / "skill-quality" / "alpha.json" path.parent.mkdir(parents=True, exist_ok=True) path.write_text( json.dumps({"slug": "alpha", "subject_type": "skill", "grade": "A"}), encoding="utf-8", ) assert mt.all_sidecars()[0]["grade"] == "A" path.write_text( json.dumps({"slug": "alpha", "subject_type": "skill", "grade": "F"}), encoding="utf-8", ) os.utime(path, (time.time() + 2.0, time.time() + 2.0)) assert mt.all_sidecars()[0]["grade"] == "F" def test_render_kpi_empty_state(fake_claude: Path) -> None: """With no sidecars, /kpi must render a helpful empty state, not 500.""" html_out = mt.render_kpi() assert "

    KPIs

    " in html_out assert "No KPI data yet" in html_out assert "ctx-skill-quality recompute --all" in html_out assert "ctx-skill-quality score --all" not in html_out def test_render_kpi_with_sidecars(fake_claude: Path) -> None: """With real sidecars + lifecycle files, /kpi must surface the four main tables: grade distribution, lifecycle tiers, categories, demotion candidates. Mirrors kpi_dashboard.render_markdown sections.""" _write_sidecar(fake_claude, "alpha", { "slug": "alpha", "subject_type": "skill", "grade": "A", "raw_score": 0.92, "score": 0.92, "hard_floor": None, "computed_at": "2026-04-19T10:00:00+00:00", }) _write_sidecar(fake_claude, "foxtrot", { "slug": "foxtrot", "subject_type": "skill", "grade": "F", "raw_score": 0.08, "score": 0.08, "hard_floor": "intake_fail", "computed_at": "2026-04-19T10:00:00+00:00", }) _write_sidecar(fake_claude, "reviewer", { "slug": "reviewer", "subject_type": "agent", "grade": "F", "raw_score": 0.06, "score": 0.06, "hard_floor": None, "computed_at": "2026-04-19T10:00:00+00:00", }) html_out = mt.render_kpi() # The five section headings must all be present. assert "Grade distribution" in html_out assert "Lifecycle tiers" in html_out assert "Top demotion candidates" in html_out assert "By category" in html_out assert "Archived" in html_out # Slug must be linked into the demotion-candidates table. assert "foxtrot" in html_out assert "/skill/reviewer?type=agent" in html_out # Cross-link to the JSON endpoint + /skills drill-down. assert "/api/kpi.json" in html_out assert "/skills" in html_out def test_api_kpi_summary_shape(fake_claude: Path) -> None: """The JSON endpoint must return a DashboardSummary-shaped dict.""" _write_sidecar(fake_claude, "alpha", { "slug": "alpha", "subject_type": "skill", "grade": "A", "raw_score": 0.9, "score": 0.9, "hard_floor": None, "computed_at": "2026-04-19T10:00:00+00:00", }) summary = mt.kpi_summary() direct_summary = kpi_service.kpi_summary(fake_claude / "skill-quality") assert summary is not None assert direct_summary is not None assert direct_summary.to_dict() == summary.to_dict() d = summary.to_dict() for key in ("total", "grade_counts", "lifecycle_counts", "category_breakdown", "hard_floor_counts", "low_quality_candidates", "archived", "generated_at"): assert key in d, f"missing {key}" assert d["total"] == 1 assert d["grade_counts"].get("A", 0) == 1 def test_kpi_summary_cache_reuses_recent_summary( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: import kpi_dashboard as kd _write_sidecar(fake_claude, "alpha", { "slug": "alpha", "subject_type": "skill", "grade": "A", "raw_score": 0.9, "score": 0.9, "hard_floor": None, "computed_at": "2026-04-19T10:00:00+00:00", }) kpi_service.reset_cache() real_generate = kd.generate calls = 0 def wrapped_generate(*, sources, top_n=10, now=None): nonlocal calls calls += 1 return real_generate(sources=sources, top_n=top_n, now=now) monkeypatch.setattr(kd, "generate", wrapped_generate) assert mt.kpi_summary() is not None assert mt.kpi_summary() is not None assert calls == 1 def test_kpi_summary_reuses_disk_cache_after_process_cache_reset( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: import kpi_dashboard as kd _write_sidecar(fake_claude, "alpha", { "slug": "alpha", "subject_type": "skill", "grade": "A", "raw_score": 0.9, "score": 0.9, "hard_floor": None, "computed_at": "2026-04-19T10:00:00+00:00", }) kpi_service.reset_cache() first = mt.kpi_summary() assert first is not None assert kpi_service.summary_disk_cache_path(fake_claude / "skill-quality").is_file() kpi_service.reset_cache() def fail_generate(*args: object, **kwargs: object) -> object: raise AssertionError("fresh process should read the KPI disk cache") monkeypatch.setattr(kd, "generate", fail_generate) second = mt.kpi_summary() assert second is not None assert second.total == first.total assert second.grade_counts == first.grade_counts def test_kpi_summary_disk_cache_invalidates_on_sidecar_rewrite( fake_claude: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: import kpi_dashboard as kd _write_sidecar(fake_claude, "alpha", { "slug": "alpha", "subject_type": "skill", "grade": "A", "raw_score": 0.9, "score": 0.9, "hard_floor": None, "computed_at": "2026-04-19T10:00:00+00:00", }) sidecar = fake_claude / "skill-quality" / "alpha.json" kpi_service.reset_cache() assert mt.kpi_summary() is not None sidecar.write_text( json.dumps({ "slug": "alpha", "subject_type": "skill", "grade": "F", "raw_score": 0.1, "score": 0.1, "hard_floor": None, "computed_at": "2026-04-19T10:00:00+00:00", }), encoding="utf-8", ) os.utime(sidecar, (time.time() + 2.0, time.time() + 2.0)) kpi_service.reset_cache() real_generate = kd.generate calls = 0 def wrapped_generate(*, sources, top_n=10, now=None): nonlocal calls calls += 1 return real_generate(sources=sources, top_n=top_n, now=now) monkeypatch.setattr(kd, "generate", wrapped_generate) summary = mt.kpi_summary() assert summary is not None assert summary.grade_counts.get("F") == 1 assert calls == 1 def test_layout_nav_includes_wiki_and_kpi() -> None: """Every rendered page must include the new Wiki + KPI tabs in the top nav — the user explicitly asked for them to be accessible.""" out = mt.layout("test", "

    body

    ") assert "href='/wiki'" in out assert "href='/harness'" in out assert "href='/docs'" in out assert "href='/kpi'" in out assert "href='/graph'" in out assert "href='/config'" in out assert "class='app-shell'" in out assert "class='app-header'" in out assert "class='app-brand'" in out assert ">Wiki<" in out assert ">Harness Setup<" in out assert ">Docs<" in out assert ">KPIs<" in out assert ">Config<" in out assert "--surface" in out assert "--accent" in out def _use_temp_docs_cache(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: dashboard_docs.reset_docs_render_cache() monkeypatch.setattr( mt, "docs_render_disk_cache_path", lambda: tmp_path / ".ctx-monitor-docs-cache.json", ) def test_render_docs_lists_repo_docs( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: _use_temp_docs_cache(tmp_path, monkeypatch) (tmp_path / "docs").mkdir() (tmp_path / "docs" / "harness").mkdir() (tmp_path / "graph").mkdir() (tmp_path / "mkdocs.yml").write_text( "\n".join([ "nav:", " - Home: index.md", " - Dashboard: dashboard.md", " - Harness:", " - Attach to hosts: harness/attaching-to-hosts.md", "extra:", " custom: !!python/name:material.extensions.emoji.twemoji", ]), encoding="utf-8", ) (tmp_path / "README.md").write_text( "# ctx\n\nMain repo docs.\n", encoding="utf-8", ) (tmp_path / "docs" / "index.md").write_text( "\n".join([ "# Home", "", "Repo docs home.", "", "!!! tip \"Install\"", "", " ctx-init --graph", "", "## Explore the docs", "", "Jump to the [dashboard docs](dashboard.md).", "", "### Deep section", "", "Nested docs body.", "", "
    ", "", "- **Knowledge graph**", "", " ---", "", " Graph docs.", "", " [:octicons-arrow-right-24: Knowledge graph](knowledge-graph.md)", "", "
    ", ]), encoding="utf-8", ) (tmp_path / "docs" / "dashboard.md").write_text( "# Dashboard\n\n- **Monitor** skills, agents, MCPs, harnesses, and graph state.\n\n## Runtime view\n", encoding="utf-8", ) (tmp_path / "docs" / "knowledge-graph.md").write_text( "# Knowledge graph\n\n## Graph artifact\n\nGraph internals.\n", encoding="utf-8", ) (tmp_path / "docs" / "harness" / "attaching-to-hosts.md").write_text( "# Attach to hosts\n\nConnect ctx to a non-Claude harness.\n", encoding="utf-8", ) (tmp_path / "graph" / "README.md").write_text( "# Graph Artifacts\n\nCompressed wiki and knowledge graph artifacts.\n", encoding="utf-8", ) monkeypatch.setattr(mt, "docs_roots", lambda: [tmp_path]) html_out = mt.render_docs() docs_script = mt.monitor_asset_text("monitor-docs.js") assert "

    Docs

    " in html_out assert "class='docs-shell'" in html_out assert "class='docs-hero'" in html_out assert "Repo documentation" in html_out assert "sections" in html_out assert "pages" in html_out assert "class='docs-tabs'" in html_out assert "data-doc-tab='home'" in html_out assert "data-doc-tab='dashboard'" in html_out assert "data-doc-tab='harness'" in html_out assert "data-doc-tab='repo'" in html_out assert "README.md" in html_out assert "docs/dashboard.md" in html_out assert "docs/harness/attaching-to-hosts.md" in html_out assert "graph/README.md" in html_out assert "Monitor skills, agents, MCPs, harnesses, and graph state." in html_out assert "Connect ctx to a non-Claude harness." in html_out assert "class=\"admonition tip\"" in html_out assert "docs-heading-link docs-heading-level-2" in html_out assert "docs-heading-link docs-heading-level-3" in html_out assert "href='#doc-home-docs-index-md-explore-the-docs'" in html_out assert "href='#doc-home-docs-index-md-deep-section'" in html_out assert 'href="#doc-dashboard-docs-dashboard-md"' in html_out assert 'href="#doc-other-docs-knowledge-graph-md"' in html_out assert 'data-doc-tab="other"' in html_out assert "id='docs-search-results'" in html_out assert docs_script in html_out assert "jumpToDocTarget" in html_out assert "nested docs body" in html_out assert '
    ' in html_out assert "Knowledge graph" in html_out assert "-> Knowledge graph" in html_out assert ":octicons-arrow-right-24:" not in html_out assert "!!! tip" not in html_out assert "**Monitor**" not in html_out assert "id='docs-search'" in html_out assert "https://stevesolun.github.io/ctx/" in html_out assert "doc-card" not in html_out def test_render_docs_sanitizes_active_html( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: _use_temp_docs_cache(tmp_path, monkeypatch) (tmp_path / "docs").mkdir() (tmp_path / "mkdocs.yml").write_text( "site_name: ctx\nnav:\n - Home: index.md\n", encoding="utf-8", ) (tmp_path / "docs" / "index.md").write_text( "# Home\n\n" "\n\n" "\n\n" "bad\n", encoding="utf-8", ) monkeypatch.setattr(mt, "docs_roots", lambda: [tmp_path]) html_out = mt.render_docs() assert "" not in html_out assert "<script" in html_out assert "<input" in html_out assert " None: _use_temp_docs_cache(tmp_path, monkeypatch) (tmp_path / "docs").mkdir() (tmp_path / "mkdocs.yml").write_text( "site_name: ctx\nnav:\n - Home: index.md\n", encoding="utf-8", ) (tmp_path / "docs" / "index.md").write_text( "# Home\n\nDocs body.\n", encoding="utf-8", ) monkeypatch.setattr(mt, "docs_roots", lambda: [tmp_path]) first = mt.render_docs() assert (tmp_path / ".ctx-monitor-docs-cache.json").is_file() dashboard_docs.reset_docs_render_cache() def fail_render_markdown(*args: object, **kwargs: object) -> str: raise AssertionError("fresh process should read the rendered docs cache") monkeypatch.setattr(mt, "render_docs_markdown", fail_render_markdown) second = mt.render_docs() assert second == first def test_render_docs_markdown_preserves_mkdocs_tab_controls() -> None: markdown_text = ( '=== "One"\n\n' " First body\n\n" '=== "Two"\n\n' " Second body\n" ) html_out = dashboard_docs.render_docs_markdown( markdown_text, "doc-home", fallback_renderer=mt.render_wiki_markdown, ) assert "<input" not in html_out assert 'type="radio"' in html_out assert 'name="__tabbed_' in html_out assert '