Sync ctx 0d3237d (part 3)
Browse filesGitHub commit: 0d3237df9965b8142d0f74ea4c4bea30f3ef16c9
- src/tests/test_feature_user_story_tracker.py +41 -2
- src/tests/test_huggingface_sync.py +4 -2
- src/tests/test_maintainer_script_bom_inputs.py +91 -0
- src/tests/test_pack_full_wiki_tar.py +75 -0
- src/tests/test_update_repo_stats.py +3 -3
- src/tests/test_validate_graph_artifacts.py +22 -0
- src/update_repo_stats.py +6 -6
- src/validate_graph_artifacts.py +12 -8
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 |
-
|
|
|
|
|
|
|
| 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"]
|
| 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 "
|
|
|
|
|
|
|
| 164 |
for artifact in _required_hydrated_artifacts():
|
| 165 |
assert artifact.as_posix() in text
|
| 166 |
-
assert f"
|
| 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
|
| 526 |
assert "recommendation layer" in description
|
| 527 |
assert "bring your org tools" in description
|
| 528 |
assert "use the shipped graph" in description
|
| 529 |
-
assert "
|
| 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
|
| 1226 |
-
"your org tools or use the shipped graph to load the
|
| 1227 |
-
"agents, MCPs, and harnesses
|
| 1228 |
-
|
| 1229 |
-
f"{
|
| 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 =
|
| 802 |
-
min_edges: int =
|
| 803 |
-
min_skills_sh_nodes: int =
|
| 804 |
-
min_semantic_edges: int =
|
| 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=
|
| 1412 |
-
parser.add_argument("--min-edges", type=int, default=
|
| 1413 |
-
parser.add_argument("--min-skills-sh-nodes", type=int, default=
|
| 1414 |
-
parser.add_argument("--min-semantic-edges", type=int, default=
|
| 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)
|