From 72877f37d2ed299cbaf5f249c26ddcc9571ad3e4 Mon Sep 17 00:00:00 2001 From: Igor Beylin Date: Sat, 19 Sep 2026 10:21:05 -0400 Subject: [PATCH 1/3] =?UTF-8?q?fix(deps):=20complete=20Gate=5FSDK=20@v1=20?= =?UTF-8?q?pin=20=E2=80=94=20lock,=20validator,=20and=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #263 adopted the moving major tag in the manifests. Sync poetry.lock to that tag and replace the SHA fail-closed check so main is coherent. Issue-Remediation-Cycle: Quantum-L9/Cognitive.Engine.Graphs#266/cycle-1 Co-authored-by: Cursor --- .pre-commit-config.yaml | 10 ++++ Makefile | 7 ++- poetry.lock | 10 ++-- scripts/validate_sdk_pin.py | 88 +++++++++++++++++++++-------- tests/unit/test_node_app.py | 2 +- tests/unit/test_validate_sdk_pin.py | 61 ++++++++++++++++++++ 6 files changed, 149 insertions(+), 29 deletions(-) create mode 100644 tests/unit/test_validate_sdk_pin.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a5acb3b5..b5a93962 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -79,6 +79,16 @@ repos: language: system pass_filenames: false + # Gate_SDK moving-major pin (CEG#266 / PR #263) + - repo: local + hooks: + - id: validate-sdk-pin + name: Gate_SDK moving-major pin + entry: python3 scripts/validate_sdk_pin.py + language: system + pass_filenames: false + always_run: true + # L9 contract enforcement (24 invariants, 27 docs) - repo: local hooks: diff --git a/Makefile b/Makefile index 06f64a9c..f25d5a5a 100644 --- a/Makefile +++ b/Makefile @@ -209,7 +209,7 @@ clean: ## Remove volumes + containers # ── Quality Gates (local, no Docker) ─────────────────────── -.PHONY: lint lint-fix typecheck check +.PHONY: lint lint-fix typecheck check sdk-pin lint: ## Ruff lint + format check (no mutation) + MyPy — matches CI's blocking gate ruff check . @@ -223,12 +223,17 @@ lint-fix: ## Autofix: ruff check --fix + ruff format . (run this when `make lint typecheck: ## MyPy type checking on engine/ mypy engine/ +sdk-pin: ## Fail closed unless Gate_SDK is pinned to moving major tag @v1 + python3 scripts/validate_sdk_pin.py + check: ## Full local quality gate (autofix lint + types + unit tests) @echo "── Lint (autofix) ──" @ruff check . --fix @ruff format . @echo "── Type Check ──" @mypy engine/ + @echo "── SDK pin ──" + @python3 scripts/validate_sdk_pin.py @echo "── Unit Tests ──" @PYTHONPATH="$${PYTHONPATH}:." python3 -m pytest tests/ -m "unit" --tb=short -q @echo "── All checks passed ──" diff --git a/poetry.lock b/poetry.lock index c9642213..ef787392 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "annotated-doc" @@ -487,7 +487,7 @@ markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win [[package]] name = "constellation-node-sdk" -version = "1.0.1" +version = "1.1.0" description = "L9 Constellation Node SDK — TransportPacket runtime, Gate client, and node factory" optional = false python-versions = ">=3.12" @@ -511,8 +511,8 @@ dev = ["build (>=1.2.0)", "mypy (>=1.11.0)", "pytest (>=8.3.0)", "pytest-asyncio [package.source] type = "git" url = "https://github.com/Quantum-L9/Gate_SDK.git" -reference = "69c6c67060b08440734a61473c03663423709964" -resolved_reference = "69c6c67060b08440734a61473c03663423709964" +reference = "v1" +resolved_reference = "e9f829f982110be13752da8f18c7a9692e8ed908" [[package]] name = "coverage" @@ -2715,4 +2715,4 @@ dev = ["pytest", "setuptools"] [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "c081e965d06e1b7f817468590c9f37dc7d415c371d7652eef528623bac6100b0" +content-hash = "1e726460d8433f929a08ddca9a953e4b2af22a87b594fc075e64a48c378147be" diff --git a/scripts/validate_sdk_pin.py b/scripts/validate_sdk_pin.py index 0db0466f..a7627888 100644 --- a/scripts/validate_sdk_pin.py +++ b/scripts/validate_sdk_pin.py @@ -1,27 +1,71 @@ #!/usr/bin/env python3 -"""Fail closed unless Gate_SDK pin matches the immutable constellation SHA.""" +"""Fail closed unless Gate_SDK is pinned to the moving major tag. +Policy (CEG#266 / PR #263): constellation-node-sdk tracks ``Quantum-L9/Gate_SDK@v1``. +Manifests name the major tag. poetry.lock records that tag as ``reference`` and +the resolved object as ``resolved_reference``. +""" + +from __future__ import annotations + +import re from pathlib import Path ROOT = Path(__file__).resolve().parents[1] -PIN = "69c6c67060b08440734a61473c03663423709964" -errors: list[str] = [] - -for rel in ["pyproject.toml", "requirements.txt", "poetry.lock"]: - path = ROOT / rel - if not path.exists(): - continue - text = path.read_text() - if "Quantum-L9/Gate_SDK" not in text: - errors.append(f"{rel}: missing Quantum-L9") - if PIN not in text: - errors.append(f"{rel}: missing pin") - if "cryptoxdog/Gate_SDK" in text: - errors.append(f"{rel}: cryptoxdog remains") - -if errors: - print("FAIL") - print("\n".join(errors)) - raise SystemExit(1) - -print("PASS CEG pin", PIN) +MAJOR_TAG = "v1" +CANONICAL_REPO = "Quantum-L9/Gate_SDK" +FORBIDDEN_FORK = "cryptoxdog/Gate_SDK" +SHA_RE = re.compile(r"\b[0-9a-f]{40}\b") +LOCK_REFERENCE_RE = re.compile( + r'name = "constellation-node-sdk".*?\[package\.source\].*?' + r'reference = "([^"]+)".*?resolved_reference = "([0-9a-f]{40})"', + re.DOTALL, +) + + +def check_text(rel: str, text: str) -> list[str]: + errors: list[str] = [] + if CANONICAL_REPO not in text: + errors.append(f"{rel}: missing {CANONICAL_REPO}") + if FORBIDDEN_FORK in text: + errors.append(f"{rel}: {FORBIDDEN_FORK} remains") + if rel == "poetry.lock": + match = LOCK_REFERENCE_RE.search(text) + if match is None: + errors.append(f"{rel}: constellation-node-sdk source block missing") + else: + reference, resolved = match.group(1), match.group(2) + if reference != MAJOR_TAG: + errors.append(f"{rel}: reference={reference!r} (want {MAJOR_TAG!r})") + if not SHA_RE.fullmatch(resolved): + errors.append(f"{rel}: resolved_reference is not a SHA") + else: + if MAJOR_TAG not in text: + errors.append(f"{rel}: missing moving major tag {MAJOR_TAG}") + if SHA_RE.search(text): + errors.append(f"{rel}: immutable SHA pin leftover") + return errors + + +def check_tree(root: Path) -> list[str]: + errors: list[str] = [] + for rel in ("pyproject.toml", "requirements.txt", "poetry.lock"): + path = root / rel + if not path.exists(): + continue + errors.extend(check_text(rel, path.read_text(encoding="utf-8"))) + return errors + + +def main() -> int: + errors = check_tree(ROOT) + if errors: + print("FAIL") + print("\n".join(errors)) + return 1 + print(f"PASS CEG pin {CANONICAL_REPO}@{MAJOR_TAG}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/unit/test_node_app.py b/tests/unit/test_node_app.py index d2c02d2b..585fa790 100644 --- a/tests/unit/test_node_app.py +++ b/tests/unit/test_node_app.py @@ -317,7 +317,7 @@ def test_max_attachments_without_schemes_is_rejected() -> None: def test_sdk_default_attachment_caps_construct() -> None: - """Pinned SDK (69c6c67 = main a0827f2 + verifying-keys env fix) ships mutually valid default attachment/packet caps. + """Pinned SDK (@v1 / 1.1.0) ships mutually valid default attachment/packet caps. Earlier pins (a770e853) defaulted max_attachment_size_bytes above max_packet_bytes and failed closed on a bare construct. The current pin diff --git a/tests/unit/test_validate_sdk_pin.py b/tests/unit/test_validate_sdk_pin.py new file mode 100644 index 00000000..c12cb82e --- /dev/null +++ b/tests/unit/test_validate_sdk_pin.py @@ -0,0 +1,61 @@ +"""Unit tests — moving-major Gate_SDK pin validator (CEG#266).""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path + +import pytest + +_ROOT = Path(__file__).resolve().parents[2] +_SPEC = importlib.util.spec_from_file_location( + "validate_sdk_pin", + _ROOT / "scripts" / "validate_sdk_pin.py", +) +assert _SPEC is not None and _SPEC.loader is not None +_mod = importlib.util.module_from_spec(_SPEC) +_SPEC.loader.exec_module(_mod) +MAJOR_TAG = _mod.MAJOR_TAG +check_text = _mod.check_text +check_tree = _mod.check_tree + + +@pytest.mark.unit +def test_manifests_accept_major_tag() -> None: + pyproject = 'constellation-node-sdk = {git = "https://github.com/Quantum-L9/Gate_SDK.git", rev = "v1"}\n' + requirements = "constellation-node-sdk @ git+https://github.com/Quantum-L9/Gate_SDK.git@v1\n" + assert check_text("pyproject.toml", pyproject) == [] + assert check_text("requirements.txt", requirements) == [] + + +@pytest.mark.unit +def test_manifests_reject_sha_and_fork() -> None: + sha = "69c6c67060b08440734a61473c03663423709964" + text = f'constellation-node-sdk = {{git = "https://github.com/cryptoxdog/Gate_SDK.git", rev = "{sha}"}}\n' + errors = check_text("pyproject.toml", text) + assert any("cryptoxdog" in item for item in errors) + assert any("SHA" in item for item in errors) + assert any("Quantum-L9" in item for item in errors) + + +@pytest.mark.unit +def test_lock_requires_tag_reference_and_resolved_sha() -> None: + lock = ( + 'name = "constellation-node-sdk"\n' + "version = \"1.1.0\"\n" + "[package.source]\n" + 'type = "git"\n' + 'url = "https://github.com/Quantum-L9/Gate_SDK.git"\n' + 'reference = "v1"\n' + 'resolved_reference = "e9f829f982110be13752da8f18c7a9692e8ed908"\n' + ) + assert check_text("poetry.lock", lock) == [] + stale = lock.replace('reference = "v1"', 'reference = "69c6c67060b08440734a61473c03663423709964"') + assert check_text("poetry.lock", stale) + + +@pytest.mark.unit +def test_repo_tree_matches_v1_policy() -> None: + errors = check_tree(_ROOT) + assert errors == [], errors + assert MAJOR_TAG == "v1" From 6e32738b2faddeba8934d83b2debc53ad4bf7a67 Mon Sep 17 00:00:00 2001 From: Igor Beylin Date: Sat, 19 Sep 2026 10:21:10 -0400 Subject: [PATCH 2/3] style: commit gate writer rewrites so make pr finishes once --- tests/unit/test_validate_sdk_pin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_validate_sdk_pin.py b/tests/unit/test_validate_sdk_pin.py index c12cb82e..47a81ff8 100644 --- a/tests/unit/test_validate_sdk_pin.py +++ b/tests/unit/test_validate_sdk_pin.py @@ -42,7 +42,7 @@ def test_manifests_reject_sha_and_fork() -> None: def test_lock_requires_tag_reference_and_resolved_sha() -> None: lock = ( 'name = "constellation-node-sdk"\n' - "version = \"1.1.0\"\n" + 'version = "1.1.0"\n' "[package.source]\n" 'type = "git"\n' 'url = "https://github.com/Quantum-L9/Gate_SDK.git"\n' From eb6d085fffb80f5be429cb261c1bf5c1e522cf64 Mon Sep 17 00:00:00 2001 From: Igor Beylin Date: Sat, 19 Sep 2026 10:21:43 -0400 Subject: [PATCH 3/3] fix(deps): keep @v1 pin off Makefile/pre-commit overlap with #280 Leave Make/hook wiring to the cypher-lint PR that already owns those files. The lock and validator still complete the #263/#266 decision. Issue-Remediation-Cycle: Quantum-L9/Cognitive.Engine.Graphs#266/cycle-1 Co-authored-by: Cursor --- .pre-commit-config.yaml | 10 ---------- Makefile | 7 +------ tests/unit/test_validate_sdk_pin.py | 5 ++++- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b5a93962..a5acb3b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -79,16 +79,6 @@ repos: language: system pass_filenames: false - # Gate_SDK moving-major pin (CEG#266 / PR #263) - - repo: local - hooks: - - id: validate-sdk-pin - name: Gate_SDK moving-major pin - entry: python3 scripts/validate_sdk_pin.py - language: system - pass_filenames: false - always_run: true - # L9 contract enforcement (24 invariants, 27 docs) - repo: local hooks: diff --git a/Makefile b/Makefile index f25d5a5a..06f64a9c 100644 --- a/Makefile +++ b/Makefile @@ -209,7 +209,7 @@ clean: ## Remove volumes + containers # ── Quality Gates (local, no Docker) ─────────────────────── -.PHONY: lint lint-fix typecheck check sdk-pin +.PHONY: lint lint-fix typecheck check lint: ## Ruff lint + format check (no mutation) + MyPy — matches CI's blocking gate ruff check . @@ -223,17 +223,12 @@ lint-fix: ## Autofix: ruff check --fix + ruff format . (run this when `make lint typecheck: ## MyPy type checking on engine/ mypy engine/ -sdk-pin: ## Fail closed unless Gate_SDK is pinned to moving major tag @v1 - python3 scripts/validate_sdk_pin.py - check: ## Full local quality gate (autofix lint + types + unit tests) @echo "── Lint (autofix) ──" @ruff check . --fix @ruff format . @echo "── Type Check ──" @mypy engine/ - @echo "── SDK pin ──" - @python3 scripts/validate_sdk_pin.py @echo "── Unit Tests ──" @PYTHONPATH="$${PYTHONPATH}:." python3 -m pytest tests/ -m "unit" --tb=short -q @echo "── All checks passed ──" diff --git a/tests/unit/test_validate_sdk_pin.py b/tests/unit/test_validate_sdk_pin.py index 47a81ff8..4e2b4e4e 100644 --- a/tests/unit/test_validate_sdk_pin.py +++ b/tests/unit/test_validate_sdk_pin.py @@ -12,7 +12,10 @@ "validate_sdk_pin", _ROOT / "scripts" / "validate_sdk_pin.py", ) -assert _SPEC is not None and _SPEC.loader is not None +if _SPEC is None: + raise RuntimeError("validate_sdk_pin.py did not load") +if _SPEC.loader is None: + raise RuntimeError("validate_sdk_pin.py has no loader") _mod = importlib.util.module_from_spec(_SPEC) _SPEC.loader.exec_module(_mod) MAJOR_TAG = _mod.MAJOR_TAG