Skip to content

ci: the combined CI canon (runner allowlist stage, native-live gate shape, fleet-routed ci/docs, post-merge build-gate, release ARM on the protected runner) - #635

Closed
luisleo526 wants to merge 13 commits into
mainfrom
ci/engine-ci-canon-r2
Closed

luisleo526 wants to merge 13 commits into
mainfrom
ci/engine-ci-canon-r2

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

Combines the pending CI changes into one branch, merged with main:

  • refuse a workflow file on a gke-/gkp- label that the runner-group mirror does not list, and list release.yml in that mirror;
  • native-live keeps job-level always() on its single aggregate gate only;
  • CI and docs authorization and the docs build run on the canonical PR fleet (VM jobs, not light hosted jobs);
  • every post-merge proof runs on every push, and build-gate is required;
  • the release ARM tarball is built on the protected runner; the release prebuilt job is scanned for secret expressions in raw text, with its guards pinned;
  • tests follow the combined ci.yml authorization fleet and the release.yml mirror line.

Verification: the full 8-step lab gate passed on this exact head (preflight, release, debug, sanitizers, kernel, docs, parity, subset).

🤖 Generated with Claude Code

luisleo526 and others added 13 commits October 29, 2026 04:38
…oup mirror does not list

The org runner group admits a workflow by its file (`<repo>/.github/workflows/
<file>@refs/heads/main`). Moving jobs onto gke- labels without that entry
queued them for eight hours with no runner and nothing in CI noticed.

Add a `runner-allowlist` preflight stage. It resolves every job's `runs-on`
(scalars, flow and block sequences, matrix rows, env and fromJSON values, files
called with workflow_call) with the readers the runner checks already use, and
fails when a file with a gke- or gkp- job is missing from the committed mirror
`.github/runner-group-allowlist.txt`, or when the mirror is malformed, unsorted,
duplicated, for another repository or not on refs/heads/main. A mirror line for
a file without such a job is only printed. The stage is in the fast and
github-only tiers and in the parallel group.

docs/ci.md: the stage list and the workflow-lint paragraph change. The
sentence "checks the declarations in `.github/actionlint.yaml`." gains a
sentence on adding the mirror line before moving a workflow onto a gke- or
gkp- label, and the sentence beginning "all stages and its existing
event-aware verifier-class selection." now names the new stage. The page keeps
its line count.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The org policy checker allows job-level always() on one proven aggregate per
workflow. native-live.yml had two (native-live-native and native-live-gate).
The native shard summary now uses the cancellable status override
(!cancelled() with authz success), keeps its refusal first step and guards its
remaining run step. native-live-gate directly needs every other job including
the shard matrix, carries no workflow or job environment (the four build values
move into both proof jobs), refuses first, and fails unless every needed result
is exactly success. The preflight pins and tests follow, with mutation rows on
the real workflow text for a failed, skipped or cancelled shard.

docs/ci.md: the native-live paragraph names the non-required override job and
the all-results gate; the page keeps its line count. Sentences replaced, named
here for the doc-revert check: "The `native-live (native)` aggregate requires
all four successful summary proofs;" and "`native-live-gate` requires it and
both sanitizer cells, refusing missing or skipped work; the caller's `build`
aggregate still requires the reusable".

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…fleet

The org policy checker admits no hosted Linux label for this repository, so
ci.yml authz and docs.yml authz and build now use the ordered fleet array
["self-hosted","pf-gcp-pr"] (pull_request workflows never get a GKE pod). The
label is declared in .github/actionlint.yaml, and the exact runner, canon,
metadata and credential checks of the preflight follow, with regression rows on
the real files.

docs/ci.md replaces, on their existing lines, the sentences and rows beginning:
"| ci.yml authz | Hosted authorization context", "Hosted `ubuntu-24.04` runs PR
`authz`, admission, `legs` and `result` jobs, and `docs.yml`'s `build`",
"Hosted exceptions cover PR light jobs", "| ci.yml | Unfiltered
pull_request", "| docs.yml | pull_request, except" and "HOSTED_JOB_RUNNERS pins
hosted jobs". The page keeps its line count.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… jobs

Two sentences in docs/ci.md still described ci.yml authz and docs.yml authz and
build as hosted light jobs after they moved to the pf-gcp-pr fleet: the closing
sentence of the runner paragraph and the fork-pull-request sentence. Both now
name the fleet jobs and say the VM-only fork-refusal hook applies to them. Same
lines, 2016 lines kept.

docs/ci.md replaces the sentences beginning: "These light, secret-free jobs need
no VM-only tools, mounts or caches." and "Fork PRs on hosted runners run without
our secrets, so the VM-only fork-refusal hook is not needed."

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
scripts/ci_docs_only.py classifies every non-pull_request event as code (a
documentation-only push now runs the proofs) and keeps the pull_request output
unchanged. ci-post.yml drops its classifier job, keeps the proof jobs, and uses
one canonical all-success aggregate, build-gate, with no name field and no
workflow or job environment; the four leg gates (build-release-gate,
build-debug-gate, kernel-only-gate, sanitizers-gate) become guarded optional
override jobs and are not required contexts. The branch ruleset must require
build-gate instead of build in the same change as the merge. The preflight pins
and release-gate tests follow, with rows that refuse a restored classifier job, a
pull_request trigger and every omitted aggregate need.

docs/ci.md replaces these sentences, quoted from the page:
"The existing docs-only classification still skips proof jobs on a
documentation-only PR or main push."
"Scheduled and manual runs execute every profile."
"A push to `main` follows the same rule, since nothing downstream needs the
proof jobs to have run on a merged commit:"
"`release.yml` starts only on a manual dispatch and builds and verifies its own
tarballs, `promote-baseline.yml` starts on the push to `main`, resolves"
"A documentation merge therefore does not rebuild; the `ci.yml` run on its
commit is `preflight` and the skip."
"That run is `main`'s latest, the one the README badge shows, so after a
documentation merge the badge reports the documentation guards, and the last"
"Shard jobs and leg aggregates skip together only on classified docs-only
changes."
It also replaces the sentences beginning: The existing docs-only classification;
Both preflight tiers together; Scheduled and manual runs execute; ci.yml
retains authorization and draft event handling; Docs-only and GitHub-only
changes; Failed, cancelled or unclassified work; The seven ci-post.yml light
jobs; The full post call graph expands; A push to main follows; A
documentation merge therefore; That run is main's latest; The
ci-workflow-contract stage pins; Shard jobs and leg aggregates. The post
runner-table fragment Seven light jobs becomes Six light jobs.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The first commit of this change said the branch ruleset must require build-gate
instead of build, and docs/ci.md said the ruleset changes with the rename. Both
are wrong: the engine's merge ruleset requires only pineforge/verify and
pineforge/parity, ci-post.yml runs only on push, schedule and dispatch, and its
contexts never appear on a pull request head. Acting on that sentence would block
every pull request. docs/ci.md now says so, on the same two lines. No ruleset
change is needed or wanted for this change.

docs/ci.md replaces the sentences beginning: "The required aggregate context is
`build-gate` (formerly `build`); the ruleset changes with that rename."

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
release.yml prebuilt moves its linux-aarch64 row from hosted ubuntu-22.04-arm to
the bare protected scalar gkp-8cpu-16gb-arm64 (runner group gke-protected,
main-ref workflows only) and runs under the release environment. Both triples,
the x86_64 runner, the release needs and tag assertions, and the upload
safeguards are unchanged; the Build step now uses the runner's core count
instead of -j 4. The preflight pins and regression rows follow.

docs/ci.md replaces sentences beginning: "Hosted exceptions cover PR light jobs
and the existing hosted release cells", "GKE jobs carry no deployment secrets"
and "Release's Linux arm64 artifact cell keeps its hosted".

Compatibility note: the protected toolchain image's base and glibc version are
not stated in the sources read; the previous Ubuntu 22.04 floor is glibc 2.35.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…and pin its guards

The prebuilt secret scan ran after comment lines were dropped, so a commented
`${{ secrets.NAME }}` inside a run block passed while GitHub still expands the
expression into the step script before the shell discards the comment. The
credential check and the runner policy now also read the raw job text for
`${{ ... }}` expressions. New rows put commented secret expressions (dotted,
indexed, dynamic index, toJSON and a format string containing a closing brace
pair) into the real prebuilt job and expect the finding, keep plain comment
prose and the GITHUB_TOKEN reference clean, change the job permissions in two
more ways than an added id-token, and give the Build step an `if:`, a
continue-on-error and a duplicate step name.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…is a gkp job

release.yml prebuilt runs its linux-aarch64 row on the protected scalar
gkp-8cpu-16gb-arm64, so the runner-allowlist stage requires the file's line
in .github/runner-group-allowlist.txt. The line mirrors the live gke-protected
runner group entry (pineforge-engine release.yml at refs/heads/main).

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…mirror line

Two RunnerGroupAllowlist rows still described the tree before the combined stack:

- test_hosted_and_pf_gcp_workflows_need_no_mirror_entry replaced the literal
  text "runs-on: ubuntu-24.04" in the authz job, but ci.yml's authz job now runs on
  the PR fleet, so the replacement text was absent. It now replaces the job's own
  runs-on line (and asserts that line names pf-gcp-pr) and still tries all four
  runners.
- test_real_tree_mirror_preserves_the_live_group_and_only_two_advisories listed
  nine mirror entries; the mirror now also lists release.yml, whose ARM row is a
  gkp job. The expected list gains release.yml; the two advisories are unchanged.

No expectation is weakened: each row still fails on a wrong mirror or a wrong runner.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
# Conflicts:
#	.github/runner-group-allowlist.txt
#	scripts/test_ci_preflight.py
@luisleo526
luisleo526 marked this pull request as ready for review October 29, 2026 16:46
@luisleo526
luisleo526 marked this pull request as draft October 29, 2026 16:51

@graphify-labs graphify-labs Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.

Not checked: tests were not run; formal verification proved 0 of 12 changed function(s) (4 sampled, not proven, 5 not verified).

Formal verification. 3 change(s) alter behavior, breaking input(s) attached. PR-changed functions: 7/12 verified (0 proven, 4 may-equivalent, 3 distinguished) · 5 not verified (5 vacuous).

Behavior changes: ci\_authz\_only\_findings changes behavior, here is the input that shows it.

The verifier found a concrete input on which ci\_authz\_only\_findings behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"workflow":"'\\\\t\\\\n'"\}, the old code produced \['ci\.yml must retain only the pull\_request trigger', 'ci\.yml must keep its exact unfiltered PR triggers', 'ci\.yml must run on every pull\_request without event filters', 'ci\.yml must retain its… but the new code produces \['ci\.yml must retain only the pull\_request trigger', 'ci\.yml must keep its exact unfiltered PR triggers', 'ci\.yml must run on every pull\_request without event filters', 'ci\.yml must retain its…. Paste that input straight into a regression test.

Behavior changes: native\_workflow\_findings changes behavior, here is the input that shows it.

The verifier found a concrete input on which native\_workflow\_findings behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"native":"' '","curl\_action":"None"\}, the old code produced \['native\-live\.yml must start with the verbatim canon authz job', 'native\-live\.yml must use readable events without pull\_request\_target', 'native\-live\.yml must retain contents: read workflow… but the new code produces \['native\-live\.yml must start with the verbatim canon authz job', 'native\-live\.yml must use readable events without pull\_request\_target', 'native\-live\.yml must retain contents: read workflow…. Paste that input straight into a regression test.

Behavior changes: post\_shard\_workflow\_findings changes behavior, here is the input that shows it.

The verifier found a concrete input on which post\_shard\_workflow\_findings behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"jobs":"\{'n': 0, 's': 'x', 'l': \[1, 2\]\}"\}, the old code produced \['build\-release\-gate must require only its own complete shard matrix and keep its docs\-only scheduling policy', 'build\-debug\-gate must require only its own complete shard matrix and keep its do… but the new code produces \['build\-release\-gate must require only its own complete shard matrix and keep its guarded non\-required override policy', 'build\-debug\-gate must require only its own complete shard matrix and ke… (outputs first differ at character 85). Paste that input straight into a regression test.

Not verified on this run: classify\_paths (vacuous: never exercised), compared (vacuous: never exercised), main (vacuous: never exercised), pipeline\_workflow\_findings (vacuous: never exercised), runner\_findings (vacuous: never exercised).


Graphify review — findings

Removes the docs-only changes classifier from ci-post.yml, so every proof job (build, sanitizers, kernel-only, ARM) now runs on every push with no skip path for documentation-only commits. The per-leg aggregate gates now key on !cancelled() instead of always(), and the shared ccache and release-tag env moves from workflow level onto each job that uses it. Adds the pf-gcp-pr and gkp-8cpu-16gb-arm64 runner labels and the release.yml allowlist entry, and moves the docs and PR workflows onto the self-hosted PR fleet, with the preflight, release-gate and shard-runner tests updated to match.

Worth a look

  • Docs authz gate now runs on self-hosted PR fleet before authorization — .github/workflows/docs.yml:20 · Escalate · high
    • NOT verified (no proof, no reproducing execution) — needs human review
  • Authorization gate now runs on the self-hosted PR fleet before authz decides — .github/workflows/ci.yml:23 · Escalate · medium
    • NOT verified (no proof, no reproducing execution) — needs human review
  • Docs authz and PR docs build moved onto the self-hosted PR fleet — .github/workflows/docs.yml:81 · Escalate · medium
    • NOT verified (no proof, no reproducing execution) — needs human review
  • ARM release runner label likely misspelled ('gkp-' vs 'gke-') — .github/workflows/release.yml:371 · Escalate · medium
    • NOT verified (no proof, no reproducing execution) — needs human review
  • Misspelled GKE runner label is allowlisted — .github/actionlint.yaml:16 · Escalate · medium
    • NOT verified (no proof, no reproducing execution) — needs human review

Review partial — this diff was larger than one review pass covers, so later files were not reviewed; some findings may be missing.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1420 functions depend on the 1411 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: native_workflow_findings() — 15 callers, 12 callees
  • worse: gated_workflow_findings() — 6 callers, 10 callees
  • worse: runner_findings() — 2 callers, 23 callees
  • worse: ci_authz_only_findings() — 2 callers, 8 callees
  • worse: pipeline_workflow_findings() — 1 callers, 13 callees
  • worse: _holds_credentials() — 3 callers, 4 callees

Verification — 1420 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1420 function(s) in the blast radius were not formally verified this run

Docs that may be stale (advisory)

…and 1 more.

Formal verification

Behavior changes: ci\_authz\_only\_findings changes behavior, here is the input that shows it.

The verifier found a concrete input on which ci\_authz\_only\_findings behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"workflow":"'\\\\t\\\\n'"\}, the old code produced \['ci\.yml must retain only the pull\_request trigger', 'ci\.yml must keep its exact unfiltered PR triggers', 'ci\.yml must run on every pull\_request without event filters', 'ci\.yml must retain its… but the new code produces \['ci\.yml must retain only the pull\_request trigger', 'ci\.yml must keep its exact unfiltered PR triggers', 'ci\.yml must run on every pull\_request without event filters', 'ci\.yml must retain its…. Paste that input straight into a regression test.

Behavior changes: native\_workflow\_findings changes behavior, here is the input that shows it.

The verifier found a concrete input on which native\_workflow\_findings behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"native":"' '","curl\_action":"None"\}, the old code produced \['native\-live\.yml must start with the verbatim canon authz job', 'native\-live\.yml must use readable events without pull\_request\_target', 'native\-live\.yml must retain contents: read workflow… but the new code produces \['native\-live\.yml must start with the verbatim canon authz job', 'native\-live\.yml must use readable events without pull\_request\_target', 'native\-live\.yml must retain contents: read workflow…. Paste that input straight into a regression test.

Behavior changes: post\_shard\_workflow\_findings changes behavior, here is the input that shows it.

The verifier found a concrete input on which post\_shard\_workflow\_findings behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"jobs":"\{'n': 0, 's': 'x', 'l': \[1, 2\]\}"\}, the old code produced \['build\-release\-gate must require only its own complete shard matrix and keep its docs\-only scheduling policy', 'build\-debug\-gate must require only its own complete shard matrix and keep its do… but the new code produces \['build\-release\-gate must require only its own complete shard matrix and keep its guarded non\-required override policy', 'build\-debug\-gate must require only its own complete shard matrix and ke… (outputs first differ at character 85). Paste that input straight into a regression test.

Could not verify: Could not verify classify\_paths.

The verifier did not have enough to check classify\_paths, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Could not verify: Could not verify compared.

The verifier did not have enough to check compared, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 18 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly Doubt — names the real obstacle, not a sampling gap)

Could not verify: Could not verify main.

The verifier did not have enough to check main, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 12 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)

No difference found (not proven): No behavior difference found in \_authz\_header (not a proof).

The verifier ran both versions of \_authz\_header on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in \_holds\_credentials (not a proof).

The verifier ran both versions of \_holds\_credentials on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in actionlint\_runner\_label\_findings (not a proof).

The verifier ran both versions of actionlint\_runner\_label\_findings on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in authz\_workflow\_findings (not a proof).

The verifier ran both versions of authz\_workflow\_findings on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify pipeline\_workflow\_findings.

The verifier did not have enough to check pipeline\_workflow\_findings, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 63 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify runner\_findings.

The verifier did not have enough to check runner\_findings, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

· 3 grounded finding(s) anchored inline below; 3 more finding(s) on lines outside this diff (see the check run).

Comment thread scripts/ci_preflight.py
return expressions


def _holds_credentials(workflow: str, body: str, *, effective_permissions: bool = False) -> bool:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — _holds_credentials()

high coupling complexity (Ca·Ce = 12).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread scripts/ci_preflight.py
@@ -3347,52 +3363,38 @@ def _native_profile_job_findings(job: str, name: str, *, sharded: bool) -> list[


def pipeline_workflow_findings(workflow: str, name: str, *, pr: bool) -> list[str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — pipeline_workflow_findings()

fans out to 13 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread scripts/ci_preflight.py
@@ -3803,7 +3796,7 @@ def docs_workflow_self_test(workflow: str, post: str) -> int:


def ci_authz_only_findings(workflow: str) -> list[str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — ci_authz_only_findings()

fans out to 8 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@luisleo526

Copy link
Copy Markdown
Collaborator Author

Superseded by #638 (runner label) and #640 (the rest, without the hosted-runner reroute). Branch kept.

@luisleo526 luisleo526 closed this Oct 29, 2026
tzutoo pushed a commit to tzutoo/pineforge-engine that referenced this pull request Nov 5, 2026
* ci: route CI Linux x64 jobs through the live ci-spot pool

* ci: route corpus parity through the ci-spot build class

* ci: route native live profiles through the ci-spot pool

* ci: route documentation through the public ci-spot class

* ci: route manual baseline promotion through ci-spot

* ci: route release Linux x64 jobs through ci-spot

* ci: require exact ci-spot routing and job labels in preflight

Replace the CR-170 standard-only runner contract with the public and engine-x64-16 fragments, including fork and untrusted-event fallback. Require one matching PF_CI_LABELS string at job scope; pin the Linux-only build and release matrix routing and the unchanged hosted legs. Retain larger-runner, timeout, core-count and reusable-workflow guards.

Update mutation and stage tests for missing, duplicate and incorrect PF_CI_LABELS, wrong label lists, unsafe fragment changes, paid labels and hosted exceptions. No tests, builds, linters or verification scripts run locally under the executor no-run rule.

* docs: record the live ci-spot pool and hosted fallbacks

Supersede the stopgap sentences from 3484a7c, squash-merged as 693fd9e (pineforge-4pass#415, CR-170). Reword the runner-selection and timing assumptions from d093560 (pineforge-4pass#290) and the documentation-only runner sentence from 133f571 (pineforge-4pass#308); preserve the platform limits from 2394ba2 (pineforge-4pass#373).

Record the 2026-10-18 live-pool bulletin, exact fragment and environment labels, class allocation, actor allowlist, hosted matrix legs and event fallbacks. Keep historical timings separate from unmeasured pool performance. No local verification run under the executor no-run rule.

* ci: restore release and deployment jobs to their original hosted runners

Apply the L6 production-credential ruling to Cloudflare docs deployment, baseline promotion and every release job. Restore all three workflow files byte-for-byte from origin/main, including their original hosted labels, and remove pool environment labels. No steps, permissions, timeouts or matrix populations change.

No tests, builds, linters, preflight, corpus verification or workflows run locally under the executor no-run rule. Nothing pushed.

* ci: adopt the b240f6d7 hosted fallback for pool jobs

Match the current pf-ci-spot public and engine-x64-16 fragments from bulletin pineforge-4pass#635. Change the hosted fallback to ubuntu-24.04 for all ten pool job definitions and preserve their matching PF_CI_LABELS, actor checks, fork protection and macOS routing.

No tests, builds, linters, preflight, corpus verification or workflows run locally under the executor no-run rule. Nothing pushed.

* ci: require hosted runners for credential-bearing jobs

Pin the b240f6d7 expressions and original hosted release/deployment routes. Detect non-GITHUB_TOKEN secrets, inherited workflow environment secrets, job or workflow OIDC write permissions and deployment environments. Require hosted routing without PF_CI_LABELS for credentials and current fragments with matching labels for other Linux x64 jobs. Preserve the larger-runner, timeout and core-count guards. Add must-fail credential and old-fallback cases and a must-pass case for the current hosted release workflow.

No tests, builds, linters, preflight, corpus verification or workflows run locally under the executor no-run rule. Nothing pushed.

* docs: record the L6 hosted credential rule and current pool fallback

Reword the pool-allocation, release/deploy routing and fallback sentences introduced by 28fe3466a and carried into f9bee22. Cite pf-ci-spot b240f6d7 and bulletin pineforge-4pass#635, retain the Dependabot and historical policy references, and explain the exact original hosted labels and updated preflight contract.

No tests, builds, linters, preflight, corpus verification or workflows run locally under the executor no-run rule. Nothing pushed.

* test(ci): anchor pool label mutations to job-level env

An unanchored four-space env search matched the tail of step-level env in promotion and release jobs, so the negative fixture never put PF_CI_LABELS in the job environment. Anchor the env and steps keys to complete job-level lines in both label fixture builders.

Assert the injected label at job scope and require the exact hosted-job diagnostic. Keep the existing hosted release baseline and the production guard unchanged. No tests, builds, linters or verification scripts run locally under the executor no-run rule. Nothing pushed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant