Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 16 additions & 17 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
name: Deploy review demo
name: Deploy CounterProof Proof Lab

on:
push:
branches: [main]
paths:
- 'skill_factory/**'
- 'skills/**'
- 'site/**'
- 'skill_factory/evolution/capabilities.py'
- 'scripts/build_pages.py'
- '.github/workflows/pages.yml'
workflow_dispatch:
Expand Down Expand Up @@ -42,33 +41,33 @@ jobs:
echo "GitHub Pages is enabled."
elif [[ "$status" == "404" ]]; then
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "::notice title=GitHub Pages not enabled::Skipping deployment. Enable Pages for this repository with GitHub Actions as the source, then re-run this workflow."
echo "::notice title=GitHub Pages not enabled::Built the Proof Lab successfully, but deployment is skipped. Enable Pages with GitHub Actions as the source, then re-run this workflow."
else
echo "Unexpected GitHub Pages API status: $status" >&2
cat /tmp/counterproof-pages.json >&2 || true
exit 1
fi

- uses: actions/checkout@v4
if: steps.pages.outputs.enabled == 'true'

- uses: actions/setup-python@v5
if: steps.pages.outputs.enabled == 'true'
with:
python-version: '3.12'
cache: pip

- name: Install, verify and test
if: steps.pages.outputs.enabled == 'true'
run: |
pip install -e '.[dev]'
ruff check skill_factory/ tests/
pytest -q

- name: Build review surface from repository skills
if: steps.pages.outputs.enabled == 'true'
- name: Build CounterProof Proof Lab
run: python scripts/build_pages.py

- name: Validate static artifact
shell: bash
run: |
set -euo pipefail
test -f dist/index.html
test -f dist/app.js
test -f dist/data/build.json
test -f dist/data/capabilities.json
grep -q '"commit"' dist/data/build.json
grep -q 'CounterProof' dist/index.html

- uses: actions/configure-pages@v5
if: steps.pages.outputs.enabled == 'true'

Expand All @@ -77,7 +76,7 @@ jobs:
with:
path: dist

- name: Deploy to GitHub Pages
- name: Deploy CounterProof Proof Lab
if: steps.pages.outputs.enabled == 'true'
id: deployment
uses: actions/deploy-pages@v4
66 changes: 16 additions & 50 deletions scripts/build_pages.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Build a static review UI from the repository's real skills and validators."""
"""Build the static CounterProof Proof Lab for GitHub Pages."""

from __future__ import annotations

Expand All @@ -9,73 +9,39 @@
from pathlib import Path

from skill_factory.evolution.capabilities import capability_report
from skill_factory.loader import load_skill
from skill_factory.verifier.static_check import verify_skill

ROOT = Path(__file__).resolve().parents[1]
SKILLS = ROOT / "skills"
DIST = ROOT / "dist"


def read_json(path: Path):
return json.loads(path.read_text(encoding="utf-8")) if path.exists() else None


def serialize_skill(skill_dir: Path) -> dict:
skill = load_skill(skill_dir)
result = verify_skill(skill_dir)
return {
"name": skill.name,
"description": skill.description,
"version": skill.meta.version,
"domain": skill.meta.domain,
"tags": skill.meta.tags,
"content": skill.content,
"validation": {
"passed": result.passed,
"errors": result.errors,
"warnings": result.warnings,
},
"evals": read_json(skill_dir / "evals" / "evals.json"),
"trigger_queries": read_json(skill_dir / "evals" / "trigger_queries.json"),
"resources": {
"scripts": len(list((skill_dir / "scripts").glob("*.py"))) if (skill_dir / "scripts").exists() else 0,
"references": len(list((skill_dir / "references").glob("*"))) if (skill_dir / "references").exists() else 0,
"assets": len(list((skill_dir / "assets").glob("*"))) if (skill_dir / "assets").exists() else 0,
},
}


def main() -> None:
if DIST.exists():
shutil.rmtree(DIST)
shutil.copytree(ROOT / "site", DIST)
skills = [
serialize_skill(path)
for path in sorted(SKILLS.iterdir())
if path.is_dir() and (path / "SKILL.md").exists()
]
payload = {

data = DIST / "data"
data.mkdir(exist_ok=True)

build = {
"generated_at": datetime.now(timezone.utc).isoformat(),
"commit": subprocess.run(
["git", "rev-parse", "--short", "HEAD"], cwd=ROOT, check=True,
capture_output=True, text=True,
["git", "rev-parse", "--short", "HEAD"],
cwd=ROOT,
check=True,
capture_output=True,
text=True,
).stdout.strip(),
"skills": skills,
"summary": {
"total": len(skills),
"valid": sum(item["validation"]["passed"] for item in skills),
"eval_cases": sum(len((item["evals"] or {}).get("cases", item["evals"] or [])) for item in skills),
},
}
data = DIST / "data"
data.mkdir(exist_ok=True)
(data / "skills.json").write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
(data / "build.json").write_text(
json.dumps(build, ensure_ascii=False, indent=2),
encoding="utf-8",
)
(data / "capabilities.json").write_text(
json.dumps(capability_report(), ensure_ascii=False, indent=2),
encoding="utf-8",
)
(DIST / ".nojekyll").touch()


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion site/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ async function init() {
fetch("data/evolution_cases.json").then(r => { if (!r.ok) throw new Error("case data " + r.status); return r.json(); }),
fetch("data/capabilities.json").then(r => { if (!r.ok) throw new Error("capabilities " + r.status); return r.json(); }),
fetch("data/reality_cases.json").then(r => r.ok ? r.json() : {cases: []}).catch(() => ({cases: []})),
fetch("data/skills.json").then(r => r.ok ? r.json() : null).catch(() => null)
fetch("data/build.json").then(r => r.ok ? r.json() : null).catch(() => null)
]);
cases = casePayload.cases;
capabilities = capabilityPayload.capabilities;
Expand Down
4 changes: 2 additions & 2 deletions site/standalone.html
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,7 @@ <h2>What is real today,<br>and what is still a research target.</h2>
if(u.endsWith("data/evolution_cases.json")) return new Response(JSON.stringify(__COUNTERPROOF_CASES__),{status:200,headers:{"Content-Type":"application/json"}});
if(u.endsWith("data/capabilities.json")) return new Response(JSON.stringify(__COUNTERPROOF_CAPS__),{status:200,headers:{"Content-Type":"application/json"}});
if(u.endsWith("data/reality_cases.json")) return new Response(JSON.stringify(__COUNTERPROOF_REALITY__),{status:200,headers:{"Content-Type":"application/json"}});
if(u.endsWith("data/skills.json")) return new Response(JSON.stringify({commit:"counterproof-reality-intake"}),{status:200,headers:{"Content-Type":"application/json"}});
if(u.endsWith("data/build.json")) return new Response(JSON.stringify({commit:"counterproof-proof-lab"}),{status:200,headers:{"Content-Type":"application/json"}});
if(__nativeFetch) return __nativeFetch(url,options);
throw new Error("Network unavailable");
};
Expand Down Expand Up @@ -1616,7 +1616,7 @@ <h2>What is real today,<br>and what is still a research target.</h2>
fetch("data/evolution_cases.json").then(r => { if (!r.ok) throw new Error("case data " + r.status); return r.json(); }),
fetch("data/capabilities.json").then(r => { if (!r.ok) throw new Error("capabilities " + r.status); return r.json(); }),
fetch("data/reality_cases.json").then(r => r.ok ? r.json() : {cases: []}).catch(() => ({cases: []})),
fetch("data/skills.json").then(r => r.ok ? r.json() : null).catch(() => null)
fetch("data/build.json").then(r => r.ok ? r.json() : null).catch(() => null)
]);
cases = casePayload.cases;
capabilities = capabilityPayload.capabilities;
Expand Down
Loading