Continuous Integration And Release Contract¶
Assertions¶
CIR-001 - Workflow topology is event-driven and immutable¶
Contract: The repository MUST contain only CI, dependency submission,
Pages, PR-body, and release workflows. Every GitHub-hosted job MUST run on
ubuntu-26.04; external actions MUST use full commit SHAs; validator images
MUST use immutable digests; checkout credentials MUST not persist; and every
workflow MUST declare permissions and concurrency.
Evidence:
test_workflow_set_is_event_driven_and_every_action_is_sha_pinned-tests/test_ci_policy.py::test_workflow_set_is_event_driven_and_every_action_is_sha_pinnedtest_every_github_hosted_job_uses_ubuntu_26_04-tests/test_ci_policy.py::test_every_github_hosted_job_uses_ubuntu_26_04
CIR-002 - Quality, compatibility, package, and OpenSSH remain visible gates¶
Contract: Reusable CI MUST assign source/policy, compatibility/coverage, distribution, hermetic OpenSSH, dependency review with an every-event exact-lock audit, Python and Actions CodeQL, and version progression to visible scoped jobs. Compatibility MUST run once for each declared Python minor; every other expensive gate MUST run once per invocation. No umbrella job MAY repeat another job's tests or build.
Evidence:
test_ci_preserves_quality_python_package_and_openssh_gates-tests/test_ci_policy.py::test_ci_preserves_quality_python_package_and_openssh_gates
CIR-003 - Write permissions are job-scoped¶
Contract: Default workflow permission MUST be read-only contents. Only CodeQL MAY write security events, dependency submission MAY write contents, Pages deploy MAY write Pages and OIDC, PR-body automation MAY write pull requests, PyPI publication MAY write OIDC, and final GitHub publication MAY write contents. Each exception MUST be confined to its owning job.
Evidence:
test_workflow_write_permissions_are_confined-tests/test_ci_policy.py::test_workflow_write_permissions_are_confined
CIR-004 - PR-body automation treats head content as bounded data¶
Contract: PR-body automation MUST be the only pull_request_target
boundary, run only for pull requests that change CHANGELOG.md, execute only
trusted default-branch code, read a bounded head changelog through the API as
inert data, preserve manual body text, and refuse concurrent overwrite.
Evidence:
test_pr_body_is_only_pull_request_target_boundary-tests/test_ci_policy.py::test_pr_body_is_only_pull_request_target_boundarytest_pr_body_preserves_manual_text_and_replaces_only_managed_content-tests/test_release_helpers.py::test_pr_body_preserves_manual_text_and_replaces_only_managed_content
CIR-005 - Version progression compares exact base and head¶
Contract: Pull requests and later main pushes MUST compare the exact base
and proposed .version values. The first repository commit MAY compare against
0.0.0. Same-version recovery MAY occur only for an unpublished version newer
than the latest stable release.
Evidence:
test_version_job_compares_exact_base_and_head-tests/test_ci_policy.py::test_version_job_compares_exact_base_and_headtest_unpublished_recovery_and_exact_notes-tests/test_release_governance.py::test_unpublished_recovery_and_exact_notes
CIR-006 - Publication builds once and never replaces conflict¶
Contract: Reusable CI's distribution job MUST build and smoke the wheel/sdist once and, for the trusted release caller only, upload that exact pair as one immutable workflow artifact. No other maintained workflow MAY set that upload input. Release jobs MUST consume it without any rebuild for PyPI and GitHub. Published or draft tags, targets, notes, metadata, asset names, sizes, and hashes MUST be verified; conflict MUST fail without moving tags, deleting assets, or overwriting files.
Evidence:
test_release_reuses_ci_and_shared_python_distributions-tests/test_ci_policy.py::test_release_reuses_ci_and_shared_python_distributionstest_release_state_is_exact_and_recovery_is_non_destructive-tests/test_ci_policy.py::test_release_state_is_exact_and_recovery_is_non_destructive
CIR-007 - Dependency submission is trusted-main-only¶
Contract: Dependency submission MUST trigger only on direct main pushes
that change dependency ownership, a lock, or snapshot automation; derive and
validate all four manifests offline; verify the expected repository and payload
shape; and use only its job-scoped standard token and contents write.
Evidence:
test_dependency_submission_is_trusted_main_only-tests/test_ci_policy.py::test_dependency_submission_is_trusted_main_only
CIR-008 - Documentation changes build before merge and deploy from main¶
Contract: Documentation and docs-tool changes MUST trigger a strict
non-deploying Pages build on pull requests. Direct main builds MUST audit
routes, links, anchors, assets, canonical URLs, sitemap XML/gzip, robots, root
files, and the absence of insecure HTTP references before a deploy job receives
Pages and OIDC writes.
Evidence:
test_pages_validates_prs_and_confines_publish_permissions-tests/test_ci_policy.py::test_pages_validates_prs_and_confines_publish_permissionstest_generated_site_audit_accepts_complete_project_site-tests/test_docs_site_audit.py::test_generated_site_audit_accepts_complete_project_site
CIR-009 - Contract evidence is machine-checked¶
Contract: Contract files MUST be the only normative documentation, MUST use unique sequential IDs, and MUST link every assertion to at least one existing test definition. User, maintenance, and site-only inputs MUST remain separate, and every relative documentation link MUST resolve.
Evidence:
test_contract_assertions_have_unique_ids_and_real_evidence-tests/test_documentation_contracts.py::test_contract_assertions_have_unique_ids_and_real_evidencetest_documentation_tree_navigation_and_language_are_separated-tests/test_documentation_contracts.py::test_documentation_tree_navigation_and_language_are_separatedtest_public_python_examples_compile_and_import_only_root_api-tests/test_documentation_contracts.py::test_public_python_examples_compile_and_import_only_root_api
CIR-010 - Repository ownership is explicit¶
Contract: CODEOWNERS MUST assign every repository path to @kogeler, so a
default-branch ruleset can require code-owner review for every change.
Evidence:
test_codeowners_assigns_entire_repository_to_kogeler-tests/test_ci_policy.py::test_codeowners_assigns_entire_repository_to_kogeler
CIR-011 - PyPI publication is secretless, exact, and recoverable¶
Contract: Release state MUST inspect PyPI and GitHub independently. A
missing PyPI version MUST publish only the gated wheel/sdist through the pypi
Environment, PyPI Trusted Publishing, a job-scoped OIDC token, and the pinned
official PyPA action. Stored credentials and blind duplicate skipping MUST be
absent. Existing PyPI files MUST match local names, types, sizes, hashes, and
yanked state before GitHub recovery continues.
Evidence:
test_pypi_publication_uses_oidc_without_stored_credentials-tests/test_ci_policy.py::test_pypi_publication_uses_oidc_without_stored_credentialstest_pypi_verifier_accepts_only_exact_local_files-tests/test_release_helpers.py::test_pypi_verifier_accepts_only_exact_local_files
CIR-012 - Mutable pins have executable owners¶
Contract: Project versions, dependency pins, action commits, image digests, runner labels, and release conventions MUST be owned by the executable configuration that consumes them. Tests MUST verify structure and relationships without duplicating unrelated mutable literals.
Evidence:
test_mutable_pins_have_executable_owners-tests/test_ci_policy.py::test_mutable_pins_have_executable_owners
CIR-013 - Automation is scoped to a Python library¶
Contract: CI and release automation MUST create only the standard pure Python wheel and source distribution. It MUST NOT contain standalone application, native executable, container-image, Node application, or Rust build processes. Quality, tests, distributions, and OpenSSH acceptance MUST each have one workflow owner, and publication MUST not rebuild CI artifacts.
Evidence:
test_ci_has_one_owner_per_library_gate_and_no_application_builds-tests/test_ci_policy.py::test_ci_has_one_owner_per_library_gate_and_no_application_builds