Stevesolun commited on
Commit
e8a8059
·
verified ·
1 Parent(s): ef47a80

Sync ctx 0d3237d (part 3)

Browse files

GitHub commit: 0d3237df9965b8142d0f74ea4c4bea30f3ef16c9

src/tests/test_feature_user_story_tracker.py CHANGED
@@ -13,7 +13,9 @@ from ctx.monitor import routes as monitor_routes # noqa: E402
13
  TRACKER = repo_root / "docs" / "qa" / "feature-user-story-status.csv"
14
  README = repo_root / "README.md"
15
  PASS_STATUSES = {"Tested Pass", "Retested Pass"}
16
- ACTIONABLE_STATUSES = PASS_STATUSES | {"Needs Fix"}
 
 
17
 
18
 
19
  def _tracker_rows() -> list[dict[str, str]]:
@@ -44,12 +46,17 @@ def test_feature_user_story_tracker_has_no_empty_core_fields() -> None:
44
  for key in required:
45
  assert row[key].strip(), f"{row.get('feature_id', '<unknown>')} missing {key}"
46
  assert row["status"] in ACTIONABLE_STATUSES
47
- if row["status"] not in PASS_STATUSES:
48
  for key in ("error_id", "error_summary", "fix_status"):
49
  assert row[key].strip(), (
50
  f"{row.get('feature_id', '<unknown>')} has "
51
  f"{row['status']} without {key}"
52
  )
 
 
 
 
 
53
 
54
 
55
  def test_feature_user_story_tracker_covers_all_console_scripts() -> None:
@@ -76,6 +83,38 @@ def test_feature_user_story_tracker_covers_monitor_route_inventory() -> None:
76
  assert [route for route in route_patterns if route not in tracker] == []
77
 
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  def test_readme_shows_user_story_examples_from_tracker() -> None:
80
  readme = README.read_text(encoding="utf-8")
81
 
 
13
  TRACKER = repo_root / "docs" / "qa" / "feature-user-story-status.csv"
14
  README = repo_root / "README.md"
15
  PASS_STATUSES = {"Tested Pass", "Retested Pass"}
16
+ VALIDATION_STATUSES = {"Needs Validation"}
17
+ FIX_STATUSES = {"Needs Fix"}
18
+ ACTIONABLE_STATUSES = PASS_STATUSES | VALIDATION_STATUSES | FIX_STATUSES
19
 
20
 
21
  def _tracker_rows() -> list[dict[str, str]]:
 
46
  for key in required:
47
  assert row[key].strip(), f"{row.get('feature_id', '<unknown>')} missing {key}"
48
  assert row["status"] in ACTIONABLE_STATUSES
49
+ if row["status"] in FIX_STATUSES:
50
  for key in ("error_id", "error_summary", "fix_status"):
51
  assert row[key].strip(), (
52
  f"{row.get('feature_id', '<unknown>')} has "
53
  f"{row['status']} without {key}"
54
  )
55
+ if row["status"] in VALIDATION_STATUSES:
56
+ assert row["notes"].strip(), (
57
+ f"{row.get('feature_id', '<unknown>')} needs validation "
58
+ "without a validation note"
59
+ )
60
 
61
 
62
  def test_feature_user_story_tracker_covers_all_console_scripts() -> None:
 
83
  assert [route for route in route_patterns if route not in tracker] == []
84
 
85
 
86
+ def test_feature_user_story_tracker_covers_distribution_workflows() -> None:
87
+ workflows = (
88
+ ".github/workflows/test.yml",
89
+ ".github/workflows/docs.yml",
90
+ ".github/workflows/huggingface-sync.yml",
91
+ ".github/workflows/publish.yml",
92
+ ".github/workflows/clean-host-contract.yml",
93
+ ".github/workflows/xdist-experiment.yml",
94
+ )
95
+ tracker = _tracker_text()
96
+
97
+ assert [workflow for workflow in workflows if workflow not in tracker] == []
98
+
99
+
100
+ def test_feature_user_story_tracker_covers_maintainer_scripts() -> None:
101
+ scripts = sorted((repo_root / "scripts").glob("*.py"))
102
+ tracker = _tracker_text()
103
+ script_paths = [script.relative_to(repo_root).as_posix() for script in scripts]
104
+
105
+ assert scripts
106
+ assert [path for path in script_paths if path not in tracker] == []
107
+
108
+
109
+ def test_feature_user_story_tracker_covers_public_docs_assets() -> None:
110
+ assets = sorted((repo_root / "docs" / "assets" / "javascripts").glob("*.js"))
111
+ tracker = _tracker_text()
112
+ asset_paths = [asset.relative_to(repo_root).as_posix() for asset in assets]
113
+
114
+ assert assets
115
+ assert [path for path in asset_paths if path not in tracker] == []
116
+
117
+
118
  def test_readme_shows_user_story_examples_from_tracker() -> None:
119
  readme = README.read_text(encoding="utf-8")
120
 
src/tests/test_huggingface_sync.py CHANGED
@@ -160,10 +160,12 @@ def test_hf_sync_workflow_uses_secret_and_hardened_script() -> None:
160
  assert "HF_TOKEN: ${{ secrets.HF_TOKEN }}" in text
161
  assert "lfs: false" in text
162
  assert "git lfs pull" not in text
163
- assert "gh release download" in text
 
 
164
  for artifact in _required_hydrated_artifacts():
165
  assert artifact.as_posix() in text
166
- assert f"--pattern {artifact.name}" in text
167
  assert "scripts/sync_huggingface.py" in text
168
  assert "Classify sync scope" in text
169
  assert "card_only_files" in text
 
160
  assert "HF_TOKEN: ${{ secrets.HF_TOKEN }}" in text
161
  assert "lfs: false" in text
162
  assert "git lfs pull" not in text
163
+ assert "Resolving graph artifacts from matching release assets" in text
164
+ assert 'tag_name.startswith("graph-artifacts-")' in text
165
+ assert "latest_tag" not in text
166
  for artifact in _required_hydrated_artifacts():
167
  assert artifact.as_posix() in text
168
+ assert f'hydrate_from_release("{artifact.as_posix()}"' in text
169
  assert "scripts/sync_huggingface.py" in text
170
  assert "Classify sync scope" in text
171
  assert "card_only_files" in text
src/tests/test_maintainer_script_bom_inputs.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import sqlite3
5
+ import sys
6
+ import zlib
7
+ from pathlib import Path
8
+
9
+ from scripts import audit_backup
10
+ from scripts.build_dashboard_graph_index import build_dashboard_index
11
+
12
+
13
+ def test_audit_backup_accepts_bom_manifest(
14
+ tmp_path: Path,
15
+ monkeypatch,
16
+ capsys,
17
+ ) -> None:
18
+ claude_home = tmp_path / "home" / ".claude"
19
+ claude_home.mkdir(parents=True)
20
+ config = claude_home / "skill-system-config.json"
21
+ config.write_text('{"ok": true}', encoding="utf-8")
22
+ snapshot = tmp_path / "snapshot"
23
+ snapshot.mkdir()
24
+ manifest = {
25
+ "entries": [
26
+ {
27
+ "source": str(config),
28
+ "dest": "skill-system-config.json",
29
+ "size": config.stat().st_size,
30
+ }
31
+ ]
32
+ }
33
+ (snapshot / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8-sig")
34
+
35
+ monkeypatch.setattr(audit_backup, "CLAUDE_HOME", claude_home)
36
+ monkeypatch.setattr(sys, "argv", ["audit_backup.py", str(snapshot)])
37
+
38
+ assert audit_backup.main() == 0
39
+ output = capsys.readouterr().out
40
+ assert "entries: 1" in output
41
+ assert "OK:" in output
42
+
43
+
44
+ def test_dashboard_graph_index_accepts_bom_graph_json(tmp_path: Path) -> None:
45
+ graph_json = tmp_path / "graph.json"
46
+ output = tmp_path / "dashboard.sqlite3"
47
+ graph_json.write_text(
48
+ json.dumps({
49
+ "graph": {"export_id": "fixture"},
50
+ "nodes": [
51
+ {
52
+ "id": "skill:alpha",
53
+ "label": "Alpha",
54
+ "type": "skill",
55
+ "tags": ["python", "test"],
56
+ "quality_score": 0.9,
57
+ },
58
+ {
59
+ "id": "mcp-server:github",
60
+ "label": "GitHub",
61
+ "type": "mcp-server",
62
+ "tags": ["github"],
63
+ },
64
+ ],
65
+ "links": [
66
+ {
67
+ "source": "skill:alpha",
68
+ "target": "mcp-server:github",
69
+ "weight": 0.77,
70
+ "shared_tags": ["github"],
71
+ "reasons": ["fixture"],
72
+ }
73
+ ],
74
+ }),
75
+ encoding="utf-8-sig",
76
+ )
77
+
78
+ build_dashboard_index(graph_json, output, top_k=5)
79
+
80
+ conn = sqlite3.connect(output)
81
+ try:
82
+ assert conn.execute(
83
+ "SELECT value FROM meta WHERE key='nodes_count'",
84
+ ).fetchone() == ("2",)
85
+ payload = conn.execute(
86
+ "SELECT payload FROM neighbors WHERE source='skill:alpha'",
87
+ ).fetchone()[0]
88
+ finally:
89
+ conn.close()
90
+ neighbors = json.loads(zlib.decompress(payload).decode("utf-8"))
91
+ assert neighbors[0]["target"] == "mcp-server:github"
src/tests/test_pack_full_wiki_tar.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import io
4
+ import json
5
+ import tarfile
6
+ from pathlib import Path
7
+
8
+ from ctx.core.wiki.wiki_packs import load_merged_wiki_pages
9
+ from scripts.pack_full_wiki_tar import repack_full_wiki_tar
10
+
11
+
12
+ def _add_text(tf: tarfile.TarFile, name: str, text: str) -> None:
13
+ payload = text.encode("utf-8")
14
+ info = tarfile.TarInfo(name)
15
+ info.size = len(payload)
16
+ tf.addfile(info, io.BytesIO(payload))
17
+
18
+
19
+ def test_repack_full_wiki_tar_moves_high_fanout_pages_into_wiki_pack(
20
+ tmp_path: Path,
21
+ ) -> None:
22
+ source = tmp_path / "wiki-graph.tar.gz"
23
+ target = tmp_path / "wiki-graph-packed.tar.gz"
24
+ with tarfile.open(source, "w:gz") as tf:
25
+ _add_text(tf, "index.md", "# Wiki\n")
26
+ _add_text(tf, "entities/skills/current.md", "# Current Skill\n")
27
+ _add_text(tf, "entities/skills/empty.md", "")
28
+ _add_text(tf, "entities/agents/reviewer.md", "# Reviewer Agent\n")
29
+ _add_text(tf, "entities/mcp-servers/github.md", "# GitHub MCP\n")
30
+ _add_text(tf, "entities/harnesses/langgraph.md", "# LangGraph Harness\n")
31
+ _add_text(tf, "concepts/empty.md", "")
32
+ _add_text(
33
+ tf,
34
+ "graphify-out/graph-export-manifest.json",
35
+ json.dumps({"export_id": "test-export"}),
36
+ )
37
+ _add_text(tf, "graphify-out/graph-report.md", "# Graph Report\n")
38
+ _add_text(tf, "graphify-out/graph.json", json.dumps({"nodes": [], "edges": []}))
39
+ _add_text(tf, "external-catalogs/skills-sh/catalog.json", json.dumps({"skills": []}))
40
+
41
+ stats = repack_full_wiki_tar(source, target)
42
+
43
+ assert stats.removed_expanded_markdown_pages == 5
44
+ assert stats.packed_pages == 8
45
+ with tarfile.open(target, "r:gz") as tf:
46
+ names = {member.name for member in tf.getmembers()}
47
+ tf.extractall(tmp_path / "extracted")
48
+
49
+ assert "entities/skills/current.md" not in names
50
+ assert "entities/skills/empty.md" not in names
51
+ assert "entities/agents/reviewer.md" not in names
52
+ assert "entities/mcp-servers/github.md" not in names
53
+ assert "entities/harnesses/langgraph.md" in names
54
+ assert "concepts/empty.md" not in names
55
+ assert "graphify-out/graph-report.md" in names
56
+ assert "wiki-packs/base-test-export/wiki-pack-manifest.json" in names
57
+ assert "wiki-packs/base-test-export/pages.jsonl" in names
58
+
59
+ pages = load_merged_wiki_pages(tmp_path / "extracted" / "wiki-packs")
60
+ assert pages["entities/skills/current.md"] == "# Current Skill\n"
61
+ assert pages["entities/skills/empty.md"] == "<!-- empty markdown page -->\n"
62
+ assert pages["entities/agents/reviewer.md"] == "# Reviewer Agent\n"
63
+ assert pages["entities/mcp-servers/github.md"] == "# GitHub MCP\n"
64
+ assert pages["concepts/empty.md"] == "<!-- empty markdown page -->\n"
65
+
66
+ second_target = tmp_path / "wiki-graph-packed-again.tar.gz"
67
+ repack_full_wiki_tar(target, second_target)
68
+ with tarfile.open(second_target, "r:gz") as tf:
69
+ second_names = {member.name for member in tf.getmembers()}
70
+ tf.extractall(tmp_path / "extracted-again")
71
+ assert "graphify-out/graph-report.md" in second_names
72
+ repacked_pages = load_merged_wiki_pages(tmp_path / "extracted-again" / "wiki-packs")
73
+ assert repacked_pages["entities/skills/current.md"] == "# Current Skill\n"
74
+ assert repacked_pages["entities/agents/reviewer.md"] == "# Reviewer Agent\n"
75
+ assert repacked_pages["entities/mcp-servers/github.md"] == "# GitHub MCP\n"
src/tests/test_update_repo_stats.py CHANGED
@@ -522,12 +522,12 @@ def test_github_about_description_uses_entity_counts() -> None:
522
 
523
  description = urs.build_github_about_description(stats)
524
 
525
- assert description.startswith("Not a catalog or marketplace.")
526
  assert "recommendation layer" in description
527
  assert "bring your org tools" in description
528
  assert "use the shipped graph" in description
529
- assert "per dev window" in description
530
- assert "cutting token and compute waste" in description
531
  assert "123,456-node LLM-wiki graph" in description
532
  assert "4,321 skills" in description
533
  assert "56 agents" in description
 
522
 
523
  description = urs.build_github_about_description(stats)
524
 
525
+ assert description.startswith("Not an Amazon-style catalog or marketplace.")
526
  assert "recommendation layer" in description
527
  assert "bring your org tools" in description
528
  assert "use the shipped graph" in description
529
+ assert "only for the current dev window" in description
530
+ assert "cutting token bills and local compute waste" in description
531
  assert "123,456-node LLM-wiki graph" in description
532
  assert "4,321 skills" in description
533
  assert "56 agents" in description
src/tests/test_validate_graph_artifacts.py CHANGED
@@ -17,7 +17,11 @@ from ctx.core.graph.graph_packs import build_pack_manifest, write_base_pack, wri
17
  from ctx.core.wiki.wiki_packs import write_wiki_base_pack
18
  from scripts.ci_preflight import GRAPH_VALIDATE_ARGS
19
  from validate_graph_artifacts import (
 
20
  DEFAULT_HARNESSES,
 
 
 
21
  GraphArtifactError,
22
  _validate_root_entity_overlay,
23
  _safe_tar_name,
@@ -1478,6 +1482,24 @@ def test_graph_only_workflow_uses_preflight_graph_contract() -> None:
1478
  assert parsed == expected
1479
 
1480
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1481
  def test_graph_only_workflow_waits_for_release_asset_upload() -> None:
1482
  workflow = yaml.safe_load(Path(".github/workflows/test.yml").read_text(
1483
  encoding="utf-8"
 
17
  from ctx.core.wiki.wiki_packs import write_wiki_base_pack
18
  from scripts.ci_preflight import GRAPH_VALIDATE_ARGS
19
  from validate_graph_artifacts import (
20
+ DEFAULT_MIN_EDGES,
21
  DEFAULT_HARNESSES,
22
+ DEFAULT_MIN_NODES,
23
+ DEFAULT_MIN_SEMANTIC_EDGES,
24
+ DEFAULT_MIN_SKILLS_SH_NODES,
25
  GraphArtifactError,
26
  _validate_root_entity_overlay,
27
  _safe_tar_name,
 
1482
  assert parsed == expected
1483
 
1484
 
1485
+ def test_validator_default_floors_match_preflight_contract() -> None:
1486
+ args = GRAPH_VALIDATE_ARGS[1:]
1487
+ values: dict[str, str] = {}
1488
+ i = 0
1489
+ while i < len(args):
1490
+ flag = args[i]
1491
+ if i + 1 >= len(args) or args[i + 1].startswith("--"):
1492
+ i += 1
1493
+ continue
1494
+ values[flag] = args[i + 1]
1495
+ i += 2
1496
+
1497
+ assert values["--min-nodes"] == str(DEFAULT_MIN_NODES)
1498
+ assert values["--min-edges"] == str(DEFAULT_MIN_EDGES)
1499
+ assert values["--min-skills-sh-nodes"] == str(DEFAULT_MIN_SKILLS_SH_NODES)
1500
+ assert values["--min-semantic-edges"] == str(DEFAULT_MIN_SEMANTIC_EDGES)
1501
+
1502
+
1503
  def test_graph_only_workflow_waits_for_release_asset_upload() -> None:
1504
  workflow = yaml.safe_load(Path(".github/workflows/test.yml").read_text(
1505
  encoding="utf-8"
src/update_repo_stats.py CHANGED
@@ -1222,12 +1222,12 @@ def build_github_about_description(stats: Mapping[str, int | None]) -> str:
1222
  if not all((nodes, skills, agents, mcps, harnesses)):
1223
  raise ValueError("missing graph stats for GitHub About description")
1224
  return (
1225
- "Not a catalog or marketplace. ctx is a recommendation layer: bring "
1226
- "your org tools or use the shipped graph to load the right skills, "
1227
- "agents, MCPs, and harnesses per dev window, cutting token and compute "
1228
- f"waste: {nodes:,}-node LLM-wiki graph, "
1229
- f"{skills:,} skills, {agents:,} agents, {mcps:,} MCPs, "
1230
- f"{harnesses:,} harnesses."
1231
  )
1232
 
1233
 
 
1222
  if not all((nodes, skills, agents, mcps, harnesses)):
1223
  raise ValueError("missing graph stats for GitHub About description")
1224
  return (
1225
+ "Not an Amazon-style catalog or marketplace. ctx is a recommendation "
1226
+ "layer: bring your org tools or use the shipped graph to load the "
1227
+ "right skills, agents, MCPs, and harnesses only for the current dev "
1228
+ "window, cutting token bills and local compute waste: "
1229
+ f"{nodes:,}-node LLM-wiki graph, {skills:,} skills, "
1230
+ f"{agents:,} agents, {mcps:,} MCPs, {harnesses:,} harnesses."
1231
  )
1232
 
1233
 
src/validate_graph_artifacts.py CHANGED
@@ -28,6 +28,10 @@ from ctx.core.wiki.wiki_packs import (
28
  )
29
 
30
  GIT_LFS_POINTER_PREFIX = b"version https://git-lfs.github.com/spec/v1"
 
 
 
 
31
  DEFAULT_HARNESSES = {
32
  "agentops",
33
  "autogen",
@@ -798,10 +802,10 @@ def validate_graph_artifacts(
798
  graph_dir: Path,
799
  *,
800
  deep: bool = False,
801
- min_nodes: int = 100_000,
802
- min_edges: int = 2_000_000,
803
- min_skills_sh_nodes: int = 89_000,
804
- min_semantic_edges: int = 1_000_000,
805
  expected_harnesses: set[str] | None = None,
806
  line_threshold: int = 180,
807
  max_stage_lines: int = 40,
@@ -1408,10 +1412,10 @@ def main() -> None:
1408
  parser = argparse.ArgumentParser(description=__doc__)
1409
  parser.add_argument("--graph-dir", type=Path, default=Path("graph"))
1410
  parser.add_argument("--deep", action="store_true")
1411
- parser.add_argument("--min-nodes", type=int, default=100_000)
1412
- parser.add_argument("--min-edges", type=int, default=2_000_000)
1413
- parser.add_argument("--min-skills-sh-nodes", type=int, default=89_000)
1414
- parser.add_argument("--min-semantic-edges", type=int, default=1_000_000)
1415
  parser.add_argument("--line-threshold", type=int, default=180)
1416
  parser.add_argument("--max-stage-lines", type=int, default=40)
1417
  parser.add_argument("--expected-nodes", type=int)
 
28
  )
29
 
30
  GIT_LFS_POINTER_PREFIX = b"version https://git-lfs.github.com/spec/v1"
31
+ DEFAULT_MIN_NODES = 79_000
32
+ DEFAULT_MIN_EDGES = 1_700_000
33
+ DEFAULT_MIN_SKILLS_SH_NODES = 67_000
34
+ DEFAULT_MIN_SEMANTIC_EDGES = 1_000_000
35
  DEFAULT_HARNESSES = {
36
  "agentops",
37
  "autogen",
 
802
  graph_dir: Path,
803
  *,
804
  deep: bool = False,
805
+ min_nodes: int = DEFAULT_MIN_NODES,
806
+ min_edges: int = DEFAULT_MIN_EDGES,
807
+ min_skills_sh_nodes: int = DEFAULT_MIN_SKILLS_SH_NODES,
808
+ min_semantic_edges: int = DEFAULT_MIN_SEMANTIC_EDGES,
809
  expected_harnesses: set[str] | None = None,
810
  line_threshold: int = 180,
811
  max_stage_lines: int = 40,
 
1412
  parser = argparse.ArgumentParser(description=__doc__)
1413
  parser.add_argument("--graph-dir", type=Path, default=Path("graph"))
1414
  parser.add_argument("--deep", action="store_true")
1415
+ parser.add_argument("--min-nodes", type=int, default=DEFAULT_MIN_NODES)
1416
+ parser.add_argument("--min-edges", type=int, default=DEFAULT_MIN_EDGES)
1417
+ parser.add_argument("--min-skills-sh-nodes", type=int, default=DEFAULT_MIN_SKILLS_SH_NODES)
1418
+ parser.add_argument("--min-semantic-edges", type=int, default=DEFAULT_MIN_SEMANTIC_EDGES)
1419
  parser.add_argument("--line-threshold", type=int, default=180)
1420
  parser.add_argument("--max-stage-lines", type=int, default=40)
1421
  parser.add_argument("--expected-nodes", type=int)