szl-substrate 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. szl_substrate-0.1.0/.github/CODEOWNERS +2 -0
  2. szl_substrate-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +57 -0
  3. szl_substrate-0.1.0/.github/ISSUE_TEMPLATE/config.yml +11 -0
  4. szl_substrate-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +17 -0
  5. szl_substrate-0.1.0/.github/dependabot.yml +50 -0
  6. szl_substrate-0.1.0/.github/scripts/szl_pypi_release_gate.py +227 -0
  7. szl_substrate-0.1.0/.github/workflows/base-python-ci.yml +67 -0
  8. szl_substrate-0.1.0/.github/workflows/ci.yml +45 -0
  9. szl_substrate-0.1.0/.github/workflows/codeql.yml +53 -0
  10. szl_substrate-0.1.0/.github/workflows/commit-lint.yml +43 -0
  11. szl_substrate-0.1.0/.github/workflows/doctrine.yml +12 -0
  12. szl_substrate-0.1.0/.github/workflows/forbidden-domain.yml +39 -0
  13. szl_substrate-0.1.0/.github/workflows/gitleaks.yml +81 -0
  14. szl_substrate-0.1.0/.github/workflows/pin-check.yml +20 -0
  15. szl_substrate-0.1.0/.github/workflows/publish-pypi.yml +129 -0
  16. szl_substrate-0.1.0/.github/workflows/scorecard.yml +24 -0
  17. szl_substrate-0.1.0/.gitignore +7 -0
  18. szl_substrate-0.1.0/.gitleaks.toml +74 -0
  19. szl_substrate-0.1.0/CITATION.cff +27 -0
  20. szl_substrate-0.1.0/CODE_OF_CONDUCT.md +36 -0
  21. szl_substrate-0.1.0/CONTRIBUTING.md +35 -0
  22. szl_substrate-0.1.0/LICENSE +201 -0
  23. szl_substrate-0.1.0/MIGRATION.md +490 -0
  24. szl_substrate-0.1.0/NOTICE +27 -0
  25. szl_substrate-0.1.0/NOTICE-THIRD-PARTY.md +174 -0
  26. szl_substrate-0.1.0/PKG-INFO +331 -0
  27. szl_substrate-0.1.0/README.md +277 -0
  28. szl_substrate-0.1.0/SECURITY.md +39 -0
  29. szl_substrate-0.1.0/examples/guarded_import_pattern.py +44 -0
  30. szl_substrate-0.1.0/pyproject.toml +97 -0
  31. szl_substrate-0.1.0/requirements/ci/base.in +15 -0
  32. szl_substrate-0.1.0/requirements/ci/base.txt +320 -0
  33. szl_substrate-0.1.0/requirements/ci/test.in +16 -0
  34. szl_substrate-0.1.0/requirements/ci/test.txt +519 -0
  35. szl_substrate-0.1.0/src/szl_substrate/__init__.py +378 -0
  36. szl_substrate-0.1.0/src/szl_substrate/_vendor_blobs.py +79 -0
  37. szl_substrate-0.1.0/src/szl_substrate/a11oy_agent_loop.py +844 -0
  38. szl_substrate-0.1.0/src/szl_substrate/a11oy_autoreview.py +1149 -0
  39. szl_substrate-0.1.0/src/szl_substrate/a11oy_code_engine.py +1457 -0
  40. szl_substrate-0.1.0/src/szl_substrate/a11oy_hf_assets.py +178 -0
  41. szl_substrate-0.1.0/src/szl_substrate/a11oy_mcp_client.py +276 -0
  42. szl_substrate-0.1.0/src/szl_substrate/a11oy_org_rag.py +3168 -0
  43. szl_substrate-0.1.0/src/szl_substrate/a11oy_uds_portability_nav.py +168 -0
  44. szl_substrate-0.1.0/src/szl_substrate/a11oy_waqay_nav.py +175 -0
  45. szl_substrate-0.1.0/src/szl_substrate/a11oy_yupay_nav.py +177 -0
  46. szl_substrate-0.1.0/src/szl_substrate/operator_shell_v4.py +569 -0
  47. szl_substrate-0.1.0/src/szl_substrate/szl_agentic_loop.py +1830 -0
  48. szl_substrate-0.1.0/src/szl_substrate/szl_allodial.py +339 -0
  49. szl_substrate-0.1.0/src/szl_substrate/szl_alloy_models.py +831 -0
  50. szl_substrate-0.1.0/src/szl_substrate/szl_anatomy_routes.py +356 -0
  51. szl_substrate-0.1.0/src/szl_substrate/szl_be_hardening.py +958 -0
  52. szl_substrate-0.1.0/src/szl_substrate/szl_brain.py +249 -0
  53. szl_substrate-0.1.0/src/szl_substrate/szl_calibration.py +329 -0
  54. szl_substrate-0.1.0/src/szl_substrate/szl_chain_of_title.py +213 -0
  55. szl_substrate-0.1.0/src/szl_substrate/szl_codename_gate.py +152 -0
  56. szl_substrate-0.1.0/src/szl_substrate/szl_conformal.py +735 -0
  57. szl_substrate-0.1.0/src/szl_substrate/szl_conjecture_factory.py +361 -0
  58. szl_substrate-0.1.0/src/szl_substrate/szl_connector_mcp.py +118 -0
  59. szl_substrate-0.1.0/src/szl_substrate/szl_connectors_serve.py +145 -0
  60. szl_substrate-0.1.0/src/szl_substrate/szl_contracting.py +736 -0
  61. szl_substrate-0.1.0/src/szl_substrate/szl_cuas_formulas.py +939 -0
  62. szl_substrate-0.1.0/src/szl_substrate/szl_deepdive_gaps.py +592 -0
  63. szl_substrate-0.1.0/src/szl_substrate/szl_dsse.py +332 -0
  64. szl_substrate-0.1.0/src/szl_substrate/szl_ecosystem_routes.py +488 -0
  65. szl_substrate-0.1.0/src/szl_substrate/szl_energy_sovereign.py +1156 -0
  66. szl_substrate-0.1.0/src/szl_substrate/szl_entanglement.py +394 -0
  67. szl_substrate-0.1.0/src/szl_substrate/szl_evidence_research.py +913 -0
  68. szl_substrate-0.1.0/src/szl_substrate/szl_formula_wiring.py +1428 -0
  69. szl_substrate-0.1.0/src/szl_substrate/szl_formulas.py +1206 -0
  70. szl_substrate-0.1.0/src/szl_substrate/szl_hf_bucket.py +726 -0
  71. szl_substrate-0.1.0/src/szl_substrate/szl_joules_truth.py +145 -0
  72. szl_substrate-0.1.0/src/szl_substrate/szl_ken.py +537 -0
  73. szl_substrate-0.1.0/src/szl_substrate/szl_khipu_consensus.py +611 -0
  74. szl_substrate-0.1.0/src/szl_substrate/szl_khipu_lmdb.py +241 -0
  75. szl_substrate-0.1.0/src/szl_substrate/szl_khipu_replicate.py +150 -0
  76. szl_substrate-0.1.0/src/szl_substrate/szl_live_wires.py +389 -0
  77. szl_substrate-0.1.0/src/szl_substrate/szl_llm_registry.py +690 -0
  78. szl_substrate-0.1.0/src/szl_substrate/szl_logging.py +45 -0
  79. szl_substrate-0.1.0/src/szl_substrate/szl_mbse_cosim.py +692 -0
  80. szl_substrate-0.1.0/src/szl_substrate/szl_mbse_nav.py +450 -0
  81. szl_substrate-0.1.0/src/szl_substrate/szl_metrics_prom.py +261 -0
  82. szl_substrate-0.1.0/src/szl_substrate/szl_neuroplasticity.py +267 -0
  83. szl_substrate-0.1.0/src/szl_substrate/szl_provenance.py +440 -0
  84. szl_substrate-0.1.0/src/szl_substrate/szl_qhawaq.py +982 -0
  85. szl_substrate-0.1.0/src/szl_substrate/szl_quantum_bio.py +252 -0
  86. szl_substrate-0.1.0/src/szl_substrate/szl_rag.py +564 -0
  87. szl_substrate-0.1.0/src/szl_substrate/szl_readiness.py +974 -0
  88. szl_substrate-0.1.0/src/szl_substrate/szl_restraint.py +655 -0
  89. szl_substrate-0.1.0/src/szl_substrate/szl_rosie_companion.py +485 -0
  90. szl_substrate-0.1.0/src/szl_substrate/szl_sapa.py +463 -0
  91. szl_substrate-0.1.0/src/szl_substrate/szl_sapa_patch.py +308 -0
  92. szl_substrate-0.1.0/src/szl_substrate/szl_scaling.py +374 -0
  93. szl_substrate-0.1.0/src/szl_substrate/szl_spaces_proxy.py +444 -0
  94. szl_substrate-0.1.0/src/szl_substrate/szl_spaces_surface.py +525 -0
  95. szl_substrate-0.1.0/src/szl_substrate/szl_uds_portability.py +770 -0
  96. szl_substrate-0.1.0/src/szl_substrate/szl_unay.py +443 -0
  97. szl_substrate-0.1.0/src/szl_substrate/szl_unay_routes.py +237 -0
  98. szl_substrate-0.1.0/src/szl_substrate/szl_unified_formulas.py +213 -0
  99. szl_substrate-0.1.0/src/szl_substrate/szl_v4_fleet.py +359 -0
  100. szl_substrate-0.1.0/src/szl_substrate/szl_waqay.py +1068 -0
  101. szl_substrate-0.1.0/src/szl_substrate/szl_warhacker_aliases.py +252 -0
  102. szl_substrate-0.1.0/src/szl_substrate/szl_yupay.py +911 -0
  103. szl_substrate-0.1.0/src/szl_substrate/test_szl_hf_bucket.py +216 -0
  104. szl_substrate-0.1.0/tests/__init__.py +0 -0
  105. szl_substrate-0.1.0/tests/test_agentic_loop_verifier_fail_closed.py +95 -0
  106. szl_substrate-0.1.0/tests/test_anatomy_routes_sources.py +57 -0
  107. szl_substrate-0.1.0/tests/test_app_parity.py +55 -0
  108. szl_substrate-0.1.0/tests/test_brain.py +41 -0
  109. szl_substrate-0.1.0/tests/test_calibration.py +50 -0
  110. szl_substrate-0.1.0/tests/test_calibration_validation.py +163 -0
  111. szl_substrate-0.1.0/tests/test_ci_requirements.py +89 -0
  112. szl_substrate-0.1.0/tests/test_code_engine_mandatory_limits.py +507 -0
  113. szl_substrate-0.1.0/tests/test_code_engine_portability.py +78 -0
  114. szl_substrate-0.1.0/tests/test_conformal_score_validation.py +50 -0
  115. szl_substrate-0.1.0/tests/test_dependency_contract.py +328 -0
  116. szl_substrate-0.1.0/tests/test_dsse.py +167 -0
  117. szl_substrate-0.1.0/tests/test_dsse_finite_payloads.py +71 -0
  118. szl_substrate-0.1.0/tests/test_exception_text_not_echoed.py +127 -0
  119. szl_substrate-0.1.0/tests/test_mtier_reconcile.py +95 -0
  120. szl_substrate-0.1.0/tests/test_mtier_wave1.py +66 -0
  121. szl_substrate-0.1.0/tests/test_mtier_wave2.py +68 -0
  122. szl_substrate-0.1.0/tests/test_mtier_wave3.py +72 -0
  123. szl_substrate-0.1.0/tests/test_mtier_wave4.py +89 -0
  124. szl_substrate-0.1.0/tests/test_org_rag_lexical_reference.py +35 -0
  125. szl_substrate-0.1.0/tests/test_readiness_parity.py +96 -0
  126. szl_substrate-0.1.0/tests/test_shipped_selftests.py +25 -0
  127. szl_substrate-0.1.0/tests/test_vendor_blob_notices.py +35 -0
  128. szl_substrate-0.1.0/tests/test_wave_s_batch.py +56 -0
  129. szl_substrate-0.1.0/tests/test_wavek_finish.py +115 -0
  130. szl_substrate-0.1.0/tools/app_parity.py +120 -0
@@ -0,0 +1,2 @@
1
+ # Default code owner (mirrors szl-holdings/.github). Per-path owners can be added below.
2
+ * @stephenlutar2-hash
@@ -0,0 +1,57 @@
1
+ # Standard bug report form for the szl-holdings estate.
2
+ # Installed by the szl-alignment engine; keep in sync with
3
+ # alignment/templates/ISSUE_TEMPLATE/bug_report.yml.
4
+ name: Bug report
5
+ description: File a reproducible, receipt-backed bug report
6
+ title: "[bug]: "
7
+ labels: ["bug", "triage"]
8
+ body:
9
+ - type: markdown
10
+ attributes:
11
+ value: |
12
+ Thanks for the report. Control before action, evidence after:
13
+ a bug is actionable when it is reproducible and receipt-backed.
14
+ - type: input
15
+ id: version
16
+ attributes:
17
+ label: Version / commit
18
+ description: What were you running? Tag, branch, or commit SHA.
19
+ placeholder: e.g. v14.2.0 or 1a2b3c4
20
+ validations:
21
+ required: true
22
+ - type: textarea
23
+ id: what-happened
24
+ attributes:
25
+ label: What happened?
26
+ description: Observed behavior, including the exact error text.
27
+ validations:
28
+ required: true
29
+ - type: textarea
30
+ id: expected
31
+ attributes:
32
+ label: Expected behavior
33
+ description: What should have happened instead?
34
+ validations:
35
+ required: true
36
+ - type: textarea
37
+ id: repro
38
+ attributes:
39
+ label: Minimal reproduction
40
+ description: Steps, commands, or a minimal repo case.
41
+ placeholder: "1. ...\n2. ...\n3. ..."
42
+ validations:
43
+ required: true
44
+ - type: textarea
45
+ id: receipts
46
+ attributes:
47
+ label: Receipts
48
+ description: Logs, CI links, screenshots. If unknown, say UNKNOWN — never guess.
49
+ - type: checkboxes
50
+ id: checks
51
+ attributes:
52
+ label: Pre-flight checks
53
+ options:
54
+ - label: I searched existing issues for duplicates
55
+ required: true
56
+ - label: This is not a security vulnerability (see SECURITY.md for private disclosure)
57
+ required: true
@@ -0,0 +1,11 @@
1
+ # Issue-template routing for the szl-holdings estate.
2
+ # Installed by the szl-alignment engine; keep in sync with
3
+ # alignment/templates/ISSUE_TEMPLATE/config.yml.
4
+ blank_issues_enabled: false
5
+ contact_links:
6
+ - name: Security vulnerability
7
+ url: https://github.com/szl-holdings
8
+ about: Report vulnerabilities privately via GitHub Security Advisories — see SECURITY.md. Never file public issues for undisclosed vulnerabilities.
9
+ - name: Org-wide questions
10
+ url: https://github.com/szl-holdings
11
+ about: Questions about the estate, doctrine, or alignment — start with the org profile.
@@ -0,0 +1,17 @@
1
+ ## What
2
+
3
+ <!-- What changed, and why. One paragraph, evidence-linked. -->
4
+
5
+ ## Receipts
6
+
7
+ <!-- CI run links, test output, screenshots, audit artifacts.
8
+ UNKNOWN is never claimed as PASS. -->
9
+
10
+ ## Checklist
11
+
12
+ - [ ] Control before action: the invariant (test / policy / assertion) landed before or with the behavior change
13
+ - [ ] Evidence after: receipts updated and linked above
14
+ - [ ] UNKNOWN is not claimed as PASS anywhere in this PR
15
+ - [ ] No new a11oy.com references — the forbidden domain must never appear (gate: `forbidden-domain.yml`)
16
+ - [ ] Docs updated (README / CHANGELOG) if behavior or surfaces changed
17
+ - [ ] Commits are conventional and signed off (`Signed-off-by:`)
@@ -0,0 +1,50 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ day: "monday"
8
+ time: "08:00"
9
+ timezone: "America/New_York"
10
+ labels:
11
+ - "dependencies"
12
+ open-pull-requests-limit: 5
13
+ groups:
14
+ github_actions:
15
+ patterns:
16
+ - "*"
17
+
18
+ - package-ecosystem: "pip"
19
+ directory: "/"
20
+ schedule:
21
+ interval: "weekly"
22
+ day: "monday"
23
+ time: "08:00"
24
+ timezone: "America/New_York"
25
+ labels:
26
+ - "dependencies"
27
+ open-pull-requests-limit: 5
28
+ groups:
29
+ pip:
30
+ patterns:
31
+ - "*"
32
+
33
+ # Hash-locked CI requirements (requirements/ci/*.txt, compiled with
34
+ # `uv pip compile --universal --generate-hashes`; see the .in headers).
35
+ # tests/test_ci_requirements.py fails if a bump leaves a lock outside the
36
+ # pyproject.toml specifiers.
37
+ - package-ecosystem: "pip"
38
+ directory: "/requirements/ci"
39
+ schedule:
40
+ interval: "weekly"
41
+ day: "monday"
42
+ time: "08:00"
43
+ timezone: "America/New_York"
44
+ labels:
45
+ - "dependencies"
46
+ open-pull-requests-limit: 5
47
+ groups:
48
+ pip-ci:
49
+ patterns:
50
+ - "*"
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env python3
2
+ """SZL PyPI release gate — build, verify, and bind a distribution to its git tag.
3
+
4
+ Canonical file: identical bytes in every szl-holdings repository that publishes
5
+ to PyPI. Runs in the `build` job of `.github/workflows/publish-pypi.yml`:
6
+
7
+ * on pull_request / push -> builds and gates every declared package (readiness)
8
+ * on release (published) -> selects the package named by the tag, requires
9
+ tag == v{version} (single package) or
10
+ {name}-v{version} (multi-package repo), refuses
11
+ versions that already exist on PyPI, and emits
12
+ `dist/` plus a JSON receipt for the publish job.
13
+
14
+ Fails closed. Nothing here talks to PyPI with credentials; publication happens
15
+ only in the separate `publish` job through Trusted Publishing (OIDC).
16
+ """
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import hashlib
21
+ import json
22
+ import os
23
+ import re
24
+ import shutil
25
+ import subprocess
26
+ import sys
27
+ import tomllib
28
+ import urllib.error
29
+ import urllib.request
30
+ import zipfile
31
+ from email.parser import HeaderParser
32
+ from pathlib import Path
33
+
34
+ FORBIDDEN_TOP_LEVEL = {"tests", "test", "docs", "examples", "example", "scripts", "benchmarks", "build", "dist", "out", "data", "payload", "fixtures"}
35
+ PRIVATE_CLASSIFIER = "Private :: Do Not Upload"
36
+
37
+
38
+ def norm(name: str) -> str:
39
+ return re.sub(r"[-_.]+", "-", name).lower()
40
+
41
+
42
+ def sh(cmd: list[str], **kw) -> subprocess.CompletedProcess:
43
+ print("+", " ".join(cmd), flush=True)
44
+ return subprocess.run(cmd, check=True, text=True, **kw)
45
+
46
+
47
+ def fail(msg: str) -> None:
48
+ print(f"::error::{msg}", flush=True)
49
+ sys.exit(1)
50
+
51
+
52
+ def warn(msg: str) -> None:
53
+ print(f"::warning::{msg}", flush=True)
54
+
55
+
56
+ def parse_packages(spec: str) -> list[dict]:
57
+ pkgs = []
58
+ for item in [s.strip() for s in spec.split(";") if s.strip()]:
59
+ name, _, rest = item.partition("=")
60
+ directory, _, env = rest.partition(":")
61
+ pkgs.append({"name": name.strip(), "dir": (directory or ".").strip(), "environment": (env or "pypi").strip()})
62
+ if not pkgs:
63
+ fail("SZL_PACKAGES is empty")
64
+ return pkgs
65
+
66
+
67
+ def pypi_versions(name: str) -> set[str] | None:
68
+ req = urllib.request.Request(f"https://pypi.org/pypi/{norm(name)}/json", headers={"User-Agent": "szl-pypi-release-gate/1"})
69
+ try:
70
+ with urllib.request.urlopen(req, timeout=30) as r:
71
+ return set(json.loads(r.read())["releases"].keys())
72
+ except urllib.error.HTTPError as e:
73
+ if e.code == 404:
74
+ return set()
75
+ warn(f"PyPI lookup for {name} failed: HTTP {e.code}")
76
+ return None
77
+ except Exception as e: # network trouble is not a reason to publish blindly on release
78
+ warn(f"PyPI lookup for {name} failed: {e}")
79
+ return None
80
+
81
+
82
+ def read_metadata(wheel: Path) -> dict:
83
+ with zipfile.ZipFile(wheel) as z:
84
+ meta_name = next(n for n in z.namelist() if n.endswith(".dist-info/METADATA"))
85
+ msg = HeaderParser().parsestr(z.read(meta_name).decode("utf-8", "replace"))
86
+ top = sorted({n.split("/")[0] for n in z.namelist() if not n.split("/")[0].endswith(".dist-info")})
87
+ py_files = [n for n in z.namelist() if n.endswith(".py")]
88
+ return {"msg": msg, "top_level": top, "py_count": len(py_files)}
89
+
90
+
91
+ def gate_package(pkg: dict, repo: str, event: str, ref_type: str, ref_name: str, outdir: Path) -> dict:
92
+ pdir = Path(pkg["dir"]).resolve()
93
+ pyproject = pdir / "pyproject.toml"
94
+ if not pyproject.is_file():
95
+ fail(f"{pkg['name']}: {pyproject} not found")
96
+ with open(pyproject, "rb") as f:
97
+ pp = tomllib.load(f)
98
+ project = pp.get("project") or {}
99
+ release_cfg = ((pp.get("tool") or {}).get("szl") or {}).get("release") or {}
100
+
101
+ if norm(project.get("name", "")) != norm(pkg["name"]):
102
+ fail(f"{pkg['name']}: pyproject [project].name is {project.get('name')!r}, expected {pkg['name']!r}")
103
+ if PRIVATE_CLASSIFIER in (project.get("classifiers") or []):
104
+ fail(f"{pkg['name']}: carries '{PRIVATE_CLASSIFIER}' — not a publishable package")
105
+ if not (pp.get("build-system") or {}).get("build-backend"):
106
+ fail(f"{pkg['name']}: pyproject has no [build-system].build-backend (PEP 517 backend must be explicit)")
107
+ for key in ("description", "readme", "requires-python"):
108
+ if not project.get(key):
109
+ fail(f"{pkg['name']}: [project].{key} is required")
110
+ if not (project.get("license") or project.get("license-files")):
111
+ fail(f"{pkg['name']}: [project].license (or license-files) is required")
112
+ urls = project.get("urls") or {}
113
+ source_url = urls.get("Source") or urls.get("Repository") or urls.get("Source Code")
114
+ expected_source_url = f"https://github.com/{repo}"
115
+ if not isinstance(source_url, str) or source_url.rstrip("/").lower() != expected_source_url.lower():
116
+ fail(f"{pkg['name']}: [project.urls] must carry Source/Repository = {expected_source_url} (got {source_url!r}); PyPI verifies this against the Trusted Publisher")
117
+
118
+ # Build into a per-package staging dir, then verify.
119
+ stage = outdir.parent / f"stage-{norm(pkg['name'])}"
120
+ shutil.rmtree(stage, ignore_errors=True)
121
+ stage.mkdir(parents=True)
122
+ sh([sys.executable, "-m", "build", "--outdir", str(stage), str(pdir)])
123
+ files = sorted(stage.iterdir())
124
+ wheels = [f for f in files if f.suffix == ".whl"]
125
+ sdists = [f for f in files if f.name.endswith(".tar.gz")]
126
+ if len(wheels) != 1 or len(sdists) != 1:
127
+ fail(f"{pkg['name']}: expected exactly one wheel and one sdist, got {[f.name for f in files]}")
128
+ sh([sys.executable, "-m", "twine", "check", "--strict", *[str(f) for f in files]])
129
+
130
+ meta = read_metadata(wheels[0])
131
+ msg = meta["msg"]
132
+ version = msg["Version"]
133
+ if norm(msg["Name"]) != norm(pkg["name"]):
134
+ fail(f"{pkg['name']}: built metadata Name={msg['Name']!r} does not match")
135
+ if meta["py_count"] == 0:
136
+ fail(f"{pkg['name']}: wheel contains no Python files — packaging is misconfigured")
137
+ bad_top = sorted(set(meta["top_level"]) & FORBIDDEN_TOP_LEVEL)
138
+ if bad_top:
139
+ fail(f"{pkg['name']}: wheel leaks non-package top-level entries {bad_top}; fix package discovery")
140
+ declared = release_cfg.get("import_names")
141
+ if declared:
142
+ actual = {t[:-3] if t.endswith(".py") else t for t in meta["top_level"]}
143
+ if actual != set(declared):
144
+ fail(f"{pkg['name']}: wheel top-level names {sorted(actual)} != declared [tool.szl.release].import_names {sorted(declared)}")
145
+ if not msg.get_all("Project-URL"):
146
+ fail(f"{pkg['name']}: built metadata has no Project-URL entries")
147
+
148
+ # Tag binding (release only) and PyPI collision check.
149
+ tag_expect_single = f"v{version}"
150
+ tag_expect_multi = f"{norm(pkg['name'])}-v{version}"
151
+ if event == "release":
152
+ if ref_type != "tag":
153
+ fail(f"release event on non-tag ref {ref_type}:{ref_name}")
154
+ if ref_name not in (tag_expect_single, tag_expect_multi):
155
+ fail(f"{pkg['name']}: tag {ref_name!r} does not match built version {version!r} (expected {tag_expect_single!r} or {tag_expect_multi!r})")
156
+ existing = pypi_versions(pkg["name"])
157
+ if existing is None and event == "release":
158
+ fail(f"{pkg['name']}: could not confirm PyPI release list; refusing to publish blind")
159
+ if existing and version in existing:
160
+ if event == "release":
161
+ fail(f"{pkg['name']}=={version} already exists on PyPI; bump [project].version before releasing")
162
+ warn(f"{pkg['name']}=={version} already exists on PyPI; a release will require a version bump")
163
+
164
+ # Smoke-install the wheel in isolation and import-check the declared names.
165
+ venv = outdir.parent / f"venv-{norm(pkg['name'])}"
166
+ shutil.rmtree(venv, ignore_errors=True)
167
+ sh([sys.executable, "-m", "venv", str(venv)])
168
+ vpy = venv / ("Scripts" if os.name == "nt" else "bin") / "python"
169
+ sh([str(vpy), "-m", "pip", "install", "--disable-pip-version-check", "-q", str(wheels[0])])
170
+ sh([str(vpy), "-c", f"import importlib.metadata as m; assert m.version({pkg['name']!r}) == {version!r}, m.version({pkg['name']!r})"])
171
+ for mod in declared or []:
172
+ sh([str(vpy), "-c", f"import importlib; importlib.import_module({mod!r})"])
173
+
174
+ outdir.mkdir(parents=True, exist_ok=True)
175
+ receipt = {"package": pkg["name"], "version": version, "environment": pkg["environment"], "event": event, "ref": f"{ref_type}:{ref_name}",
176
+ "repository": repo, "source_sha": os.environ.get("GITHUB_SHA"), "run_id": os.environ.get("GITHUB_RUN_ID"),
177
+ "files": {}, "top_level": meta["top_level"]}
178
+ for f in files:
179
+ receipt["files"][f.name] = hashlib.sha256(f.read_bytes()).hexdigest()
180
+ shutil.copy2(f, outdir / f.name)
181
+ print(json.dumps(receipt, indent=2))
182
+ return receipt
183
+
184
+
185
+ def main() -> None:
186
+ ap = argparse.ArgumentParser()
187
+ ap.add_argument("--packages", required=True, help="name=dir[:environment];name2=dir2[:environment]")
188
+ ap.add_argument("--repo", required=True)
189
+ ap.add_argument("--event", required=True)
190
+ ap.add_argument("--ref-type", required=True)
191
+ ap.add_argument("--ref-name", required=True)
192
+ ap.add_argument("--outdir", default="dist")
193
+ args = ap.parse_args()
194
+
195
+ pkgs = parse_packages(args.packages)
196
+ outdir = Path(args.outdir).resolve()
197
+ shutil.rmtree(outdir, ignore_errors=True)
198
+
199
+ if args.event == "release":
200
+ if len(pkgs) == 1:
201
+ selected = pkgs
202
+ else:
203
+ m = re.match(r"^(?P<name>.+?)-v(?P<ver>\d.*)$", args.ref_name)
204
+ if not m:
205
+ fail(f"multi-package repository: tag must be <package>-v<version>, got {args.ref_name!r}")
206
+ selected = [p for p in pkgs if norm(p["name"]) == norm(m.group("name"))]
207
+ if len(selected) != 1:
208
+ fail(f"tag {args.ref_name!r} names no declared package; declared: {[p['name'] for p in pkgs]}")
209
+ else:
210
+ selected = pkgs
211
+
212
+ receipts = []
213
+ for pkg in selected:
214
+ pkg_out = outdir if args.event == "release" else outdir / norm(pkg["name"])
215
+ receipts.append(gate_package(pkg, args.repo, args.event, args.ref_type, args.ref_name, pkg_out))
216
+
217
+ (outdir.parent / "release-gate.json").write_text(json.dumps(receipts, indent=2))
218
+ gh_out = os.environ.get("GITHUB_OUTPUT")
219
+ if gh_out and args.event == "release":
220
+ r = receipts[0]
221
+ with open(gh_out, "a") as f:
222
+ f.write(f"package={r['package']}\nversion={r['version']}\nenvironment={r['environment']}\n")
223
+ print(f"release gate PASS for {[r['package'] + '==' + r['version'] for r in receipts]}")
224
+
225
+
226
+ if __name__ == "__main__":
227
+ main()
@@ -0,0 +1,67 @@
1
+ # Standard Python gate for the szl-holdings estate: ruff + pytest on the two
2
+ # supported interpreters. Installed by the szl-alignment engine (only into
3
+ # repos where Python is detected) from
4
+ # alignment/templates/workflows/base-python-ci.yml. Local deviation from that
5
+ # template: the toolchain, the core dependency and the dev extra install from
6
+ # the hash-locked requirements/ci/base.txt (OpenSSF Scorecard
7
+ # Pinned-Dependencies), the package installs with --no-deps, and the
8
+ # template's requirements*.txt probes are dropped because this repository has
9
+ # no such files. The web extra stays uninstalled on purpose, so this gate
10
+ # keeps proving that the suite runs without fastapi/starlette/httpx.
11
+ name: base-python-ci
12
+
13
+ on:
14
+ pull_request:
15
+ push:
16
+ branches: [main, master]
17
+
18
+ permissions:
19
+ contents: read
20
+
21
+ jobs:
22
+ test:
23
+ runs-on: ubuntu-latest
24
+ strategy:
25
+ fail-fast: false
26
+ matrix:
27
+ python-version: ["3.11", "3.12"]
28
+ steps:
29
+ # Org policy: every action is pinned to a full-length commit SHA.
30
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
31
+ with:
32
+ persist-credentials: false
33
+
34
+ - name: Set up Python ${{ matrix.python-version }}
35
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
36
+ with:
37
+ python-version: ${{ matrix.python-version }}
38
+
39
+ # Regenerate with the command in requirements/ci/base.in.
40
+ - name: Install toolchain (hash-locked)
41
+ run: python -m pip install --disable-pip-version-check --require-hashes -r requirements/ci/base.txt
42
+
43
+ - name: Install the package (no dependency resolution)
44
+ run: python -m pip install --disable-pip-version-check --no-deps --no-build-isolation -e .
45
+
46
+ # Blocking scope is the classes that indicate real breakage (syntax
47
+ # errors, undefined names, unused imports). The full default ruleset runs
48
+ # non-blocking so repos keep visibility without a forced flag day; repos
49
+ # with their own ruff config in pyproject.toml already get stricter
50
+ # behavior from their primary CI.
51
+ - name: "Ruff (blocking: E9/F)"
52
+ run: ruff check . --output-format github --select E9,F
53
+
54
+ - name: Ruff (full ruleset, informational)
55
+ run: ruff check . --statistics --exit-zero
56
+
57
+ # The base gate owns tests/ only. Suites living elsewhere (scripts/,
58
+ # tools/) are exercised by each repo's dedicated workflows with their
59
+ # own dependency pins; collecting them here would fail on imports this
60
+ # minimal install does not provide. Repos with no tests/ skip honestly.
61
+ - name: Pytest (blocking)
62
+ run: |
63
+ if [ -d tests ]; then
64
+ pytest -q tests
65
+ else
66
+ echo "::notice::No tests/ directory; base gate defers to repo-specific suites."
67
+ fi
@@ -0,0 +1,45 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["**"]
6
+ pull_request:
7
+ branches: ["**"]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ name: pytest (${{ matrix.python-version }})
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
20
+
21
+ steps:
22
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
23
+ with:
24
+ persist-credentials: false
25
+
26
+ - name: Set up Python ${{ matrix.python-version }}
27
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
28
+ with:
29
+ python-version: ${{ matrix.python-version }}
30
+
31
+ # Every distribution comes from the hash-locked file (core + dev + web
32
+ # extras, plus the build backend); the package itself then installs
33
+ # without resolving anything. Regenerate with the command in
34
+ # requirements/ci/test.in.
35
+ - name: Install hash-locked test dependencies
36
+ run: python -m pip install --disable-pip-version-check --require-hashes -r requirements/ci/test.txt
37
+
38
+ - name: Install the package (no dependency resolution)
39
+ run: python -m pip install --disable-pip-version-check --no-deps --no-build-isolation -e .
40
+
41
+ - name: Check installed requirements are consistent
42
+ run: python -m pip check
43
+
44
+ - name: Run pytest
45
+ run: python -m pytest -q
@@ -0,0 +1,53 @@
1
+ name: CodeQL
2
+
3
+ # Self-contained CodeQL SAST for Python. This repository is public, so code
4
+ # scanning results upload to the Security tab (the action's default) and pull
5
+ # requests get the code-scanning check. The earlier private-repo pattern ran with
6
+ # upload:false, which only failed on analysis errors and never surfaced a
7
+ # finding. All actions SHA-pinned (org policy).
8
+
9
+ on:
10
+ push:
11
+ branches: [main]
12
+ pull_request:
13
+ branches: [main]
14
+ schedule:
15
+ - cron: '27 4 * * 1'
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ concurrency:
21
+ group: codeql-${{ github.ref }}
22
+ cancel-in-progress: true
23
+
24
+ jobs:
25
+ analyze:
26
+ name: Analyze (${{ matrix.language }})
27
+ runs-on: ubuntu-latest
28
+ permissions:
29
+ actions: read
30
+ contents: read
31
+ security-events: write
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ language: [python]
36
+ steps:
37
+ - name: Harden the runner (Audit all outbound calls)
38
+ uses: step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a # v2.22.4
39
+ with:
40
+ egress-policy: audit
41
+ - name: Checkout repository
42
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
43
+ with:
44
+ persist-credentials: false
45
+ - name: Initialize CodeQL
46
+ uses: github/codeql-action/init@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
47
+ with:
48
+ languages: ${{ matrix.language }}
49
+ queries: security-extended
50
+ - name: Perform CodeQL Analysis
51
+ uses: github/codeql-action/analyze@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
52
+ with:
53
+ category: "/language:${{ matrix.language }}"
@@ -0,0 +1,43 @@
1
+ name: Conventional Commits PR title lint
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, edited, synchronize, reopened]
6
+
7
+ permissions:
8
+ contents: read
9
+ pull-requests: read
10
+
11
+ jobs:
12
+ commitlint:
13
+ name: Lint PR title (Conventional Commits)
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Check PR title follows Conventional Commits
17
+ env:
18
+ # Passed via env (never interpolated into the script) to avoid injection.
19
+ PR_TITLE: ${{ github.event.pull_request.title }}
20
+ run: |
21
+ set -euo pipefail
22
+
23
+ # Conventional Commits types allowed
24
+ types='feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert'
25
+
26
+ # <type>(optional-scope)(optional !): <subject>
27
+ header_re="^(${types})(\([a-z0-9._/-]+\))?(!)?: .+$"
28
+
29
+ if [[ ! "${PR_TITLE}" =~ ${header_re} ]]; then
30
+ echo "::error::PR title \"${PR_TITLE}\" is not a valid Conventional Commit."
31
+ echo "Expected format: <type>(optional-scope): <subject>"
32
+ echo "Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert"
33
+ exit 1
34
+ fi
35
+
36
+ # Subject must not start with an upper-case letter
37
+ subject="${PR_TITLE#*: }"
38
+ if [[ "${subject}" =~ ^[A-Z] ]]; then
39
+ echo "::error::The subject \"${subject}\" does not match the required pattern. Please use lower-case for the subject."
40
+ exit 1
41
+ fi
42
+
43
+ echo "PR title OK: ${PR_TITLE}"
@@ -0,0 +1,12 @@
1
+ name: Doctrine
2
+ on:
3
+ pull_request:
4
+ push:
5
+ branches: [main]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ check:
12
+ uses: szl-holdings/.github/.github/workflows/doctrine-check.yml@79cb06b79d6a3156a5796948295617ff6b207683 # main
@@ -0,0 +1,39 @@
1
+ # Release-blocking CRITICAL gate for the szl-holdings estate.
2
+ #
3
+ # Rule (validated on the live org): a line matching
4
+ # (?<!-)a11oy\.com
5
+ # is a violation UNLESS the same line is a prohibition/guard context, i.e. it
6
+ # also matches /(never|forbidden|not in|assertNotIn|does not appear|is not a
7
+ # surface|blocklist)/i — guard assertions, blocklists, and doctrine notes are
8
+ # legitimate. Canonical surfaces are a-11-oy.com (product) and a11oy.net
9
+ # (proof).
10
+ #
11
+ # Installed by the szl-alignment engine; keep in sync with
12
+ # alignment/templates/workflows/forbidden-domain.yml.
13
+ name: forbidden-domain
14
+
15
+ on:
16
+ pull_request:
17
+
18
+ permissions:
19
+ contents: read
20
+
21
+ jobs:
22
+ scan:
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
+
27
+ - name: Scan for forbidden domain references
28
+ shell: bash
29
+ run: |
30
+ set -euo pipefail
31
+ hits="$(rg -n --no-heading --hidden -P '(?<!-)a11oy\.com' --glob '!.git' . || true)"
32
+ violations="$(printf '%s' "$hits" | rg -v -i 'never|forbidden|not in|assertNotIn|does not appear|is not a surface|blocklist' || true)"
33
+ if [ -n "$violations" ]; then
34
+ echo "::error::Forbidden domain references found (release-blocking CRITICAL)."
35
+ echo "::error::Replace with a-11-oy.com or remove; guard contexts must say so on the same line."
36
+ printf '%s\n' "$violations"
37
+ exit 1
38
+ fi
39
+ echo "OK: no forbidden domain references outside guard contexts."