Skip to content
90 changes: 88 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
name: ci
on:
workflow_dispatch:
inputs:
expected_healed_sha:
description: Immutable healed commit required by an auto-heal revalidation run
required: false
type: string
push:
branches: [main]
pull_request:
Expand All @@ -25,6 +30,23 @@ jobs:
uses: actions/checkout@v5
with:
fetch-depth: 0
ref: ${{ inputs.expected_healed_sha || github.sha }}
- name: Refuse a heal revalidation whose run subject or checkout does not name the expected healed SHA
run: |
EXPECTED_HEALED_SHA="${{ inputs.expected_healed_sha }}"
RUN_SUBJECT_HEAD="${{ github.sha }}"
if [ -n "$EXPECTED_HEALED_SHA" ]; then
ACTUAL_HEAD="$(git rev-parse HEAD)"
if ! [ "$RUN_SUBJECT_HEAD" = "$EXPECTED_HEALED_SHA" ]; then
echo "::error::heal revalidation refused: workflow run subject $RUN_SUBJECT_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA"
exit 1
fi
if ! [ "$ACTUAL_HEAD" = "$EXPECTED_HEALED_SHA" ]; then
echo "::error::heal revalidation refused: checkout head $ACTUAL_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA"
exit 1
fi
echo "heal revalidation preflight: checkout names expected healed head $EXPECTED_HEALED_SHA"
fi
- name: Isolate toolchain dirs
run: |
rm -rf "$RUNNER_TEMP/rustup" "$RUNNER_TEMP/cargo"
Expand Down Expand Up @@ -123,6 +145,23 @@ jobs:
uses: actions/checkout@v5
with:
fetch-depth: 0
ref: ${{ inputs.expected_healed_sha || github.sha }}
- name: Refuse a heal revalidation whose run subject or checkout does not name the expected healed SHA
run: |
EXPECTED_HEALED_SHA="${{ inputs.expected_healed_sha }}"
RUN_SUBJECT_HEAD="${{ github.sha }}"
if [ -n "$EXPECTED_HEALED_SHA" ]; then
ACTUAL_HEAD="$(git rev-parse HEAD)"
if ! [ "$RUN_SUBJECT_HEAD" = "$EXPECTED_HEALED_SHA" ]; then
echo "::error::heal revalidation refused: workflow run subject $RUN_SUBJECT_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA"
exit 1
fi
if ! [ "$ACTUAL_HEAD" = "$EXPECTED_HEALED_SHA" ]; then
echo "::error::heal revalidation refused: checkout head $ACTUAL_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA"
exit 1
fi
echo "heal revalidation preflight: checkout names expected healed head $EXPECTED_HEALED_SHA"
fi
- name: Setup Rust (floor gate toolchain — batch-4 emit_host/self_host gates need cargo/rustfmt; no isolated rustup)
uses: actions-rust-lang/setup-rust-toolchain@v1.16.0
with:
Expand Down Expand Up @@ -182,6 +221,23 @@ jobs:
uses: actions/checkout@v5
with:
fetch-depth: 0
ref: ${{ inputs.expected_healed_sha || github.sha }}
- name: Refuse a heal revalidation whose run subject or checkout does not name the expected healed SHA
run: |
EXPECTED_HEALED_SHA="${{ inputs.expected_healed_sha }}"
RUN_SUBJECT_HEAD="${{ github.sha }}"
if [ -n "$EXPECTED_HEALED_SHA" ]; then
ACTUAL_HEAD="$(git rev-parse HEAD)"
if ! [ "$RUN_SUBJECT_HEAD" = "$EXPECTED_HEALED_SHA" ]; then
echo "::error::heal revalidation refused: workflow run subject $RUN_SUBJECT_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA"
exit 1
fi
if ! [ "$ACTUAL_HEAD" = "$EXPECTED_HEALED_SHA" ]; then
echo "::error::heal revalidation refused: checkout head $ACTUAL_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA"
exit 1
fi
echo "heal revalidation preflight: checkout names expected healed head $EXPECTED_HEALED_SHA"
fi
- name: Setup Rust (floor gate toolchain — batch-4 emit_host/self_host gates need cargo/rustfmt; no isolated rustup)
uses: actions-rust-lang/setup-rust-toolchain@v1.16.0
with:
Expand Down Expand Up @@ -244,6 +300,23 @@ jobs:
uses: actions/checkout@v5
with:
fetch-depth: 0
ref: ${{ inputs.expected_healed_sha || github.sha }}
- name: Refuse a heal revalidation whose run subject or checkout does not name the expected healed SHA
run: |
EXPECTED_HEALED_SHA="${{ inputs.expected_healed_sha }}"
RUN_SUBJECT_HEAD="${{ github.sha }}"
if [ -n "$EXPECTED_HEALED_SHA" ]; then
ACTUAL_HEAD="$(git rev-parse HEAD)"
if ! [ "$RUN_SUBJECT_HEAD" = "$EXPECTED_HEALED_SHA" ]; then
echo "::error::heal revalidation refused: workflow run subject $RUN_SUBJECT_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA"
exit 1
fi
if ! [ "$ACTUAL_HEAD" = "$EXPECTED_HEALED_SHA" ]; then
echo "::error::heal revalidation refused: checkout head $ACTUAL_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA"
exit 1
fi
echo "heal revalidation preflight: checkout names expected healed head $EXPECTED_HEALED_SHA"
fi
- name: Download release-bins artifact
uses: actions/download-artifact@v4
with:
Expand All @@ -269,6 +342,7 @@ jobs:
if: github.event_name == 'pull_request'
permissions:
contents: write
actions: write
steps:
- name: Checkout triggering branch head (heal pushes the regeneration back to this branch)
uses: actions/checkout@v5
Expand Down Expand Up @@ -409,11 +483,23 @@ jobs:
if [ -e "docs/plans/fleet-acceptance-criteria.md" ]; then git add "docs/plans/fleet-acceptance-criteria.md"; else echo "chore-heal: registered generated artifact absent on this tree, not staging: docs/plans/fleet-acceptance-criteria.md"; fi
if [ -e "docs/plans/structural-quadratic-wall-coverage-audit.md" ]; then git add "docs/plans/structural-quadratic-wall-coverage-audit.md"; else echo "chore-heal: registered generated artifact absent on this tree, not staging: docs/plans/structural-quadratic-wall-coverage-audit.md"; fi
if [ -e "docs/plans/ci-performance-tanking-evidence.md" ]; then git add "docs/plans/ci-performance-tanking-evidence.md"; else echo "chore-heal: registered generated artifact absent on this tree, not staging: docs/plans/ci-performance-tanking-evidence.md"; fi
PRIOR_HEAD=$(git rev-parse HEAD)
if git diff --cached --quiet; then
echo "chore-heal: no generated-artifact drift on this tree; nothing to commit"
echo "HealNoChange head=$PRIOR_HEAD"
exit 0
else
CHANGED_ARTIFACTS=$(git diff --cached --name-only)
git commit -m "chore: regenerate drifted generated artifacts (ci auto-heal)"
HEALED_HEAD=$(git rev-parse HEAD)
git push origin HEAD:${{ github.head_ref || github.ref_name }}
echo "chore-heal: pushed regenerated generated artifacts to the triggering branch"
echo "HealProduced prior_head=$PRIOR_HEAD healed_head=$HEALED_HEAD changed_artifacts=$CHANGED_ARTIFACTS"
ROOT=$(git rev-parse --show-toplevel)
if ! GUNBC_HEALED_HEAD="$HEALED_HEAD" GUNBC_HEAL_BRANCH_REF="${{ github.head_ref || github.ref_name }}" "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/tools/ci_heal_dispatch.dag --function main
then
echo "::error::HealDispatchRefused healed_head=$HEALED_HEAD cause=CreateWorkflowDispatchFailed" >&2
exit 1
fi
echo "SupersededByHealedHead prior_head=$PRIOR_HEAD healed_head=$HEALED_HEAD; revalidation-required"
exit 1
fi
timeout-minutes: 5
72 changes: 72 additions & 0 deletions dag/extdeps/github/workflows.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
module extdeps.github.workflows

import extdeps.github.github { default_api_base }
import extdeps.github.errors { GitHubErrorShape }
import std.credentials { EnvVar }
import std.types { CommitSha, NonEmptyStr }
import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }

data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "docs.github.com/en/rest/actions/workflows#create-a-workflow-dispatch-event"
}
}

data workflows_api_version: NonEmptyStr = "2026-03-10"

data create_dispatch_version_fidelity_note: String = "Create Workflow Dispatch has two live response contracts across GitHub REST versions. Requests without X-GitHub-Api-Version default to 2022-11-28 and return 204 No Content. Version 2026-03-10 returns 200 with workflow_run_id, run_url, and html_url. This service explicitly pins 2026-03-10 in the transport header, so WorkflowDispatchReceipt is the cited response rather than a fabricated body projected from an unversioned 204. Authority: docs.github.com/en/rest/about-the-rest-api/api-versions and the Create a workflow dispatch event response schema, read 2026-08-01."

type WorkflowDispatchReceipt {
workflow_run_id: Int
run_url: NonEmptyStr
html_url: NonEmptyStr
}

service github.Workflows {
config {
endpoint: default_api_base
auth: Bearer
auth_source: EnvVar { name: "GITHUB_TOKEN" }

rate_limit: { requests: 5000, per: hour, scope: core }

retry: { max_attempts: 1, backoff: none, retry_on: [] }
}

operation CreateDispatch {
input {
owner: String
repo: String
workflow_id: String
ref: String
expected_healed_sha: CommitSha
}
output {
workflow_run_id: Int
run_url: NonEmptyStr
html_url: NonEmptyStr
}
transport rest {
method: POST,
path: "/repos/\{owner\}/\{repo\}/actions/workflows/\{workflow_id\}/dispatches",
headers: { "X-GitHub-Api-Version": workflows_api_version },
body: {
ref: ref,
inputs: { expected_healed_sha: expected_healed_sha }
}
}
response {
200 => WorkflowDispatchReceipt
401 => GitHubErrorShape
403 => GitHubErrorShape
404 => GitHubErrorShape
422 => GitHubErrorShape
429 => GitHubErrorShape
5xx => GitHubErrorShape
}
}
}

data create_dispatch_retry_policy_note: String = "CreateDispatch is intentionally single-attempt. Once the heal push has created a new subject, a dispatch refusal must be visible as HealDispatchRefused and leave revalidation required; silently retrying a permission, rate-limit, or server refusal into apparent success would erase the classification operators need. The owning workflow may be retriggered deliberately after diagnosis, but this operation never assumes that the GITHUB_TOKEN push created a run."
52 changes: 50 additions & 2 deletions dag/extdeps/languages/yaml/gha_workflow.dag
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import extdeps.cron.schedule_model { render_cron_schedule }
import extdeps.github.actions {
Workflow, Job, Step, RunStep, UsesStep, ActionRef,
WorkflowTrigger, Push, PullRequest, WorkflowDispatch, Schedule, MergeGroup,
DispatchInput, DispatchInputType, InputString, InputBoolean, InputChoice, InputEnvironment,
PullRequestActivity, Opened, Synchronize, Reopened, ReadyForReview, Closed,
WorkflowPermissions, PermissionLevel, PermRead, PermWrite, PermNone,
ConcurrencySpec, ConcurrencyScalar, ConcurrencyMappingQueueMax, ConcurrencyMappingQueueNotMax,
Expand Down Expand Up @@ -88,10 +89,57 @@ fn push_trigger_yaml(branches: List<String>, paths: List<String>) -> YamlKeyValu
kv(key: "push", value: yaml_mapping(entries: entries))
}

fn dispatch_input_type_label(t: DispatchInputType) -> String {
match t {
InputString => "string"
InputBoolean => "boolean"
InputChoice => "choice"
InputEnvironment => "environment"
}
}

fn dispatch_input_optional_string(key: String, value: String?) -> List<YamlKeyValue> {
match value {
Present { value: s } => [kv(key: key, value: yaml_string(s: s))]
Absent => []
}
}

fn dispatch_input_yaml(input: DispatchInput) -> YamlKeyValue {
kv(
key: input.name,
value: yaml_mapping(entries: concat(
dispatch_input_optional_string(key: "description", value: input.description),
concat(
[kv(key: "required", value: yaml_bool(b: input.required))],
concat(
dispatch_input_optional_string(key: "default", value: input.default),
[kv(key: "type", value: yaml_string(s: dispatch_input_type_label(t: input.type)))]
)
)
))
)
}

fn workflow_dispatch_yaml(inputs: List<DispatchInput>) -> YamlValue {
if length(inputs) == 0 {
YamlNull
} else {
yaml_mapping(entries: [
kv(
key: "inputs",
value: yaml_mapping(entries: map(inputs, input => dispatch_input_yaml(input: input)))
)
])
}
}

data workflow_dispatch_input_projection_note: String = "WorkflowDispatch.inputs was modeled in extdeps.github.actions but this serializer discarded the entire list and emitted workflow_dispatch: null. That specification-without-execution gap is now closed: every modeled input is projected into the workflow syntax, while the empty list deliberately retains the compact null form. The CI heal revalidation input is the first production consumer and its witness reads the projected YamlValue rather than trusting generated text."

fn workflow_trigger_entry(trigger: WorkflowTrigger) -> YamlKeyValue {
match trigger {
WorkflowDispatch { inputs: _ } =>
kv(key: "workflow_dispatch", value: YamlNull)
WorkflowDispatch { inputs: inputs } =>
kv(key: "workflow_dispatch", value: workflow_dispatch_yaml(inputs: inputs))
Push { branches: bs, paths: ps } =>
push_trigger_yaml(branches: bs, paths: ps)
PullRequest { branches: bs, types: ts } =>
Expand Down
20 changes: 17 additions & 3 deletions dag/gunbc/ci_materialization_gate.dag
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
module gunbc.ci_materialization_gate


data ci_materialization_gate_note: String = "The live binding: the ladder run over the ACTUAL emitted workflow (gunbc.ci_workflow data row), never a fixture copy of it. Demands, exemption identities, and fresh-effect scripts are all resolved against the same authority the ci.yml is emitted from, so a workflow edit that introduces cross-job duplication without a covering provider reds the enrolled witness in the same CI run that would have paid the duplication. Post-fold reality (ci_regen job folded into the ci job 2026-07-11): the workflow now has a SINGLE building job, so the release build is one demand -> AcceptedSingleRecompute (state 5: a single undeclared demand needs no provider, a cache there is dead weight), and the ladder holds WITH AND WITHOUT the sccache provider. The cross-job duplication the ladder refutes is now carried as the discriminating RED control by the synthetic placement_grain witness (two independently-building jobs -> AuthoredDuplication/RefusedNoProvider), which the live witness's demand==1 / discharged==0 pins guard against reintroducing. Fresh effects: the floor cgroup-peak measurement scripts (a memory.peak read is a new fact each run, never memoizable — the rust_tests cgroup-peak measurement was the prior subject; it retired with the rust_tests job 2026-07-11, and the ci-job floor peak pre/post reads are the same measurement category) and every deploy stage script (a deploy is the effect being the use); all resolved by projection, not copied strings."
data ci_materialization_gate_note: String = "The live binding: the ladder run over the ACTUAL emitted workflow (gunbc.ci_workflow data row), never a fixture copy of it. Demands, exemption identities, and fresh-effect scripts are all resolved against the same authority the ci.yml is emitted from, so a workflow edit that introduces cross-job duplication without a covering provider reds the enrolled witness in the same CI run that would have paid the duplication. Post-fold reality (ci_regen job folded into the ci job 2026-07-11): the workflow now has a SINGLE building job, so the release build is one demand -> AcceptedSingleRecompute (state 5: a single undeclared demand needs no provider, a cache there is dead weight), and the ladder holds WITH AND WITHOUT the sccache provider. The cross-job duplication the ladder refutes is now carried as the discriminating RED control by the synthetic placement_grain witness (two independently-building jobs -> AuthoredDuplication/RefusedNoProvider), which the live witness's demand==1 / discharged==0 pins guard against reintroducing. Fresh effects: the floor cgroup-peak measurement scripts (a memory.peak read is a new fact each run, never memoizable — the rust_tests cgroup-peak measurement was the prior subject; it retired with the rust_tests job 2026-07-11, and the ci-job floor peak pre/post reads are the same measurement category), every deploy stage script (a deploy is the effect being the use), and the heal-revalidation exact-head preflight (git HEAD is a new checkout fact in each isolated job and must be observed there, never memoized or discharged from another job); all resolved by projection, not copied strings."

fn ci_fresh_effect_scripts() -> List<String> {
concat(
[step_run_script(s: ci_floor_peak_pre_step()), step_run_script(s: ci_floor_peak_post_step())],
[
step_run_script(s: ci_floor_peak_pre_step()),
step_run_script(s: ci_floor_peak_post_step()),
ci_heal_revalidation_preflight_script(),
],
map(gunbc_ci_deploy_stages, s => gunbc_ci_deploy_invoke(stage: s))
)
}
Expand Down Expand Up @@ -131,6 +134,17 @@ fn ci_exempt_fresh_verdict_count() -> Int {
|> count()
}

fn ci_heal_revalidation_preflight_is_fresh_effect() -> Bool {
let identity = ci_heal_revalidation_preflight_script()
let demand_count = ci_materialization_demands()
|> filter(d => d.identity == identity)
|> count()
let verdict_count = ci_materialization_verdicts()
|> filter(v => verdict_is_exempt_fresh(v: v, identity: identity))
|> count()
demand_count > 1 && verdict_count == 1
}

fn ci_uses_steps_skipped_count() -> Int {
workflow_uses_steps_count(w: ci_workflow)
}
Expand Down
Loading