diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml new file mode 100644 index 0000000..0369261 --- /dev/null +++ b/.github/workflows/quality.yml @@ -0,0 +1,18 @@ +name: Profile quality + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + static: + name: Static integrity + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - run: python3 scripts/check_text.py + - run: python3 scripts/check_profile.py diff --git a/profile/README.md b/profile/README.md index 0b9f3ea..1ed4ab4 100644 --- a/profile/README.md +++ b/profile/README.md @@ -6,11 +6,13 @@
-Semantic Compatibility Infrastructure for agent graphs.
+ +
- Semantic Compatibility Infrastructure for agent graphs.
- Find the first incompatible edge, inspect the recorded witness, and trace everything downstream.
+ GraphABI finds the first edge where meaning changed, shows the recorded witness,
+ and maps every downstream node that may be affected.
Repository · @@ -29,4 +35,4 @@ uvx --from git+https://github.com/graphabi/graphabi graphabi demo --allow-breaki Security
-Public alpha · Apache-2.0 · Local and deterministic by default · No API key required
+One project · Public alpha · Apache-2.0 · Local and deterministic by default
diff --git a/scripts/check_profile.py b/scripts/check_profile.py new file mode 100644 index 0000000..132c32a --- /dev/null +++ b/scripts/check_profile.py @@ -0,0 +1,39 @@ +"""Verify the organization profile's small public contract.""" + +from __future__ import annotations + +from pathlib import Path +from xml.etree import ElementTree + +ROOT = Path(__file__).parents[1] + + +def main() -> int: + readme = (ROOT / "profile/README.md").read_text(encoding="utf-8") + required = ( + "Explicit contracts", + "Trace-backed evidence", + "Honest uncertainty", + "https://github.com/graphabi/graphabi", + "https://graphabi.github.io", + "CONTRIBUTING.md", + "/security/policy", + "uvx --from git+https://github.com/graphabi/graphabi", + ) + missing = [item for item in required if item not in readme] + for name in ("logo-dark.svg", "logo-light.svg"): + ElementTree.parse(ROOT / "profile/assets" / name) + gif = (ROOT / "profile/assets/demo.gif").read_bytes() + if not gif.startswith((b"GIF87a", b"GIF89a")): + missing.append("valid demo.gif") + if missing: + print("Organization profile verification failed:") + for item in missing: + print(f"- missing {item}") + return 1 + print("Organization profile links, principles, command, and assets are present.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/check_text.py b/scripts/check_text.py new file mode 100644 index 0000000..67b2fcc --- /dev/null +++ b/scripts/check_text.py @@ -0,0 +1,36 @@ +"""Reject prohibited punctuation and private paths in tracked text.""" + +from __future__ import annotations + +import subprocess +from pathlib import Path + +ROOT = Path(__file__).parents[1] + + +def main() -> int: + names = subprocess.check_output(["git", "ls-files", "-z"], cwd=ROOT).split(b"\0") + offenders: list[str] = [] + mac_user_prefix = "/" + "Users" + "/" + windows_user_prefix = "C:" + "\\" + "Users" + "\\" + for name in names: + if not name: + continue + path = ROOT / name.decode() + try: + text = path.read_text(encoding="utf-8") + except (UnicodeDecodeError, OSError): + continue + if chr(0x2014) in text or mac_user_prefix in text or windows_user_prefix in text: + offenders.append(str(path.relative_to(ROOT))) + if offenders: + print("Tracked public text contains prohibited content:") + for offender in offenders: + print(f"- {offender}") + return 1 + print("Tracked public text contains no em dash or private absolute path.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())