diff --git a/.github/workflows/governance-contracts.yml b/.github/workflows/governance-contracts.yml index 559024c..3e438cb 100644 --- a/.github/workflows/governance-contracts.yml +++ b/.github/workflows/governance-contracts.yml @@ -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 diff --git a/.github/workflows/reusable-validate.yml b/.github/workflows/reusable-validate.yml index a235a4e..70637b1 100644 --- a/.github/workflows/reusable-validate.yml +++ b/.github/workflows/reusable-validate.yml @@ -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. @@ -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 }} @@ -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' diff --git a/.quirk/evidence/ubuntu26-preflight-9f7720814e8b.json b/.quirk/evidence/ubuntu26-preflight-9f7720814e8b.json new file mode 100644 index 0000000..b35c17c --- /dev/null +++ b/.quirk/evidence/ubuntu26-preflight-9f7720814e8b.json @@ -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" +} diff --git a/.quirk/evidence/ubuntu26-preflight-repair-dc7ada466704.json b/.quirk/evidence/ubuntu26-preflight-repair-dc7ada466704.json new file mode 100644 index 0000000..ce3bfda --- /dev/null +++ b/.quirk/evidence/ubuntu26-preflight-repair-dc7ada466704.json @@ -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" +} + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5fd0c15 --- /dev/null +++ b/CHANGELOG.md @@ -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. diff --git a/docs/governance/REUSABLE_WORKFLOWS.md b/docs/governance/REUSABLE_WORKFLOWS.md index 8616d31..509d4e6 100644 --- a/docs/governance/REUSABLE_WORKFLOWS.md +++ b/docs/governance/REUSABLE_WORKFLOWS.md @@ -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 | @@ -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. diff --git a/docs/governance/UBUNTU_26_AUDIT.md b/docs/governance/UBUNTU_26_AUDIT.md new file mode 100644 index 0000000..3c2fc93 --- /dev/null +++ b/docs/governance/UBUNTU_26_AUDIT.md @@ -0,0 +1,45 @@ +# Ubuntu 26 runner migration audit — 2026-10-08 + +Status: observed snapshot; authority effect: none. This document records source inspection, not an admission or compatibility certificate. + +GitHub schedules ubuntu-latest migration for October 19–November 19, 2026. [Official announcement](https://github.blog/changelog/2026-09-17-ubuntu-26-generally-available-and-latest-migration/) and [runner-image migration issue](https://github.com/actions/runner-images/issues/14748). [Playwright release notes](https://playwright.dev/docs/release-notes#version-161) establish Ubuntu 26 support in 1.61. + +This public projection includes only public repository workflow sources. Private repository findings belong in their own review surfaces. The accessible portfolio audit inspected 50 repository records, 20 nonempty trees, and 50 workflow files, including actual pinned reusable targets. Empty repository records were not bootstrapped. + +## Public source inventory + +| Repository | Observed main head | Direct latest workflows | Assessment | +| --- | --- | --- | --- | +| project-scaffold | `eae6dc9aabb4c63dfe97f72f0cb565266daa709b` | ci.yml, deps-audit.yml | Playwright 1.62.1 supports Ubuntu 26; native package/build proof and E2E still needed. deps-audit installs Bun; no specific OS defect established. | +| quirk-generator | `8b5d979e3c4cb42f076d2bf5836267e290bf873f` | ci.yml | Playwright 1.63.0 supports Ubuntu 26; no frozen install in current workflow. Validate resolved dependency tree before compatibility claims. | +| .github | `eb889adf3009951de873890103199600f4aad20f` | quirk-semantic-governance.yml, reusable-validate.yml | Shared browser path and implicit-system-Python semantic path warrant preflight; 14 other shared workflows already retain Ubuntu 24. | +| quirk-feed | `a338ea3e0158437167cd875bb4655b67c3b9dc68` | ci.yml | Playwright 1.63.0 supports Ubuntu 26; frozen dependencies plus native SQLite, build, browser dependency installation and isolated E2E warrant explicit probe. | +| quirk-os | `03f70e1a790bac09294fe0f31a3c5f0d0fb4418e` | agent-reliability.yml | Python 3.12 setup and pinned eval requirements reduce preinstalled-runtime exposure. Add exact-head Ubuntu 26 fixtures; no Python incompatibility established. Other direct Linux jobs retain Ubuntu 24. | +| quirk-core | `b3162b0cd6dcc6c07570f240ca33c711ed860f0b` | quirk-contracts-conformance.yml | Python 3.12 explicitly installed; jsonschema range floats. Open PR #12 owns locking dependencies; do not duplicate it. | +| greenfield | `6e6d4b425fe9082d469493d64a5b8e12b15dc9da` | None | No direct latest exposure observed. | + +## Shared infrastructure and dependency boundaries + +- VERIFIED: no observed default-branch caller uses reusable-validate.yml. Its optional Playwright path remains available infrastructure; the new fixture tests it without inventing a consumer. +- VERIFIED: semantic-workflow consumers pin commits 1263f5008838f743b16ee218046e26dac5c9edd8, 92e5b2d928f25d0fedae87aaad2ef764a806065d, 52eed09e4fbb9a08d9d0eba4fc101483241f3965, and 571ecd4f9e1086d3f3cd4c5e6d4a40ae8ea3748d. All inspected target versions select ubuntu-latest. Their code pin does not pin the OS. Existing caller pins remain untouched. +- VERIFIED: current runner inventory lists default Python 3.12.3 on Ubuntu 24 versus 3.14.4 on Ubuntu 26; default Node 22.23.3 versus 24.21.0; GCC and OpenSSL versions also change. Explicit setup actions reduce runtime drift, but do not isolate system libraries or native builds. Image lists are moving snapshots, not immutable guarantees. +- INFERRED: semantic governance, which invokes system Python without setup-python, merits a Python 3.14 probe. No failing deprecated API has been established; retaining all its callers on Ubuntu 24 would be premature. +- VERIFIED: Playwright installs bundled browsers and OS dependencies. Updated system Chrome/Firefox alone is not a defect for this path. Compatibility of installed dependency packages and browser launch must still be tested. +- VERIFIED: SQLite native module builds and framework native binaries are material preflight targets. No unavailable apt package, hard-coded OpenSSL/ICU version, or demonstrated Ubuntu 26 failure was identified in the inspected public workflow commands. + +## Migration versus selective Ubuntu 24 retention + +| Path | Choose when | Evidence and exit condition | +| --- | --- | --- | +| Keep existing latest label | Exact-head Ubuntu 26 application checks pass | Record image, resolved versions, dependency installation and full app tests; current results remain limited to that image | +| Explicit Ubuntu 26 preflight | Support exists but app proof is missing | Read-only additive job, timeout, no secrets or side effects; preserve existing required-check contexts | +| Temporary Ubuntu 24 retention | Reproducible OS-specific failure or critical unvalidated gate before rollout | Scope to affected job/caller; record failure and independent Ubuntu 24 pass; remove after repaired exact-head Ubuntu 26 pass | + +No blanket pin is proposed. Existing Ubuntu 24 governance choices are preserved. No ruleset readback or changes, approvals, merges, deploys, capability grants, canonical changes, or external-service tests are claimed. Review and human merge authority remain separate from evidence. + +## Candidate changes and validation limits + +Shared reusable validation receives a closed image choice and an exact-head npm/Playwright fixture on Ubuntu 24 and 26. Feed and agent reliability receive additive Ubuntu 26 jobs running existing application/fixture commands. Existing check contexts remain unchanged. Revert the added probe jobs (and the shared optional input) to undo the change. Changelogs accompany each candidate. + +Local tests and receipt gates are supplemental. Hosted run IDs, candidate head, image version, dependency output and actual tests must be resolved from the PR before any compatibility claim. Shared fixture success does not certify every package manager, older pinned shared workflow, private app, native dependency tree, or downstream consumer. An additive Ubuntu 26 system-Python job tests the current semantic registry and governance regressions without setup-python. Its result does not retroactively certify older pinned semantic-policy code. Remaining application preflights and pinned-policy revisions remain gaps until independently run. + diff --git a/fixtures/ubuntu-preflight/.gitignore b/fixtures/ubuntu-preflight/.gitignore new file mode 100644 index 0000000..f0a232b --- /dev/null +++ b/fixtures/ubuntu-preflight/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +test-results/ +playwright-report/ diff --git a/fixtures/ubuntu-preflight/browser.spec.ts b/fixtures/ubuntu-preflight/browser.spec.ts new file mode 100644 index 0000000..a64631e --- /dev/null +++ b/fixtures/ubuntu-preflight/browser.spec.ts @@ -0,0 +1,9 @@ +import { readFileSync } from 'node:fs'; +import { test, expect } from '@playwright/test'; + +test('built fixture executes JavaScript in each bundled browser', async ({ page }) => { + await page.setContent(readFileSync('dist/index.html', 'utf8')); + await expect(page).toHaveTitle('Ubuntu preflight'); + await page.getByRole('button', { name: 'Count: 0' }).click(); + await expect(page.getByRole('button')).toHaveText('Count: 1'); +}); diff --git a/fixtures/ubuntu-preflight/build.mjs b/fixtures/ubuntu-preflight/build.mjs new file mode 100644 index 0000000..30e5ac3 --- /dev/null +++ b/fixtures/ubuntu-preflight/build.mjs @@ -0,0 +1,15 @@ +import { mkdirSync, writeFileSync } from 'node:fs'; +import { pathToFileURL } from 'node:url'; + +export function render(title) { + const escaped = title.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>'); + return `${escaped}`; +} + +if (import.meta.url === pathToFileURL(process.argv[1]).href) { + mkdirSync('dist', { recursive: true }); + writeFileSync('dist/index.html', render('Ubuntu preflight')); +} diff --git a/fixtures/ubuntu-preflight/build.test.mjs b/fixtures/ubuntu-preflight/build.test.mjs new file mode 100644 index 0000000..1ab9739 --- /dev/null +++ b/fixtures/ubuntu-preflight/build.test.mjs @@ -0,0 +1,9 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { render } from './build.mjs'; + +test('untrusted title cannot terminate the title element', () => { + const html = render('&'); + assert.ok(html.includes('</title><script>bad()</script>&')); + assert.equal(html.match(/