Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/governance-contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,45 @@ permissions:
contents: read

jobs:
ubuntu-preflight:
name: Shared validation preflight / ${{ matrix.image }}
strategy:
fail-fast: false
matrix:
image: [ubuntu-24.04, ubuntu-26.04]
uses: $/.github/workflows/reusable-validate.yml
with:
package-manager: npm
node-version: "22"
working-directory: fixtures/ubuntu-preflight
ubuntu-image: ${{ matrix.image }}
run-e2e: true

ubuntu26-system-python:
name: Semantic and governance / Ubuntu 26 system Python
runs-on: ubuntu-26.04
timeout-minutes: 10
steps:
- name: Checkout exact event revision with history
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify exact head and record system Python
env:
EXPECTED_HEAD: ${{ github.event.pull_request.head.sha || github.sha }}
run: |
test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD"
git show --no-patch --format='commit=%H'
cat /etc/os-release
printf 'ImageOS=%s ImageVersion=%s\n' "$ImageOS" "$ImageVersion"
python3 --version
- name: Test governance under the image's Python
run: python3 -m unittest discover -s tests -v
- name: Test semantic registry under the image's Python
run: python3 -I scripts/quirk_concept.py lint

validate:
name: validate
runs-on: ubuntu-24.04
Expand Down
34 changes: 32 additions & 2 deletions .github/workflows/reusable-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,17 @@ on:
required: false
type: string
default: "test:e2e"
ubuntu-image:
description: "Runner: ubuntu-latest (default), ubuntu-24.04 (temporary hold), or ubuntu-26.04 (preflight)"
required: false
type: string
default: ubuntu-latest

jobs:
validate:
runs-on: ubuntu-latest
# Map to known hosted images; caller input cannot select a self-hosted runner.
runs-on: ${{ inputs.ubuntu-image == 'ubuntu-26.04' && 'ubuntu-26.04' || inputs.ubuntu-image == 'ubuntu-24.04' && 'ubuntu-24.04' || 'ubuntu-latest' }}
timeout-minutes: 20
env:
# Quirk Systems repos use t3-env; allow the build to skip strict env
# validation when secrets aren't configured in CI.
Expand All @@ -56,10 +63,28 @@ jobs:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Validate runner choice and record exact environment
env:
UBUNTU_IMAGE: ${{ inputs.ubuntu-image }}
EXPECTED_HEAD: ${{ github.event.pull_request.head.sha || github.sha }}
working-directory: .
run: |
case "$UBUNTU_IMAGE" in
ubuntu-latest|ubuntu-24.04|ubuntu-26.04) ;;
*) echo "Unsupported Ubuntu runner choice" >&2; exit 1 ;;
esac
test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD"
git show --no-patch --format='commit=%H'
cat /etc/os-release
printf 'ImageOS=%s ImageVersion=%s\n' "$ImageOS" "$ImageVersion"

- name: Setup Bun
if: inputs.package-manager == 'bun'
uses: oven-sh/setup-bun@v2
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: ${{ inputs.bun-version }}

Expand All @@ -73,6 +98,11 @@ jobs:
with:
node-version: ${{ inputs.node-version }}
cache: ${{ inputs.package-manager }}
cache-dependency-path: |
${{ inputs.working-directory }}/package-lock.json
${{ inputs.working-directory }}/npm-shrinkwrap.json
${{ inputs.working-directory }}/yarn.lock
${{ inputs.working-directory }}/pnpm-lock.yaml

- name: Setup Node.js
if: inputs.package-manager == 'bun'
Expand Down
160 changes: 160 additions & 0 deletions .quirk/evidence/ubuntu26-preflight-9f7720814e8b.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
{
"schema_version": "evidence-receipt.v1",
"receipt_id": "qreceipt.ubuntu26-preflight.9f7720814e8b",
"repository": "Quirk-Systems/.github",
"status": "verified",
"subject": {
"base_commit": "eb889adf3009951de873890103199600f4aad20f",
"commit": "9f7720814e8b0a80fce5add9c1bb2bd35e3e7750",
"changed_paths": [
".github/workflows/governance-contracts.yml",
".github/workflows/reusable-validate.yml",
"CHANGELOG.md",
"docs/governance/REUSABLE_WORKFLOWS.md",
"docs/governance/UBUNTU_26_AUDIT.md",
"fixtures/ubuntu-preflight/.gitignore",
"fixtures/ubuntu-preflight/browser.spec.ts",
"fixtures/ubuntu-preflight/build.mjs",
"fixtures/ubuntu-preflight/build.test.mjs",
"fixtures/ubuntu-preflight/package-lock.json",
"fixtures/ubuntu-preflight/package.json",
"fixtures/ubuntu-preflight/playwright.config.ts",
"fixtures/ubuntu-preflight/tsconfig.json",
"tests/test_evidence_receipts.py",
"tests/test_ubuntu_preflight.py"
]
},
"claims": [
{
"claim_id": "qclaim.ubuntu26-preflight.9f7720814e8b",
"claim_type": "evidence",
"authority_effect": "none",
"statement": "Adds a closed Ubuntu runner choice, exact-head validation, read-only Ubuntu 24/26 npm browser fixture and Ubuntu 26 system-Python probes. Local governance suite (237 tests), validators and offline zizmor passed; ruff and actionlint unavailable. Hosted Ubuntu compatibility and downstream application compatibility remain unproven; no review, merge, or authority is granted.",
"evidence_paths": [
".github/workflows/governance-contracts.yml",
".github/workflows/reusable-validate.yml",
"CHANGELOG.md",
"docs/governance/REUSABLE_WORKFLOWS.md",
"docs/governance/UBUNTU_26_AUDIT.md",
"fixtures/ubuntu-preflight/.gitignore",
"fixtures/ubuntu-preflight/browser.spec.ts",
"fixtures/ubuntu-preflight/build.mjs",
"fixtures/ubuntu-preflight/build.test.mjs",
"fixtures/ubuntu-preflight/package-lock.json",
"fixtures/ubuntu-preflight/package.json",
"fixtures/ubuntu-preflight/playwright.config.ts",
"fixtures/ubuntu-preflight/tsconfig.json",
"tests/test_evidence_receipts.py",
"tests/test_ubuntu_preflight.py"
]
}
],
"artifacts": [
{
"path": ".github/workflows/governance-contracts.yml",
"state": "present",
"git_blob": "3e438cb9be36609e4230b7b1e193fdae644f7098",
"sha256": "sha256:de4262793aaba4fa7be80d25a979b276290d264308e9fe7cea0e7dcc04f50827"
},
{
"path": ".github/workflows/reusable-validate.yml",
"state": "present",
"git_blob": "8e4508063f8925d1b88ec37c1dd33140555a7bed",
"sha256": "sha256:124fc89642683a09b372e766be477ff97221b080be9f358b7c9ea21a641808b2"
},
{
"path": "CHANGELOG.md",
"state": "present",
"git_blob": "03407e0b9e878d84d8784a1fa9d912e059ad027b",
"sha256": "sha256:d07b38698fb3fe3dcf79c4e1b487793ad2b910ba09798616db93a0fc31726f08"
},
{
"path": "docs/governance/REUSABLE_WORKFLOWS.md",
"state": "present",
"git_blob": "509d4e62eac61e5ce974dfb22dffa109c7d69274",
"sha256": "sha256:176c77f9b2d82ab0342558f8db0b04f8b8a1ffbc0fb9cb4ee9b900ec8dcbb906"
},
{
"path": "docs/governance/UBUNTU_26_AUDIT.md",
"state": "present",
"git_blob": "3c2fc9385cc8d411c8b485792235083e33e9cf67",
"sha256": "sha256:5c312ed3ddd1aa318a54c2013d62f9901bf61d77a805939ead8098716f7c6a01"
},
{
"path": "fixtures/ubuntu-preflight/.gitignore",
"state": "present",
"git_blob": "f0a232bfffba9aee3214a9c12f50e6ccf4cf0c6c",
"sha256": "sha256:463b5f9362420aaa6bbf70c200b7cef361af47f34e60e6767851406f3091a39a"
},
{
"path": "fixtures/ubuntu-preflight/browser.spec.ts",
"state": "present",
"git_blob": "a64631e9ab2959a1f1d5f81faff01ff7f3439233",
"sha256": "sha256:f74e9b4ae08b5bd2636fe5c5b6beb87a10df3fd7008bc1aa3f411e5463ead2ff"
},
{
"path": "fixtures/ubuntu-preflight/build.mjs",
"state": "present",
"git_blob": "30e5ac3b28ed71c97bf8c901353b883b010f4c65",
"sha256": "sha256:20365e7c524392244c5d68c494770926438ce15462cbbd0240c2d2d58f309756"
},
{
"path": "fixtures/ubuntu-preflight/build.test.mjs",
"state": "present",
"git_blob": "1ab973900a4793ebdde7e3a2f0622a8d27214752",
"sha256": "sha256:c150480fce97ede8f11817bfd362259ae572a3b184b998284f173a6850472c0a"
},
{
"path": "fixtures/ubuntu-preflight/package-lock.json",
"state": "present",
"git_blob": "8b3b5ebca0461ac3d43e21b2bc313519d0276d52",
"sha256": "sha256:0be572cbf1c477373377cb3115b9f5546410e4ce58ff7d3ecb6530f90ee16afe"
},
{
"path": "fixtures/ubuntu-preflight/package.json",
"state": "present",
"git_blob": "36249aa5a77897fe6f5c88ec9c3f39477f781c13",
"sha256": "sha256:b0ee5bd92d70ed559f57e7f8e32b3758234f6a13dc04d0bec06da186022a46f2"
},
{
"path": "fixtures/ubuntu-preflight/playwright.config.ts",
"state": "present",
"git_blob": "3a76db67d525838cac5b7b9c87b00bed8b2228d2",
"sha256": "sha256:d38e999b09ee149778d96831e04eae5398f0b6ea2b6707b7b7b3353edbf29eec"
},
{
"path": "fixtures/ubuntu-preflight/tsconfig.json",
"state": "present",
"git_blob": "c99697c0c828c2f516850ab42c2d7eafeda4ba79",
"sha256": "sha256:e78ecddab416e33fc842d78788b85fc380bb14584b5932eb1c897517eaed2bec"
},
{
"path": "tests/test_evidence_receipts.py",
"state": "present",
"git_blob": "d3fe27288b303d1a02bd36f8407fd2286da1b11e",
"sha256": "sha256:7737c1fdb9cac64ce08c222b3d8d3cdec5b036dfb1341905d6a6e19debf15794"
},
{
"path": "tests/test_ubuntu_preflight.py",
"state": "present",
"git_blob": "02b6820efb5b702d91cca5f008216ab44d6d7b21",
"sha256": "sha256:843b02c4699cb93d2963ac64ace5c96a136acc3acf9c3271fa038bedb93844c2"
}
],
"verification": {
"commands": [
{
"command": "scripts/validate.sh",
"result": "pass",
"exit_code": 0
}
],
"verified_at": "2026-10-08T16:47:06Z"
},
"authority": {
"admission_effect": "none",
"authority_ref": null
},
"correction": null,
"receipt_sha256": "sha256:f51e95f0b0a7fc83aee48288ac7e47832b7afdc4a369f46d41ebf7dec10678f8"
}
65 changes: 65 additions & 0 deletions .quirk/evidence/ubuntu26-preflight-repair-dc7ada466704.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"schema_version": "evidence-receipt.v1",
"receipt_id": "qreceipt.ubuntu26-preflight-repair.dc7ada466704",
"repository": "Quirk-Systems/.github",
"status": "verified",
"subject": {
"base_commit": "51d671d9731194f3215a03ce4558cb5181b22046",
"commit": "dc7ada46670428757796120b33b3da29bfd13ed6",
"changed_paths": [
".github/workflows/reusable-validate.yml",
"CHANGELOG.md",
"tests/test_ubuntu_preflight.py"
]
},
"claims": [
{
"claim_id": "qclaim.ubuntu26-preflight-repair.dc7ada466704",
"claim_type": "evidence",
"authority_effect": "none",
"statement": "Pins the inherited Bun setup action to verified v2.2.0 and resolves cache lockfiles from the declared working directory. Local validation passed including 237 tests and offline pedantic zizmor; ruff/actionlint unavailable. Hosted browser proof, review, merge and authority are not granted by this receipt.",
"evidence_paths": [
".github/workflows/reusable-validate.yml",
"CHANGELOG.md",
"tests/test_ubuntu_preflight.py"
]
}
],
"artifacts": [
{
"path": ".github/workflows/reusable-validate.yml",
"state": "present",
"git_blob": "70637b104889ab04a81cc27c144edfeb53e3aa28",
"sha256": "sha256:5cf0eeae14435dc93d4a319c6f6b2ff878573237ccfadd84a9b7feda26f56325"
},
{
"path": "CHANGELOG.md",
"state": "present",
"git_blob": "5fd0c15ff597d71c99102273d1c6c09f7dd103b0",
"sha256": "sha256:0563d148a742e000e5392f2f63ca1fbb7864af9c3987d80bb138d4db70df3a9c"
},
{
"path": "tests/test_ubuntu_preflight.py",
"state": "present",
"git_blob": "77820f7b8ef656ad95458af7e75649aac87e4518",
"sha256": "sha256:4fa21cdb9443110922d73c476c414d72c32609400c03439c75c5b7fe381a8f64"
}
],
"verification": {
"commands": [
{
"command": "scripts/validate.sh",
"result": "pass",
"exit_code": 0
}
],
"verified_at": "2026-10-08T16:52:44Z"
},
"authority": {
"admission_effect": "none",
"authority_ref": null
},
"correction": null,
"receipt_sha256": "sha256:94bf76da98540d67fb7c1dd8eb5f17e4552f980b730652641adc4761507a79a2"
}

24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Changelog

## Unreleased — 2026-10-08

- Add a closed `ubuntu-image` choice to reusable validation: the existing
`ubuntu-latest` default, explicit Ubuntu 24.04 retention, or Ubuntu 26.04
preflight. Invalid choices fail before dependency setup; arbitrary or
self-hosted runner selection is impossible.
- Check out and verify the exact PR head, record the OS/image, and bound shared
validation to 20 minutes. Add an npm fixture through the real reusable
workflow on Ubuntu 24 and 26, including Chromium, Firefox, and WebKit.
- Add an Ubuntu 26 system-Python governance/semantic probe without setup-python.
- Add runner-choice rejection and stale-head regression coverage. Preserve
governance gates, action pins, legacy lint exemptions, and owner authority.
- No existing consumer pin is changed. Hosted fixture proof establishes only
the tested fixture and image; downstream apps require their own preflight.
Revert this change to remove the input and fixture jobs.

- Pin the inherited Bun setup action to the verified v2.2.0 commit. Hosted
policy rejected its floating v2 tag even for the npm fixture's skipped step;
fix the workflow rather than changing the organization's full-SHA protection.
- Resolve setup-node cache lockfiles from the declared working directory. The
real nested npm fixture exposed the inherited root-only cache lookup failure
on both Ubuntu images; lockfile patterns cover npm, yarn, and pnpm.
25 changes: 24 additions & 1 deletion docs/governance/REUSABLE_WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ through `with:`; no reusable workflow interpolates caller input inside a
| --- | --- | --- | --- | --- | --- |
| `reusable-evidence-binding.yml` | `pull_request` only | none | `contents: read` | Every changed path in the PR range is covered by a fresh verified receipt | Semantic sufficiency, review, merge, canon |
| `quirk-semantic-governance.yml` | any | none | `contents: read` | Caller `.quirk/manifest.json` has the required keys; the canonical registry, read at this workflow's own pinned commit, lints clean | That the manifest's domain claims are true |
| `reusable-validate.yml` | any | `package-manager`, `node-version`, `bun-version`, `working-directory`, `test-script`, `run-build`, `run-e2e`, `e2e-script` | `contents: read` | JS lint, type-check, tests, build, optional Playwright e2e | Deployment or runtime behavior. Being hardened in a separate PR (pins, permissions) |
| `reusable-validate.yml` | any | `package-manager`, `node-version`, `bun-version`, `working-directory`, `test-script`, `run-build`, `run-e2e`, `e2e-script`, `ubuntu-image` | `contents: read` | JS lint, type-check, tests, build, optional Playwright e2e | Deployment or runtime behavior. Being hardened in a separate PR (pins, permissions) |
| `reusable-pr-title-lint.yml` | `pull_request` | none | `pull-requests: read` | PR title is a Conventional Commit | Commit contents |
| `reusable-codeql.yml` | `push`, `pull_request`, `schedule` | `languages` (required), `build-mode`, `queries` | `contents: read`, `actions: read`, `security-events: write` | CodeQL analysis uploaded to code scanning | Absence of vulnerabilities outside the query suite |
| `reusable-dependency-review.yml` | `pull_request` only | `fail-on-severity`, `deny-licenses` | `contents: read` | No newly introduced dependency crosses the severity or license policy | Runtime reachability of any advisory |
Expand Down Expand Up @@ -91,3 +91,26 @@ Repository files cannot activate rulesets. Follow
[`REQUIRED_CHECKS_ROLLOUT.md`](./REQUIRED_CHECKS_ROLLOUT.md): observe the
emitted check context on a representative pull request, record it, then move
the ruleset from Evaluate to Active.

## Ubuntu migration preflight (2026-10-08)

`reusable-validate.yml` retains its existing `ubuntu-latest` default. The new
`ubuntu-image` input accepts only `ubuntu-latest`, `ubuntu-24.04`, or
`ubuntu-26.04`. Its closed runner mapping cannot select a self-hosted machine,
and unsupported values fail before setup. Use `ubuntu-26.04` to obtain proof
before the October 19–November 19 rollout; use `ubuntu-24.04` only for a documented
compatibility blocker, with a retest and removal condition. Neither image label
freezes weekly image updates.

Governance Contracts calls the exact candidate's local reusable workflow with
an npm fixture on Ubuntu 24 and 26. It installs the locked Playwright browsers
and OS dependencies, then tests JavaScript execution in Chromium, Firefox, and
WebKit. This fixture establishes only the tested workflow path, versions, and
image, not every package manager or downstream application. Existing remote
callers remain pinned to their reviewed revisions until a separately reviewed
pin bump; a shared change does not repair an old SHA automatically.

The reusable workflow now checks out the exact PR head rather than a synthetic
merge commit. This makes its evidence subject explicit; it does not replace any
repository's merge-integration checks or confer approval. Existing runner-pin
policy for hardened governance jobs remains unchanged.
Loading