From b190a76d5d3002aa8342b1325e88f951d39572ee Mon Sep 17 00:00:00 2001 From: Zenith Runeblade Date: Fri, 18 Sep 2026 18:55:07 +0200 Subject: [PATCH 1/2] ci: add core Python checks without host dependencies --- .github/workflows/checks.yml | 11 +++----- .github/workflows/core-checks.yml | 44 +++++++++++++++++++++++++++++++ CONTRIBUTING.md | 24 +++++++++++++---- Makefile | 11 ++++++++ docs/adr/03-quality.md | 11 +++++--- docs/adr/04-testing.md | 7 +++-- 6 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/core-checks.yml diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index db4c843..474f4e8 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,12 +1,12 @@ name: Repository Checks -# Manual only for now. The gate is green on a maintainer workstation but red on a +# Full qualification remains manual. Core Python CI runs its named subset on PRs. +# This broader gate is green on a maintainer workstation but red on a # GitHub-hosted runner, and the differences are environmental rather than code: # ubuntu-latest carries Podman 4.9, whose Quadlet generator rejects the `Pod=` # key that Podman 5 accepts, and the file-publication and workspace-restore # receipts classify a restored path differently there than on the developer's -# filesystem. Restore the push and pull_request triggers once those are settled; -# leaving them on now only teaches everyone to ignore a red mark. +# filesystem. Resolve those prerequisites before expanding automatic coverage. on: workflow_dispatch: @@ -124,10 +124,7 @@ jobs: documentation: name: Hexanomicon build - # Main already performs this clean build while producing the Pages artifact. - # Keep the independent check here for pull requests without rebuilding twice - # after the same revision lands. - if: github.event_name == 'pull_request' + # Include documentation in manual qualification; Pages builds main separately. runs-on: ubuntu-latest timeout-minutes: 15 env: diff --git a/.github/workflows/core-checks.yml b/.github/workflows/core-checks.yml new file mode 100644 index 0000000..8de2fef --- /dev/null +++ b/.github/workflows/core-checks.yml @@ -0,0 +1,44 @@ +name: Core Python CI + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +concurrency: + group: core-python-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + core: + name: Core Python checks (no host or containers) + runs-on: ubuntu-24.04 + timeout-minutes: 20 + env: + UV_LOCKED: "1" + UV_CACHE_DIR: .cache/uv + PYTEST_BASETEMP: .cache/pytest/core-${{ github.run_id }}-${{ github.run_attempt }} + steps: + - name: Check out source + uses: actions/checkout@v7 + with: + persist-credentials: false + + - name: Install Python and uv + uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + version: "0.12.10" + python-version: "3.13" + enable-cache: true + cache-suffix: core-python + + - name: Check lint, formatting and types + run: make lint format-check type-check + + - name: Test core contracts + run: make test-ci diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aacebc7..59d8b7f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,11 +89,25 @@ pytest's native compact report, capture logs, and allocate a unique scratch dire `.cache/pytest`; set `PYTEST_BASETEMP` to an explicit current-user-owned path only when a caller must own that location. -Pull requests run four independent repository checks: the Python umbrella, the disposable -PostgreSQL receipts, the Altar check/build plus generated-diff guard, and a clean documentation -build. Pushes to `main` repeat the first three while the deployment workflow's clean documentation -build supplies the fourth gate and its Pages artifact. The tag/manual release-candidate workflow -remains a separate non-publishing artifact receipt. +Pull requests and pushes to `main` run **Core Python CI**: repository lint, formatting and strict +typing, followed by `make test-ci`. This explicit selection covers configuration, database model +contracts, animation/dispatch/orchestration, core Run logic, extensions, agents and web contracts, +plus the configuration workflow tests. It uses in-process substitutes, requires no container +daemon, running database, systemd service, model or GPU, and does not prove host operation. +`CI_PYTEST_TARGETS` in the Makefile owns the exact selection. Reproduce it locally with: + +```bash +make lint format-check type-check +make test-ci +``` + +The **Repository Checks** workflow remains manually dispatched: full `make check`, disposable +PostgreSQL, Altar check/build with the generated-diff guard, and a clean documentation build. +Host-dependent Quadlet/filesystem differences on GitHub runners remain unresolved; a core-CI +pass does not waive these broader checks for a change that needs them. The Pages workflow builds +documentation on `main`; the tag/manual release-candidate workflow remains a separate +non-publishing artifact receipt. `make test` and `make check` retain their full non-container +selection; `make test-ci` does not replace them for final verification. Disposable PostgreSQL receipts are an explicit host-integration profile, not part of ordinary `make check`: diff --git a/Makefile b/Makefile index e5c87db..28187ad 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,13 @@ MAKEFLAGS += --no-print-directory N ?= 0 VERBOSE ?= 0 PYTEST_TARGETS ?= tests +# Automatic CI covers these in-process contracts; host/filesystem qualification +# and disposable PostgreSQL remain separate. Keep the full test/check defaults. +CI_PYTEST_TARGETS := tests/unit/config tests/unit/db \ + tests/unit/domain/animation tests/unit/domain/cortex \ + tests/unit/domain/orchestration tests/unit/domain/web \ + tests/unit/extensions tests/unit/lib tests/agents tests/web \ + tests/integration/test_configuration_workflows.py CONTAINER_TEST_TARGETS ?= tests/integration/test_db_consent_pg.py \ tests/integration/test_database_authority_pg.py \ tests/integration/test_db_atlas_pg.py \ @@ -240,6 +247,10 @@ test-containers: ## Run explicit disposable-PostgreSQL receipts; requires a Dock fi @$(UV_DEV_RUN) --group container-test pytest $(CONTAINER_PYTEST_ARGS) --basetemp "$$basetemp" $(CONTAINER_TEST_TARGETS) +.PHONY: test-ci +test-ci: ## Run the core CI selection without real host services, models or containers + @$(MAKE) test PYTEST_TARGETS="$(CI_PYTEST_TARGETS)" M="not container" N=0 + .PHONY: test-config test-config: ## Run configurable/runes focused tests only @$(MAKE) test PYTEST_TARGETS="tests/unit/config/runes tests/unit/system/services/test_codex.py" diff --git a/docs/adr/03-quality.md b/docs/adr/03-quality.md index 342d4fe..bfbd286 100644 --- a/docs/adr/03-quality.md +++ b/docs/adr/03-quality.md @@ -45,10 +45,13 @@ The Python umbrella does not silently run frontend work. Both frontend gates reg contract; the build also changes the tracked static projection, which belongs in review with the source that produced it. A generated diff guard catches disagreement. -Pull requests expose all four lanes independently. A push to `main` repeats the first three; -the Pages workflow supplies the clean documentation gate and deployment artifact in one build. -The tag/manual release-candidate workflow remains separate: a green source change does not bind -release archives or prove installation on a host. +Automatic pull-request and `main` checks run repository lint, formatting, strict typing and the +explicit core test selection owned by `make test-ci`. That selection uses in-process substitutes +and excludes real host and container qualification. The full four-lane Repository Checks workflow +remains manual while host-runner compatibility is unresolved; changes still require the broader +gates appropriate to their boundary. The Pages workflow separately supplies the clean documentation +gate and deployment artifact on `main`. The tag/manual release-candidate workflow remains separate: +a green core check does not bind release archives or prove installation on a host. [Frontend](15-frontend.md#decision-lock-and-reopening-gate) owns the exact Node/npm pins and single client vocabulary. Bun, a second lock/runtime, Tailwind, or another styling compiler must diff --git a/docs/adr/04-testing.md b/docs/adr/04-testing.md index 251df17..5f6b336 100644 --- a/docs/adr/04-testing.md +++ b/docs/adr/04-testing.md @@ -61,8 +61,11 @@ structural exclusions. `make coverage` enforces it serially with the ordinary `n selection. Default tests, the Python umbrella, pull-request checks, and release-candidate CI do not pass `--cov`. Coverage is an opt-in gate, not an implicitly passed release condition. -Pull-request and `main` workflows run ordinary tests and disposable PostgreSQL receipts in -separate jobs. Their results remain separate even when both are green. +Pull-request and `main` Core Python CI runs the explicit `make test-ci` selection, using +in-process substitutes without a real host, model or container daemon. Full ordinary tests and +disposable PostgreSQL receipts remain separate jobs in the manually dispatched Repository Checks +workflow. A core-CI pass does not establish those broader results or waive affected-boundary +verification. The normal `make test` and `make check` selections remain unchanged. ### 5. Runtime Surface Probes From cac9e8a2abf948324fa29805d6e6f2c7dbf39216 Mon Sep 17 00:00:00 2001 From: Zenith Runeblade Date: Fri, 18 Sep 2026 19:07:24 +0200 Subject: [PATCH 2/2] Install optional test dependencies for static analysis --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 28187ad..004a1ec 100644 --- a/Makefile +++ b/Makefile @@ -62,14 +62,14 @@ ifeq ($(RTK_ACTIVE),1) RUN := $(UV_DEV_RUN) $(RTK) run ERR := $(UV_DEV_RUN) $(RTK) err RUFF := $(UV_DEV_RUN) $(RTK) ruff -TYPECHECK := $(UV_DEV_RUN) --group typing $(RTK) err basedpyright +TYPECHECK := $(UV_DEV_RUN) --group typing --group container-test $(RTK) err basedpyright CURL := $(RTK) curl GREP := $(RTK) grep else RUN := ERR := RUFF := $(UV_DEV_RUN) ruff -TYPECHECK := $(UV_DEV_RUN) --group typing basedpyright +TYPECHECK := $(UV_DEV_RUN) --group typing --group container-test basedpyright CURL := curl GREP := grep endif