From 52ecca8ec198725592a16e9a845ded70403e857d Mon Sep 17 00:00:00 2001 From: Daniel Strader Date: Sat, 1 Aug 2026 19:13:04 -0700 Subject: [PATCH 1/6] ci: enforce srdcheck result conformance --- .github/workflows/ci.yml | 34 ++++++++ docs/CONTRACTS.md | 17 ++-- scripts/check_srdcheck_conformance.py | 116 ++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 scripts/check_srdcheck_conformance.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 968e0f6..bfd324d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,3 +120,37 @@ jobs: "$GITHUB_WORKSPACE/table-kit-src/scripts/check_dmcheck_conformance.py" \ --tablekit "$RUNNER_TEMP/portfolio-conformance/bin/tablekit" \ --dmcheck "$RUNNER_TEMP/portfolio-conformance/bin/dmcheck" + + srdcheck-conformance: + runs-on: ubuntu-latest + steps: + - name: Check out table-kit candidate + uses: actions/checkout@v5 + with: + path: table-kit-src + - name: Check out pinned srdcheck candidate + uses: actions/checkout@v5 + with: + repository: chaoz23/srdcheck + ref: 94f87fc54b011ce9048a13aefa96f05d8708711a + path: srdcheck-src + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + - name: Build both candidate wheels + run: | + python -m pip install build + mkdir wheels + python -m build --wheel --outdir "$GITHUB_WORKSPACE/wheels" table-kit-src + python -m build --wheel --outdir "$GITHUB_WORKSPACE/wheels" srdcheck-src + - name: Cold-install and run srdcheck conformance + run: | + python -m venv "$RUNNER_TEMP/srdcheck-conformance" + "$RUNNER_TEMP/srdcheck-conformance/bin/python" -m pip install jsonschema + "$RUNNER_TEMP/srdcheck-conformance/bin/python" -m pip install \ + --no-index --find-links "$GITHUB_WORKSPACE/wheels" tablekit srdcheck + cd "$RUNNER_TEMP" + "$RUNNER_TEMP/srdcheck-conformance/bin/python" \ + "$GITHUB_WORKSPACE/table-kit-src/scripts/check_srdcheck_conformance.py" \ + --tablekit "$RUNNER_TEMP/srdcheck-conformance/bin/tablekit" \ + --srdcheck "$RUNNER_TEMP/srdcheck-conformance/bin/srdcheck" diff --git a/docs/CONTRACTS.md b/docs/CONTRACTS.md index cbba1ac..55ffee9 100644 --- a/docs/CONTRACTS.md +++ b/docs/CONTRACTS.md @@ -68,10 +68,13 @@ schema enums with `python scripts/generate_contract_types.py`; CI rejects stale artifacts. Runtime remains dependency-free. JSON Schema validation is a test and integration concern. -CI also builds and cold-installs this candidate with the exact dmcheck commit -named in the workflow, outside either source checkout. The gate exercises a -complete clean result, an exact-evidence finding, the canonical transport gap, -deterministic legacy migration, and redacted content. It validates both event -and evaluation envelopes with the installed table-kit schema and semantic -checks. The pinned commit is an integration lock, not a release dependency; -advance it deliberately when the dmcheck contract consumer changes. +CI also builds and cold-installs this candidate with the exact dmcheck and +srdcheck commits named in the workflow, outside their source checkouts. The +dmcheck gate exercises a complete clean result, an exact-evidence finding, the +canonical transport gap, deterministic legacy migration, and redacted content. +The srdcheck gate exercises successful and failed-but-adjudicated saves, an +exact-evidence illegal action, missing facts, unsupported content, and invalid +input. Both validate installed evaluation envelopes with table-kit's schema and +semantic checks; dmcheck also validates events. Pinned commits are integration +locks, not release dependencies; advance them deliberately when a consumer +changes. diff --git a/scripts/check_srdcheck_conformance.py b/scripts/check_srdcheck_conformance.py new file mode 100644 index 0000000..7852463 --- /dev/null +++ b/scripts/check_srdcheck_conformance.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Cold-installed table-kit -> srdcheck result conformance gate.""" + +import argparse +import json +from pathlib import Path +import subprocess +import sys +import tempfile + +from jsonschema import Draft202012Validator, FormatChecker + +import srdcheck +import tablekit +from tablekit import contracts + + +def _run(command, expected, cwd): + result = subprocess.run(command, cwd=cwd, capture_output=True, text=True) + if result.returncode != expected: + raise AssertionError( + "%s exited %d, expected %d\nstdout:\n%s\nstderr:\n%s" % ( + " ".join(command), result.returncode, expected, + result.stdout, result.stderr)) + return json.loads(result.stdout) + + +def _assert_cold_install(module): + module_path = Path(module.__file__).resolve() + prefix = Path(sys.prefix).resolve() + if prefix not in module_path.parents: + raise AssertionError("%s was imported outside the test environment: %s" % ( + module.__name__, module_path)) + + +def _query(command, validator, root, query_type, params, expected, status): + value = _run([ + command, "query", query_type, + json.dumps(params, sort_keys=True, separators=(",", ":")), + "--table-evaluation", + ], expected, root) + validator.validate(value) + contracts.require_evaluation_semantics(value) + assert value["status"] == status + assert value["exit_code"] == expected + assert value["authority_status"] == "self_attested" + assert value["subject"]["kind"] == "rules_query" + assert value["context"]["policy_digest"].startswith("sha256:") + return value + + +def main(argv=None): + parser = argparse.ArgumentParser() + parser.add_argument("--tablekit", default="tablekit") + parser.add_argument("--srdcheck", default="srdcheck") + args = parser.parse_args(argv) + + _assert_cold_install(tablekit) + _assert_cold_install(srdcheck) + schema = _run([args.tablekit, "contract", "evaluation"], 0, None) + validator = Draft202012Validator(schema, format_checker=FormatChecker()) + + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + save = {"modifier": 2, "dc": 10, "d20_result": 12, + "save_ability": "dex", "saver_conditions": []} + clean = _query( + args.srdcheck, validator, root, "save.check", save, + 0, "checked_clean") + assert clean["coverage"]["complete"] is True + + # A failed saving throw is still a completely adjudicated rules query; + # it is not an illegal action or a portfolio finding. + failed_save = dict(save, d20_result=2) + resolved_failure = _query( + args.srdcheck, validator, root, "save.check", failed_save, + 0, "checked_clean") + assert resolved_failure["findings"] == [] + + illegal = _query( + args.srdcheck, validator, root, "mage-hand.use", + {"kind": "attack"}, 1, "findings") + assert "mage-hand.cant-attack" in illegal["findings"][0]["evidence_refs"] + repeated = _query( + args.srdcheck, validator, root, "mage-hand.use", + {"kind": "attack"}, 1, "findings") + assert repeated == illegal + + missing = _query( + args.srdcheck, validator, root, "save.check", {"modifier": 2}, + 2, "incomplete") + assert missing["errors"][0]["code"] == "srdcheck.missing_fact" + + unsupported = _query( + args.srdcheck, validator, root, "spell.facts", + {"name": "Hexblade"}, 2, "unsupported") + assert unsupported["errors"][0]["code"] == \ + "srdcheck.unsupported_content" + + invalid = _query( + args.srdcheck, validator, root, "save.check", + {"modifier": 2, "dc": 10, "bogus": 1}, 2, "invalid") + assert invalid["errors"][0]["code"] == "srdcheck.invalid_input" + + print(json.dumps({ + "status": "passed", "authority_status": "self_attested", + "tablekit_version": tablekit.__version__, + "srdcheck_version": srdcheck.__version__, + "cases": ["checked_clean", "resolved_failed_save", "findings", + "missing_fact", "unsupported_content", "invalid_input"], + }, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From f41a98b758ba5df210025ca62354b14872fe2784 Mon Sep 17 00:00:00 2001 From: Daniel Strader Date: Sat, 1 Aug 2026 19:18:31 -0700 Subject: [PATCH 2/6] ci: follow rebased srdcheck projection --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfd324d..63e8ab4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,7 +132,7 @@ jobs: uses: actions/checkout@v5 with: repository: chaoz23/srdcheck - ref: 94f87fc54b011ce9048a13aefa96f05d8708711a + ref: 28f765572105a40a5ba862c27b0286d4982dde59 path: srdcheck-src - uses: actions/setup-python@v6 with: From de43ca293393dc4274c20689a3814eae2110b1b0 Mon Sep 17 00:00:00 2001 From: Daniel Strader Date: Sat, 1 Aug 2026 19:21:32 -0700 Subject: [PATCH 3/6] ci: follow current srdcheck main --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63e8ab4..e79a12c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,7 +132,7 @@ jobs: uses: actions/checkout@v5 with: repository: chaoz23/srdcheck - ref: 28f765572105a40a5ba862c27b0286d4982dde59 + ref: 21a43d8c2a6791465be9e0fb9856ce05888292d0 path: srdcheck-src - uses: actions/setup-python@v6 with: From 7bd6cfa118ca5768ca098dcf77be05c20f750d13 Mon Sep 17 00:00:00 2001 From: Daniel Strader Date: Sat, 1 Aug 2026 19:50:28 -0700 Subject: [PATCH 4/6] ci: advance pinned srdcheck projection --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e79a12c..6f9def9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,7 +132,7 @@ jobs: uses: actions/checkout@v5 with: repository: chaoz23/srdcheck - ref: 21a43d8c2a6791465be9e0fb9856ce05888292d0 + ref: 4bb87ee31f69f0dd70f6ceb176392c97b3147f33 path: srdcheck-src - uses: actions/setup-python@v6 with: From 427ab98d38f62b7c8c2ac8572d69c85f8dc2232e Mon Sep 17 00:00:00 2001 From: Daniel Strader Date: Sun, 2 Aug 2026 20:16:37 -0700 Subject: [PATCH 5/6] ci: pin merged srdcheck projection --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f9def9..c510165 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,7 +132,7 @@ jobs: uses: actions/checkout@v5 with: repository: chaoz23/srdcheck - ref: 4bb87ee31f69f0dd70f6ceb176392c97b3147f33 + ref: 236188326fc7a1210f1d3aff3a1fc7c3ccd7bec8 path: srdcheck-src - uses: actions/setup-python@v6 with: From 17e847cb62213c660c00c807b65d98455d807733 Mon Sep 17 00:00:00 2001 From: Daniel Strader Date: Mon, 3 Aug 2026 07:48:39 -0700 Subject: [PATCH 6/6] ci: rerun after stack restack