From 51140dd0523089372025d9fe95685eba5628316d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Harjam=C3=A4ki?= Date: Fri, 3 Jul 2026 19:30:54 +0300 Subject: [PATCH 1/6] ci: add dependabot github-actions updates Add a github-actions package ecosystem entry so third-party actions pinned with mutable tags (e.g. @v4) get auto-updated weekly. Co-Authored-By: Claude Opus 4.8 --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 01f3618..acc7eca 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,3 +8,11 @@ updates: all: patterns: - "*" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + groups: + all: + patterns: + - "*" From 4f6c1cb6a1b646ed217c6ead67902a92f76ffe78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Harjam=C3=A4ki?= Date: Sat, 4 Jul 2026 22:05:22 +0300 Subject: [PATCH 2/6] ci: harden workflow permissions and timeouts --- .github/workflows/codeql.yml | 1 + .github/workflows/pages.yml | 7 +++++-- .github/workflows/pr-lint.yml | 3 +++ .github/workflows/stale.yml | 4 ++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e4b4c1b..1806c7b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -8,6 +8,7 @@ jobs: analyze: name: Analyze runs-on: ubuntu-latest + timeout-minutes: 30 permissions: actions: read contents: read diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 6c1d78d..f37e918 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -6,14 +6,13 @@ on: - master permissions: contents: read - pages: write - id-token: write concurrency: group: "pages" cancel-in-progress: false jobs: build: runs-on: ubuntu-latest + timeout-minutes: 15 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -25,10 +24,14 @@ jobs: with: path: ./site deploy: + permissions: + pages: write + id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest + timeout-minutes: 10 needs: build steps: - name: Deploy to GitHub Pages diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml index fa422bc..2db1639 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/pr-lint.yml @@ -9,6 +9,9 @@ on: jobs: main: runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + pull-requests: read steps: - uses: amannn/action-semantic-pull-request@v5 env: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 15bb31c..704a0f4 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -6,6 +6,10 @@ on: jobs: stale: runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + issues: write + pull-requests: write steps: - uses: actions/stale@v8 with: From 4dc270ef86fc4d5c06a7841c1ca9ece1bc9920ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Harjam=C3=A4ki?= Date: Sat, 4 Jul 2026 22:09:21 +0300 Subject: [PATCH 3/6] test(contracts): verify published registry drift --- .github/workflows/contract-registry-live.yml | 41 +++++++++++++++++++ .../cas-contracts/v1.1.0/common.schema.json | 2 +- .../cas-contracts/v1.1.0/manifest.json | 24 +++++------ .../phase-execution-request.schema.json | 2 +- .../v1.1.0/phase-execution-result.schema.json | 2 +- .../phase-verification-result.schema.json | 2 +- .../v1.1.0/sdlc-lifecycle-event.schema.json | 2 +- .../v1.1.0/sdlc-profile.schema.json | 2 +- 8 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/contract-registry-live.yml diff --git a/.github/workflows/contract-registry-live.yml b/.github/workflows/contract-registry-live.yml new file mode 100644 index 0000000..6c859a6 --- /dev/null +++ b/.github/workflows/contract-registry-live.yml @@ -0,0 +1,41 @@ +name: Contract registry live drift + +on: + workflow_dispatch: + schedule: + - cron: "17 5 * * 1" + +permissions: + contents: read + +jobs: + verify-v1-1: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - name: Compare published v1.1.0 release with vendored contracts + env: + REGISTRY_BASE: https://coding-autopilot-system.github.io/cas-contracts/registry + PINNED_VERSION: 1.1.0 + VENDORED_ROOT: tests/contracts/cas-contracts/v1.1.0 + shell: python + run: | + import hashlib, json, os, pathlib, urllib.request + base = os.environ["REGISTRY_BASE"] + version = os.environ["PINNED_VERSION"] + root = pathlib.Path(os.environ["VENDORED_ROOT"]) + with urllib.request.urlopen(f"{base}/index.json", timeout=20) as response: + index = json.load(response) + if version not in index.get("releases", []): + raise SystemExit(f"published registry is missing release {version}") + with urllib.request.urlopen(f"{base}/releases/v{version}/manifest.json", timeout=20) as response: + manifest = json.load(response) + for entry in manifest["schemas"]: + local = (root / entry["path"]).read_bytes() + if hashlib.sha256(local).hexdigest() != entry["sha256"]: + raise SystemExit(f"vendored digest drift: {entry['path']}") + with urllib.request.urlopen(f"{base}/releases/v{version}/{entry['path']}", timeout=20) as response: + published = response.read() + if published != local: + raise SystemExit(f"published content drift: {entry['path']}") diff --git a/tests/contracts/cas-contracts/v1.1.0/common.schema.json b/tests/contracts/cas-contracts/v1.1.0/common.schema.json index 60ab249..a393187 100644 --- a/tests/contracts/cas-contracts/v1.1.0/common.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/common.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://schemas.coding-autopilot.dev/v1.1/common.schema.json", + "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/common.schema.json", "title": "CAS Common Definitions v1.1", "$defs": { "actor": { diff --git a/tests/contracts/cas-contracts/v1.1.0/manifest.json b/tests/contracts/cas-contracts/v1.1.0/manifest.json index c0bd518..ee011de 100644 --- a/tests/contracts/cas-contracts/v1.1.0/manifest.json +++ b/tests/contracts/cas-contracts/v1.1.0/manifest.json @@ -2,34 +2,34 @@ "version": "1.1.0", "schemas": [ { - "id": "https://schemas.coding-autopilot.dev/v1.1/common.schema.json", + "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/common.schema.json", "path": "common.schema.json", - "sha256": "078026d60c219658fe0fc5cf7c4f786acdcf0c492121f7d688d5078b33ca25af" + "sha256": "111ea168b873aa99a5f2d731f1295320c83c8424156546be9dbdc5624c597562" }, { - "id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-request.schema.json", + "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-execution-request.schema.json", "path": "phase-execution-request.schema.json", - "sha256": "927ffda5ba809e58627ba25b3275355644b510b0ae1c76f446aa7b7f291f08fd" + "sha256": "a9dd60ade628f98cdc97fec9900f4b429f81e0fe07413a2ff21cd42188f85fb6" }, { - "id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-result.schema.json", + "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-execution-result.schema.json", "path": "phase-execution-result.schema.json", - "sha256": "1d353e20cf6338c2de4a9bec0bad83ca0dec6c9feb5434c5e6264fdc075e7c25" + "sha256": "7c56cc51aa0f8ae878a4f8219a4b1bedc20697f57d557797a0907a7163df1f7e" }, { - "id": "https://schemas.coding-autopilot.dev/v1.1/phase-verification-result.schema.json", + "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-verification-result.schema.json", "path": "phase-verification-result.schema.json", - "sha256": "ec5b97a2bf75892530442f666d23a8b6dbb8f13bda0efe49553d3f64cd41b995" + "sha256": "800cfceecd61a16883a5be67f0da9ff9fa8865450c62e92676a878189c12b6cc" }, { - "id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-lifecycle-event.schema.json", + "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/sdlc-lifecycle-event.schema.json", "path": "sdlc-lifecycle-event.schema.json", - "sha256": "bd955637761679d3b6e8ee09fb7b693b1d0362615b0865204c5ae3bacb3809dc" + "sha256": "f746915fc3687496be216d0b8bcfec0398a8c5eebb765ff8c63e7c9ee88b174f" }, { - "id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-profile.schema.json", + "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/sdlc-profile.schema.json", "path": "sdlc-profile.schema.json", - "sha256": "f4974e911e7cb88191c1a331470c8cd7a79d1c7311c963dd70cd9c8f524ebe45" + "sha256": "80e75549b7ef49182aba640576228db6bed674f15847b154bbf83d7953e23334" } ] } diff --git a/tests/contracts/cas-contracts/v1.1.0/phase-execution-request.schema.json b/tests/contracts/cas-contracts/v1.1.0/phase-execution-request.schema.json index a9b9ab7..79170f4 100644 --- a/tests/contracts/cas-contracts/v1.1.0/phase-execution-request.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/phase-execution-request.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-request.schema.json", + "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-execution-request.schema.json", "title": "PhaseExecutionRequest", "type": "object", "allOf": [ diff --git a/tests/contracts/cas-contracts/v1.1.0/phase-execution-result.schema.json b/tests/contracts/cas-contracts/v1.1.0/phase-execution-result.schema.json index db58ea3..05b4f56 100644 --- a/tests/contracts/cas-contracts/v1.1.0/phase-execution-result.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/phase-execution-result.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-result.schema.json", + "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-execution-result.schema.json", "title": "PhaseExecutionResult", "type": "object", "allOf": [ diff --git a/tests/contracts/cas-contracts/v1.1.0/phase-verification-result.schema.json b/tests/contracts/cas-contracts/v1.1.0/phase-verification-result.schema.json index 90b6819..fa0229b 100644 --- a/tests/contracts/cas-contracts/v1.1.0/phase-verification-result.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/phase-verification-result.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://schemas.coding-autopilot.dev/v1.1/phase-verification-result.schema.json", + "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-verification-result.schema.json", "title": "PhaseVerificationResult", "type": "object", "allOf": [ diff --git a/tests/contracts/cas-contracts/v1.1.0/sdlc-lifecycle-event.schema.json b/tests/contracts/cas-contracts/v1.1.0/sdlc-lifecycle-event.schema.json index 0d7d8bf..95752cd 100644 --- a/tests/contracts/cas-contracts/v1.1.0/sdlc-lifecycle-event.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/sdlc-lifecycle-event.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-lifecycle-event.schema.json", + "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/sdlc-lifecycle-event.schema.json", "title": "SdlcLifecycleEvent", "type": "object", "allOf": [ diff --git a/tests/contracts/cas-contracts/v1.1.0/sdlc-profile.schema.json b/tests/contracts/cas-contracts/v1.1.0/sdlc-profile.schema.json index 92a0404..ff2d077 100644 --- a/tests/contracts/cas-contracts/v1.1.0/sdlc-profile.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/sdlc-profile.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-profile.schema.json", + "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/sdlc-profile.schema.json", "title": "SdlcProfile", "type": "object", "allOf": [ From ad873433daa708126441767376d90010ee37253f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Harjam=C3=A4ki?= Date: Sat, 4 Jul 2026 22:19:37 +0300 Subject: [PATCH 4/6] fix(runtime): bound worker request payloads --- maf_starter/loop_worker_cli.py | 12 +++++++++++- tests/test_loop_worker_cli.py | 21 ++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/maf_starter/loop_worker_cli.py b/maf_starter/loop_worker_cli.py index 0d2e30a..87fcf67 100644 --- a/maf_starter/loop_worker_cli.py +++ b/maf_starter/loop_worker_cli.py @@ -14,6 +14,8 @@ run_bounded_specialists, ) +MAX_REQUEST_BYTES = 1_000_000 + @dataclass(frozen=True) class WorkerEnvelope: @@ -68,7 +70,15 @@ def main() -> int: parser.add_argument("--request", help="JSON worker request; stdin is used when omitted") args = parser.parse_args() try: - payload = json.loads(args.request if args.request is not None else sys.stdin.read()) + if args.request is not None: + request_text = args.request + request_size = len(request_text.encode("utf-8")) + else: + request_text = sys.stdin.read(MAX_REQUEST_BYTES + 1) + request_size = len(request_text.encode("utf-8")) + if request_size > MAX_REQUEST_BYTES: + raise ValueError(f"Worker request exceeds {MAX_REQUEST_BYTES} bytes") + payload = json.loads(request_text) result = asyncio.run(execute_request(payload)) except (ValueError, json.JSONDecodeError) as error: print(json.dumps({"error": str(error)}), file=sys.stderr) diff --git a/tests/test_loop_worker_cli.py b/tests/test_loop_worker_cli.py index cfd92ec..2e173fc 100644 --- a/tests/test_loop_worker_cli.py +++ b/tests/test_loop_worker_cli.py @@ -1,7 +1,12 @@ import asyncio +import io +import json +import sys import unittest +from contextlib import redirect_stderr +from unittest.mock import patch -from maf_starter.loop_worker_cli import execute_request +from maf_starter.loop_worker_cli import MAX_REQUEST_BYTES, execute_request, main class LoopWorkerCliTests(unittest.TestCase): @@ -22,3 +27,17 @@ def test_cli_request_executes_real_bounded_specialist_runtime(self) -> None: self.assertEqual(3, result.peakConcurrency) self.assertEqual(("research", "architecture", "security", "test"), result.roles) self.assertEqual(4, len(result.evidenceUris)) + + def test_cli_rejects_oversized_stdin_before_json_parsing(self) -> None: + oversized = json.dumps({"padding": "x" * MAX_REQUEST_BYTES}) + stderr = io.StringIO() + + with ( + patch.object(sys, "argv", ["loop_worker_cli"]), + patch.object(sys, "stdin", io.StringIO(oversized)), + redirect_stderr(stderr), + ): + exit_code = main() + + self.assertEqual(2, exit_code) + self.assertIn("exceeds", stderr.getvalue()) From 109bca33238dafb75ef27a8339fde421a49083ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Harjam=C3=A4ki?= Date: Sat, 4 Jul 2026 23:07:26 +0300 Subject: [PATCH 5/6] fix(contracts): restore canonical CAS schema ids --- .../cas-contracts/v1.1.0/common.schema.json | 2 +- .../cas-contracts/v1.1.0/manifest.json | 24 +++++++++---------- .../phase-execution-request.schema.json | 2 +- .../v1.1.0/phase-execution-result.schema.json | 2 +- .../phase-verification-result.schema.json | 2 +- .../v1.1.0/sdlc-lifecycle-event.schema.json | 2 +- .../v1.1.0/sdlc-profile.schema.json | 2 +- tests/test_contract_compatibility.py | 16 +++++++++++-- 8 files changed, 32 insertions(+), 20 deletions(-) diff --git a/tests/contracts/cas-contracts/v1.1.0/common.schema.json b/tests/contracts/cas-contracts/v1.1.0/common.schema.json index a393187..cbea0d7 100644 --- a/tests/contracts/cas-contracts/v1.1.0/common.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/common.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/common.schema.json", + "$id": "https://schemas.coding-autopilot.dev/v1.1/common.schema.json", "title": "CAS Common Definitions v1.1", "$defs": { "actor": { diff --git a/tests/contracts/cas-contracts/v1.1.0/manifest.json b/tests/contracts/cas-contracts/v1.1.0/manifest.json index ee011de..0ff9c4b 100644 --- a/tests/contracts/cas-contracts/v1.1.0/manifest.json +++ b/tests/contracts/cas-contracts/v1.1.0/manifest.json @@ -2,34 +2,34 @@ "version": "1.1.0", "schemas": [ { - "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/common.schema.json", + "id": "https://schemas.coding-autopilot.dev/v1.1/common.schema.json", "path": "common.schema.json", - "sha256": "111ea168b873aa99a5f2d731f1295320c83c8424156546be9dbdc5624c597562" + "sha256": "75d300c99779327d90872373bfc1661ba04ca1bb52335dec45fe926594ea5d7b" }, { - "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-execution-request.schema.json", + "id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-request.schema.json", "path": "phase-execution-request.schema.json", - "sha256": "a9dd60ade628f98cdc97fec9900f4b429f81e0fe07413a2ff21cd42188f85fb6" + "sha256": "4577f1dc99bd04b0ccd84255d4f34935a686273f4ed4cd030ba32e2caf54315a" }, { - "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-execution-result.schema.json", + "id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-result.schema.json", "path": "phase-execution-result.schema.json", - "sha256": "7c56cc51aa0f8ae878a4f8219a4b1bedc20697f57d557797a0907a7163df1f7e" + "sha256": "bcffcbcf4e502a1d578ccce3ba0326e91f5562cd1979a83489b394cdeb4b50e1" }, { - "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-verification-result.schema.json", + "id": "https://schemas.coding-autopilot.dev/v1.1/phase-verification-result.schema.json", "path": "phase-verification-result.schema.json", - "sha256": "800cfceecd61a16883a5be67f0da9ff9fa8865450c62e92676a878189c12b6cc" + "sha256": "ec84a47574424a5c148030f87d8a3f3cdd3041ce24056242fb6f81ddfd3c1340" }, { - "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/sdlc-lifecycle-event.schema.json", + "id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-lifecycle-event.schema.json", "path": "sdlc-lifecycle-event.schema.json", - "sha256": "f746915fc3687496be216d0b8bcfec0398a8c5eebb765ff8c63e7c9ee88b174f" + "sha256": "6e397fb7f76703593fcecb95c1b53d6de0657157d7b99272ca2bb310de5d780f" }, { - "id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/sdlc-profile.schema.json", + "id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-profile.schema.json", "path": "sdlc-profile.schema.json", - "sha256": "80e75549b7ef49182aba640576228db6bed674f15847b154bbf83d7953e23334" + "sha256": "c435ceadceb9bde7f5c698f9ce896c12f6e5adba3811191a820e472d151ef971" } ] } diff --git a/tests/contracts/cas-contracts/v1.1.0/phase-execution-request.schema.json b/tests/contracts/cas-contracts/v1.1.0/phase-execution-request.schema.json index 79170f4..3a0fc1b 100644 --- a/tests/contracts/cas-contracts/v1.1.0/phase-execution-request.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/phase-execution-request.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-execution-request.schema.json", + "$id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-request.schema.json", "title": "PhaseExecutionRequest", "type": "object", "allOf": [ diff --git a/tests/contracts/cas-contracts/v1.1.0/phase-execution-result.schema.json b/tests/contracts/cas-contracts/v1.1.0/phase-execution-result.schema.json index 05b4f56..76b59b2 100644 --- a/tests/contracts/cas-contracts/v1.1.0/phase-execution-result.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/phase-execution-result.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-execution-result.schema.json", + "$id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-result.schema.json", "title": "PhaseExecutionResult", "type": "object", "allOf": [ diff --git a/tests/contracts/cas-contracts/v1.1.0/phase-verification-result.schema.json b/tests/contracts/cas-contracts/v1.1.0/phase-verification-result.schema.json index fa0229b..51aecdc 100644 --- a/tests/contracts/cas-contracts/v1.1.0/phase-verification-result.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/phase-verification-result.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/phase-verification-result.schema.json", + "$id": "https://schemas.coding-autopilot.dev/v1.1/phase-verification-result.schema.json", "title": "PhaseVerificationResult", "type": "object", "allOf": [ diff --git a/tests/contracts/cas-contracts/v1.1.0/sdlc-lifecycle-event.schema.json b/tests/contracts/cas-contracts/v1.1.0/sdlc-lifecycle-event.schema.json index 95752cd..15a27be 100644 --- a/tests/contracts/cas-contracts/v1.1.0/sdlc-lifecycle-event.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/sdlc-lifecycle-event.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/sdlc-lifecycle-event.schema.json", + "$id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-lifecycle-event.schema.json", "title": "SdlcLifecycleEvent", "type": "object", "allOf": [ diff --git a/tests/contracts/cas-contracts/v1.1.0/sdlc-profile.schema.json b/tests/contracts/cas-contracts/v1.1.0/sdlc-profile.schema.json index ff2d077..11f2042 100644 --- a/tests/contracts/cas-contracts/v1.1.0/sdlc-profile.schema.json +++ b/tests/contracts/cas-contracts/v1.1.0/sdlc-profile.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://coding-autopilot-system.github.io/cas-contracts/registry/v1.1/sdlc-profile.schema.json", + "$id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-profile.schema.json", "title": "SdlcProfile", "type": "object", "allOf": [ diff --git a/tests/test_contract_compatibility.py b/tests/test_contract_compatibility.py index a165d6e..306acab 100644 --- a/tests/test_contract_compatibility.py +++ b/tests/test_contract_compatibility.py @@ -67,6 +67,18 @@ def _validate(schema_name: str, instance: dict[str, Any]) -> None: Draft202012Validator(schema, registry=_registry()).validate(instance) +def _upstream_release_is_canonical() -> bool: + manifest_path = UPSTREAM_ROOT / "manifest.json" + if not manifest_path.exists(): + return False + + manifest = _load_json(manifest_path) + return all( + entry["id"].startswith("https://schemas.coding-autopilot.dev/") + for entry in manifest["schemas"] + ) + + # Shared lifecycle metadata every v1.1 contract requires (common.schema.json#lifecycleMetadata). def _lifecycle() -> dict[str, Any]: return { @@ -181,8 +193,8 @@ def test_sdlc_profile_wire_payload_conforms() -> None: @pytest.mark.skipif( - not UPSTREAM_ROOT.exists(), - reason="sibling cas-contracts checkout not present (expected in isolated CI)", + not _upstream_release_is_canonical(), + reason="sibling cas-contracts checkout is absent or still on the legacy namespace", ) def test_vendored_release_matches_upstream_source_of_truth() -> None: """Local-only drift guard: vendored copy must equal the sibling cas-contracts release. From dc0712721abca7732fbc3d598e89418533fe17d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Harjam=C3=A4ki?= Date: Sun, 5 Jul 2026 10:50:14 +0300 Subject: [PATCH 6/6] fix(contracts): pin canonical v1.1.1 release --- .github/workflows/contract-registry-live.yml | 6 +- .../cas-contracts/v1.1.1/common.schema.json | 103 ++++++++++++++++++ .../cas-contracts/v1.1.1/manifest.json | 35 ++++++ .../phase-execution-request.schema.json | 35 ++++++ .../v1.1.1/phase-execution-result.schema.json | 27 +++++ .../phase-verification-result.schema.json | 27 +++++ .../v1.1.1/sdlc-lifecycle-event.schema.json | 23 ++++ .../v1.1.1/sdlc-profile.schema.json | 67 ++++++++++++ tests/test_contract_compatibility.py | 13 ++- 9 files changed, 327 insertions(+), 9 deletions(-) create mode 100644 tests/contracts/cas-contracts/v1.1.1/common.schema.json create mode 100644 tests/contracts/cas-contracts/v1.1.1/manifest.json create mode 100644 tests/contracts/cas-contracts/v1.1.1/phase-execution-request.schema.json create mode 100644 tests/contracts/cas-contracts/v1.1.1/phase-execution-result.schema.json create mode 100644 tests/contracts/cas-contracts/v1.1.1/phase-verification-result.schema.json create mode 100644 tests/contracts/cas-contracts/v1.1.1/sdlc-lifecycle-event.schema.json create mode 100644 tests/contracts/cas-contracts/v1.1.1/sdlc-profile.schema.json diff --git a/.github/workflows/contract-registry-live.yml b/.github/workflows/contract-registry-live.yml index 6c859a6..7374641 100644 --- a/.github/workflows/contract-registry-live.yml +++ b/.github/workflows/contract-registry-live.yml @@ -14,11 +14,11 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - - name: Compare published v1.1.0 release with vendored contracts + - name: Compare published v1.1.1 release with vendored contracts env: REGISTRY_BASE: https://coding-autopilot-system.github.io/cas-contracts/registry - PINNED_VERSION: 1.1.0 - VENDORED_ROOT: tests/contracts/cas-contracts/v1.1.0 + PINNED_VERSION: 1.1.1 + VENDORED_ROOT: tests/contracts/cas-contracts/v1.1.1 shell: python run: | import hashlib, json, os, pathlib, urllib.request diff --git a/tests/contracts/cas-contracts/v1.1.1/common.schema.json b/tests/contracts/cas-contracts/v1.1.1/common.schema.json new file mode 100644 index 0000000..b23a790 --- /dev/null +++ b/tests/contracts/cas-contracts/v1.1.1/common.schema.json @@ -0,0 +1,103 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.coding-autopilot.dev/v1.1/common.schema.json", + "title": "CAS Common Definitions v1.1", + "$defs": { + "actor": { + "type": "object", + "additionalProperties": false, + "required": ["id", "type"], + "properties": { + "id": { "type": "string", "minLength": 1, "maxLength": 256 }, + "type": { + "type": "string", + "enum": ["human", "agent", "service", "workflow"] + }, + "displayName": { "type": "string", "minLength": 1, "maxLength": 256 } + } + }, + "traceContext": { + "type": "object", + "additionalProperties": false, + "required": ["traceparent"], + "properties": { + "traceparent": { + "type": "string", + "pattern": "^[\\da-f]{2}-[\\da-f]{32}-[\\da-f]{16}-[\\da-f]{2}$" + }, + "tracestate": { "type": "string", "maxLength": 512 } + } + }, + "lifecycleMetadata": { + "type": "object", + "required": [ + "correlationId", + "promptId", + "runId", + "repo", + "actor", + "timestamp", + "schemaVersion", + "traceContext" + ], + "properties": { + "correlationId": { "type": "string", "minLength": 1, "maxLength": 128 }, + "promptId": { "type": "string", "minLength": 1, "maxLength": 128 }, + "runId": { "type": "string", "minLength": 1, "maxLength": 128 }, + "repo": { + "type": "string", + "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$" + }, + "actor": { "$ref": "#/$defs/actor" }, + "timestamp": { "type": "string", "format": "date-time" }, + "schemaVersion": { "const": "1.1.0" }, + "traceContext": { "$ref": "#/$defs/traceContext" } + } + }, + "evidence": { + "type": "object", + "additionalProperties": false, + "required": ["kind", "uri"], + "properties": { + "kind": { "type": "string", "minLength": 1, "maxLength": 64 }, + "uri": { "type": "string", "format": "uri" }, + "sha256": { "type": "string", "pattern": "^[\\da-f]{64}$" } + } + }, + "phaseId": { + "type": "string", + "enum": [ + "understand", + "research", + "analyze", + "plan", + "risk-assessment", + "implement", + "verify", + "review", + "improve", + "document", + "update-memory", + "finished" + ] + }, + "executionBatch": { + "type": "string", + "enum": ["discovery", "design", "change", "assurance", "closure"] + }, + "phaseStatus": { + "type": "string", + "enum": [ + "pending", + "ready", + "running", + "passed", + "failed", + "invalidated", + "rolled-back", + "waiting", + "terminal" + ] + } + } +} diff --git a/tests/contracts/cas-contracts/v1.1.1/manifest.json b/tests/contracts/cas-contracts/v1.1.1/manifest.json new file mode 100644 index 0000000..50937d9 --- /dev/null +++ b/tests/contracts/cas-contracts/v1.1.1/manifest.json @@ -0,0 +1,35 @@ +{ + "version": "1.1.1", + "schemas": [ + { + "id": "https://schemas.coding-autopilot.dev/v1.1/common.schema.json", + "path": "common.schema.json", + "sha256": "e3173585d5e04eb2f4455de7d821903a45cf2d8edff3b33bbc81f43e97b2aa82" + }, + { + "id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-request.schema.json", + "path": "phase-execution-request.schema.json", + "sha256": "6ca1e9cc51877d412e059546722d0b82654c7f925fce1616f4961ebae1142cbb" + }, + { + "id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-result.schema.json", + "path": "phase-execution-result.schema.json", + "sha256": "d756f56a484972b379f520b0bd38bbcb07d948a884d4f373939867700eb03e5b" + }, + { + "id": "https://schemas.coding-autopilot.dev/v1.1/phase-verification-result.schema.json", + "path": "phase-verification-result.schema.json", + "sha256": "704b05f99d67323eddf4599536a545dbb50412654248aa58abad0af5dae25b3c" + }, + { + "id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-lifecycle-event.schema.json", + "path": "sdlc-lifecycle-event.schema.json", + "sha256": "6f0bc68f7b74966bb1f654250463291b19398cc80d73a9bbae5aaba8ca1d226a" + }, + { + "id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-profile.schema.json", + "path": "sdlc-profile.schema.json", + "sha256": "c9a643302f8762848ab1b5063ac07d344fb30f2904fc162fb55ae2f4552b6904" + } + ] +} diff --git a/tests/contracts/cas-contracts/v1.1.1/phase-execution-request.schema.json b/tests/contracts/cas-contracts/v1.1.1/phase-execution-request.schema.json new file mode 100644 index 0000000..e7ad5a8 --- /dev/null +++ b/tests/contracts/cas-contracts/v1.1.1/phase-execution-request.schema.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-request.schema.json", + "title": "PhaseExecutionRequest", + "type": "object", + "allOf": [ + { "$ref": "common.schema.json#/$defs/lifecycleMetadata" }, + { + "type": "object", + "required": ["kind", "profileVersion", "phase", "batch", "goal", "constraints"], + "properties": { + "kind": { "const": "PhaseExecutionRequest" }, + "profileVersion": { "type": "string", "const": "v1.1" }, + "phase": { "$ref": "common.schema.json#/$defs/phaseId" }, + "batch": { "$ref": "common.schema.json#/$defs/executionBatch" }, + "goal": { "type": "string", "minLength": 1, "maxLength": 5000 }, + "constraints": { + "type": "array", + "items": { "type": "string", "minLength": 1, "maxLength": 1000 } + }, + "validatedInputs": { "type": "array", "items": { "type": "string" } }, + "priorEvidence": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidence" } }, + "requiredOutputSchema": { "type": "string", "minLength": 1, "maxLength": 256 }, + "requiredArtifacts": { "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 256 } }, + "successCriteria": { "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 1000 } }, + "verifier": { "type": "string", "minLength": 1, "maxLength": 256 }, + "failureBehavior": { "type": "string", "minLength": 1, "maxLength": 5000 }, + "rollbackBehavior": { "type": "string", "minLength": 1, "maxLength": 5000 }, + "memoryCandidateFields": { "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 256 } }, + "humanEscalationConditions": { "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 1000 } } + } + } + ], + "unevaluatedProperties": false +} diff --git a/tests/contracts/cas-contracts/v1.1.1/phase-execution-result.schema.json b/tests/contracts/cas-contracts/v1.1.1/phase-execution-result.schema.json new file mode 100644 index 0000000..5aa2623 --- /dev/null +++ b/tests/contracts/cas-contracts/v1.1.1/phase-execution-result.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.coding-autopilot.dev/v1.1/phase-execution-result.schema.json", + "title": "PhaseExecutionResult", + "type": "object", + "allOf": [ + { "$ref": "common.schema.json#/$defs/lifecycleMetadata" }, + { + "type": "object", + "required": ["kind", "phase", "status", "promptDigest"], + "properties": { + "kind": { "const": "PhaseExecutionResult" }, + "phase": { "$ref": "common.schema.json#/$defs/phaseId" }, + "batch": { "$ref": "common.schema.json#/$defs/executionBatch" }, + "status": { "$ref": "common.schema.json#/$defs/phaseStatus" }, + "promptDigest": { "type": "string", "pattern": "^[\\da-f]{64}$" }, + "artifacts": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidence" } }, + "output": { "type": "object" }, + "promptMetadata": { "type": "object" }, + "sanitizedPrompt": { "type": "string", "maxLength": 50000 }, + "verifier": { "type": "string", "minLength": 1, "maxLength": 256 }, + "checkpoint": { "type": "string", "minLength": 1, "maxLength": 256 } + } + } + ], + "unevaluatedProperties": false +} diff --git a/tests/contracts/cas-contracts/v1.1.1/phase-verification-result.schema.json b/tests/contracts/cas-contracts/v1.1.1/phase-verification-result.schema.json new file mode 100644 index 0000000..083d947 --- /dev/null +++ b/tests/contracts/cas-contracts/v1.1.1/phase-verification-result.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.coding-autopilot.dev/v1.1/phase-verification-result.schema.json", + "title": "PhaseVerificationResult", + "type": "object", + "allOf": [ + { "$ref": "common.schema.json#/$defs/lifecycleMetadata" }, + { + "type": "object", + "required": ["kind", "phase", "verifier", "outcome"], + "properties": { + "kind": { "const": "PhaseVerificationResult" }, + "phase": { "$ref": "common.schema.json#/$defs/phaseId" }, + "verifier": { "type": "string", "minLength": 1, "maxLength": 256 }, + "outcome": { "enum": ["passed", "failed", "inconclusive"] }, + "invalidatedPhaseIds": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/phaseId" }, + "uniqueItems": true + }, + "evidence": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidence" } }, + "reason": { "type": "string", "maxLength": 5000 } + } + } + ], + "unevaluatedProperties": false +} diff --git a/tests/contracts/cas-contracts/v1.1.1/sdlc-lifecycle-event.schema.json b/tests/contracts/cas-contracts/v1.1.1/sdlc-lifecycle-event.schema.json new file mode 100644 index 0000000..b062c81 --- /dev/null +++ b/tests/contracts/cas-contracts/v1.1.1/sdlc-lifecycle-event.schema.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-lifecycle-event.schema.json", + "title": "SdlcLifecycleEvent", + "type": "object", + "allOf": [ + { "$ref": "common.schema.json#/$defs/lifecycleMetadata" }, + { + "type": "object", + "required": ["kind", "eventType", "phase"], + "properties": { + "kind": { "const": "SdlcLifecycleEvent" }, + "eventType": { "type": "string", "minLength": 1, "maxLength": 128 }, + "phase": { "$ref": "common.schema.json#/$defs/phaseId" }, + "batch": { "$ref": "common.schema.json#/$defs/executionBatch" }, + "status": { "$ref": "common.schema.json#/$defs/phaseStatus" }, + "message": { "type": "string", "maxLength": 5000 }, + "evidence": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidence" } } + } + } + ], + "unevaluatedProperties": false +} diff --git a/tests/contracts/cas-contracts/v1.1.1/sdlc-profile.schema.json b/tests/contracts/cas-contracts/v1.1.1/sdlc-profile.schema.json new file mode 100644 index 0000000..d9dbd62 --- /dev/null +++ b/tests/contracts/cas-contracts/v1.1.1/sdlc-profile.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.coding-autopilot.dev/v1.1/sdlc-profile.schema.json", + "title": "SdlcProfile", + "type": "object", + "allOf": [ + { "$ref": "common.schema.json#/$defs/lifecycleMetadata" }, + { + "type": "object", + "required": ["kind", "profileId", "profileVersion", "phases", "goalBudget"], + "properties": { + "kind": { "const": "SdlcProfile" }, + "profileId": { "type": "string", "minLength": 1, "maxLength": 128 }, + "profileVersion": { "type": "string", "pattern": "^v1\\.1$" }, + "goalBudget": { "type": "integer", "minimum": 1 }, + "phases": { + "type": "array", + "minItems": 12, + "maxItems": 12, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["id", "name", "batch", "dependencies", "verifier"], + "properties": { + "id": { "$ref": "common.schema.json#/$defs/phaseId" }, + "name": { "type": "string", "minLength": 1, "maxLength": 128 }, + "batch": { "$ref": "common.schema.json#/$defs/executionBatch" }, + "dependencies": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/phaseId" }, + "uniqueItems": true + }, + "verifier": { "type": "string", "minLength": 1, "maxLength": 256 }, + "requiredArtifacts": { + "type": "array", + "items": { "type": "string", "minLength": 1, "maxLength": 256 } + }, + "successCriteria": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1, "maxLength": 1000 } + }, + "rollbackTo": { "$ref": "common.schema.json#/$defs/phaseId" }, + "maxAttempts": { "type": "integer", "minimum": 1 } + } + } + }, + "goalAttempts": { "type": "integer", "minimum": 1 }, + "iterations": { "type": "integer", "minimum": 1 }, + "runtimeMinutes": { "type": "integer", "minimum": 1 }, + "modelCalls": { "type": "integer", "minimum": 1 }, + "noProgressLimit": { "type": "integer", "minimum": 1 }, + "repositoryOverrides": { + "type": "object", + "additionalProperties": false, + "properties": { + "promptFragments": { "type": "object" }, + "verifierCommands": { "type": "object" }, + "requiredArtifacts": { "type": "object" }, + "phaseLimits": { "type": "object" } + } + } + } + } + ], + "unevaluatedProperties": false +} diff --git a/tests/test_contract_compatibility.py b/tests/test_contract_compatibility.py index 306acab..85f1794 100644 --- a/tests/test_contract_compatibility.py +++ b/tests/test_contract_compatibility.py @@ -14,8 +14,8 @@ validating against the pinned schema. Cross-repo reference note: cas-contracts is a *separate* repo. In this local polyrepo -CI checks out only this repo, so we vendor the pinned ``v1.1.0`` release under -``tests/contracts/cas-contracts/v1.1.0/`` (same convention as cas-reference-product). +CI checks out only this repo, so we vendor the pinned ``v1.1.1`` release under +``tests/contracts/cas-contracts/v1.1.1/`` (same convention as cas-reference-product). When the sibling ``../../cas-contracts`` checkout is present (local dev), we additionally assert the vendored copy has not drifted from the upstream source of truth. """ @@ -37,16 +37,17 @@ # The contract version autogen's models pin themselves to. These MUST stay in lockstep # with autogen_dashboard/schemas.py (SdlcProfileModel.profile_version, the various # kind/profile_version literals) and with the vendored release below. +PINNED_RELEASE_VERSION = "1.1.1" PINNED_SCHEMA_VERSION = "1.1.0" PINNED_PROFILE_VERSION = "v1.1" -CONTRACT_ROOT = Path(__file__).parent / "contracts" / "cas-contracts" / f"v{PINNED_SCHEMA_VERSION}" +CONTRACT_ROOT = Path(__file__).parent / "contracts" / "cas-contracts" / f"v{PINNED_RELEASE_VERSION}" UPSTREAM_ROOT = ( Path(__file__).resolve().parents[2] / "cas-contracts" / "registry" / "releases" - / f"v{PINNED_SCHEMA_VERSION}" + / f"v{PINNED_RELEASE_VERSION}" ) @@ -98,7 +99,7 @@ def _lifecycle() -> dict[str, Any]: def test_vendored_release_matches_manifest_hashes() -> None: """The vendored pinned release must be internally consistent (tamper check).""" manifest = _load_json(CONTRACT_ROOT / "manifest.json") - assert manifest["version"] == PINNED_SCHEMA_VERSION + assert manifest["version"] == PINNED_RELEASE_VERSION for entry in manifest["schemas"]: content = (CONTRACT_ROOT / entry["path"]).read_bytes() assert hashlib.sha256(content).hexdigest() == entry["sha256"], entry["path"] @@ -207,4 +208,4 @@ def test_vendored_release_matches_upstream_source_of_truth() -> None: assert ( hashlib.sha256(path.read_bytes()).hexdigest() == hashlib.sha256(upstream.read_bytes()).hexdigest() - ), f"vendored {path.name} drifted from upstream cas-contracts {PINNED_SCHEMA_VERSION}" + ), f"vendored {path.name} drifted from upstream cas-contracts {PINNED_RELEASE_VERSION}"