Skip to content
Merged
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
echo "::warning::cargo/sccache EAGAIN under fleet pressure; cold retry CARGO_BUILD_JOBS=1 (keep sccache)"
RETRY_LOG=$(mktemp)
if ! ( CARGO_BUILD_JOBS=1 cargo build -p v1-compiler --release --features text_lookup_work_counter --bins ) 2>&1 | tee "$RETRY_LOG"; then
if grep -qiE 'Resource temporarily unavailable|failed to spawn|sccache:' "$RETRY_LOG"; then
if grep -qiE 'Resource temporarily unavailable|failed to spawn|sccache: encountered fatal error' "$RETRY_LOG"; then
echo "::warning::cargo/sccache still failing; retry without RUSTC_WRAPPER"
env -u RUSTC_WRAPPER CARGO_BUILD_JOBS=1 cargo build -p v1-compiler --release --features text_lookup_work_counter --bins || exit 1
else
Expand Down Expand Up @@ -142,7 +142,7 @@ jobs:
echo "::warning::cargo/sccache EAGAIN under fleet pressure; cold retry CARGO_BUILD_JOBS=1 (keep sccache)"
RETRY_LOG=$(mktemp)
if ! ( CARGO_BUILD_JOBS=1 cargo build -p v1-compiler --release --features text_lookup_work_counter --bins ) 2>&1 | tee "$RETRY_LOG"; then
if grep -qiE 'Resource temporarily unavailable|failed to spawn|sccache:' "$RETRY_LOG"; then
if grep -qiE 'Resource temporarily unavailable|failed to spawn|sccache: encountered fatal error' "$RETRY_LOG"; then
echo "::warning::cargo/sccache still failing; retry without RUSTC_WRAPPER"
env -u RUSTC_WRAPPER CARGO_BUILD_JOBS=1 cargo build -p v1-compiler --release --features text_lookup_work_counter --bins || exit 1
else
Expand All @@ -166,7 +166,7 @@ jobs:
echo "::warning::cargo/sccache EAGAIN under fleet pressure; cold retry CARGO_BUILD_JOBS=1 (keep sccache)"
RETRY_LOG=$(mktemp)
if ! ( CARGO_BUILD_JOBS=1 "$ROOT/target/release/gunbc" run --source-root dsl --source-root src/v2 --entry dsl/tools/rust_gates_ci.dag --function main ) 2>&1 | tee "$RETRY_LOG"; then
if grep -qiE 'Resource temporarily unavailable|failed to spawn|sccache:' "$RETRY_LOG"; then
if grep -qiE 'Resource temporarily unavailable|failed to spawn|sccache: encountered fatal error' "$RETRY_LOG"; then
echo "::warning::cargo/sccache still failing; retry without RUSTC_WRAPPER"
env -u RUSTC_WRAPPER CARGO_BUILD_JOBS=1 "$ROOT/target/release/gunbc" run --source-root dsl --source-root src/v2 --entry dsl/tools/rust_gates_ci.dag --function main || exit 1
else
Expand Down
30 changes: 30 additions & 0 deletions docs/plans/ci-merge-freshness.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,33 @@ re-leak silently excused, a §5 fail-open). It is **complementary** and explicit
3×-red fix — claiming so would be a false-green about a false-green. The forward completeness checks
already exist; the reverse-staleness gate is the genuine residual lens; the freshness requirement in §3 is
the actual 3×-red killer.

## 6. The infra-vs-structural verdict ships DORMANT (scaffold + written enable-gate)

#5615 (`gunbc.ci_failure_class`) builds the construction-first half that makes the §3 freshness
requirement *viable*: a classifier that partitions a floor red into `Infra | Structural` and a
fail-closed verdict surface `floor_exit_blocks_merge(ProcessExit) -> Bool` (success / unambiguous-infra ⇒
non-blocking; everything else, including a bare exit-137, ⇒ Structural ⇒ blocks). Without it, requiring
green-on-main would block merges on the environmental reds (sccache/EAGAIN/runner contention) that are
*not* the defect the freshness rule targets.

**#5615 ships this verdict surface DORMANT.** It is built and witnessed but **nothing consumes
`floor_exit_blocks_merge` to block any merge** — it is dormant by construction, since the freshness
*mechanism* (§3, the operator merge-queue that would actually re-run vs current `main` and consume the
verdict) is explicitly out of #5615's scope. This is a §6 **scaffold-with-dissolution-trigger**: the
verdict exists; flipping it to *enforcing* is the dissolution event, and it is gated — **do not wire the
merge-queue to block on this verdict until BOTH hold:**

- **(a) the cross-run clustering-reclassification consumer has landed.** A *lone* exit-137 fails closed
to `Structural` (a single `ProcessExit` cannot tell a co-residence OOM from a structural
memory-runaway). If the green-on-main mechanism went live first, a genuine co-residence OOM on a
re-run would read `Structural ⇒ block ⇒ re-run ⇒ OOM ⇒ …` — the livelock deep-otter-528 warned of.
The clustering consumer (simultaneous multi-job death + host-uptime reset in a tight window) is what
*reclassifies* such a clustered OOM back to `Infra`; it is a **hard prerequisite** for enforcement.
- **(b) cap-recovery has reclaimed session slots.** Green-on-main re-runs *increase* floor memory load
during exactly the contention window this lockdown is containing; enforcing before the host has slack
worsens the OOM pressure the verdict is meant to triage.

Until both clear, the verdict is a loaded mechanism left unwired on purpose: a future worker must **not**
flip it to enforcing early. The trigger is decidable (both consumers either exist on `main` or do not),
so this is a wall-after-grounding, not a forever-ratchet.
2 changes: 2 additions & 0 deletions dsl/extdeps/cache/sccache.dag
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {

data sccache_local_id: CacheInterfaceId = "sccache_local"

data sccache_fatal_error_log_fragment: String = "sccache: encountered fatal error"

data sccache_local_facts: CacheInterfaceCatalogFacts = {
identity: sccache_local_id
backing_surface: "sccache-local-disk"
Expand Down
22 changes: 22 additions & 0 deletions dsl/extdeps/github/ci_runner.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module extdeps.github.ci_runner

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/actions/hosting-your-own-runners/managing-self-hosted-runners/monitoring-and-troubleshooting-self-hosted-runners"
}
}

type RunnerLifecycleSignal
= ShutdownReceived
| OperationCanceled

fn runner_lifecycle_log_fragment(signal: RunnerLifecycleSignal) -> String {
match signal {
ShutdownReceived => "The runner has received a shutdown signal"
OperationCanceled => "The operation was canceled"
}
}
29 changes: 29 additions & 0 deletions dsl/extdeps/runtime/errno.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module extdeps.runtime.errno

import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }

data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html"
}
}

type PosixErrno
= EAGAIN
| ENOMEM

fn errno_number(e: PosixErrno) -> Int {
match e {
EAGAIN => 11
ENOMEM => 12
}
}

fn errno_strerror(e: PosixErrno) -> String {
match e {
EAGAIN => "Resource temporarily unavailable"
ENOMEM => "Cannot allocate memory"
}
}
22 changes: 22 additions & 0 deletions dsl/extdeps/rust/std_process.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module extdeps.rust.std_process

import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }

data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "doc.rust-lang.org/std/process/struct.Command.html"
}
}

type StdProcessSpawnOutcome
= SpawnSucceeded
| SpawnFailed

fn spawn_outcome_log_fragment(outcome: StdProcessSpawnOutcome) -> String {
match outcome {
SpawnSucceeded => ""
SpawnFailed => "failed to spawn"
}
}
111 changes: 111 additions & 0 deletions dsl/gunbc/ci_failure_class.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
module gunbc.ci_failure_class

import std.process { ProcessExit, ExitSuccess, ExitFailure }
import extdeps.runtime.errno { PosixErrno, EAGAIN, errno_strerror }
import extdeps.rust.std_process { StdProcessSpawnOutcome, SpawnFailed, spawn_outcome_log_fragment }
import extdeps.cache.sccache { sccache_fatal_error_log_fragment }
import extdeps.github.ci_runner { RunnerLifecycleSignal, ShutdownReceived, runner_lifecycle_log_fragment }

type InfraSignature
= ResourceTemporarilyUnavailable
| ProcessSpawnFailure
| SccacheFatalError
| RunnerLost

type SignatureSite
= BuildLog
| JobSignal

type FloorFailureClass
= Infra { signature: InfraSignature }
| Structural { reason: String }

data verdict_infra_signatures: List<InfraSignature> = [
ResourceTemporarilyUnavailable,
ProcessSpawnFailure,
SccacheFatalError,
RunnerLost
]

fn infra_signature_match_substring(sig: InfraSignature) -> String {
match sig {
ResourceTemporarilyUnavailable => errno_strerror(e: EAGAIN)
ProcessSpawnFailure => spawn_outcome_log_fragment(outcome: SpawnFailed)
SccacheFatalError => sccache_fatal_error_log_fragment
RunnerLost => runner_lifecycle_log_fragment(signal: ShutdownReceived)
}
}

fn infra_signature_origin(sig: InfraSignature) -> String {
match sig {
ResourceTemporarilyUnavailable => "extdeps.runtime.errno (POSIX EAGAIN, errno 11)"
ProcessSpawnFailure => "extdeps.rust.std_process (Command::spawn failure)"
SccacheFatalError => "extdeps.cache.sccache (server fatal error)"
RunnerLost => "extdeps.github.ci_runner (runner shutdown signal)"
}
}

fn infra_signature_site(sig: InfraSignature) -> SignatureSite {
match sig {
ResourceTemporarilyUnavailable => BuildLog
ProcessSpawnFailure => BuildLog
SccacheFatalError => BuildLog
RunnerLost => JobSignal
}
}

fn build_retry_infra_signatures() -> List<InfraSignature> {
fold(verdict_infra_signatures, init: [], f: fn(acc, sig) {
match infra_signature_site(sig: sig) {
BuildLog => concat(acc, [sig])
JobSignal => acc
}
})
}

fn infra_retry_grep_alternation() -> String {
fold(build_retry_infra_signatures(), init: "", f: fn(acc, sig) {
let sub = infra_signature_match_substring(sig: sig)
if acc == "" { sub } else { concat(concat(acc, "|"), sub) }
})
}

fn reason_matches_infra_signature(reason: String, sig: InfraSignature) -> Bool {
string_contains(s: reason, pattern: infra_signature_match_substring(sig: sig))
}

fn classify_failure_reason(reason: String) -> FloorFailureClass {
fold(verdict_infra_signatures, init: Structural { reason: reason }, f: fn(acc, sig) {
match acc {
Infra { signature: _ } => acc
Structural { reason: r } =>
if reason_matches_infra_signature(reason: r, sig: sig) {
Infra { signature: sig }
} else {
acc
}
}
})
}

fn classify_floor_exit(e: ProcessExit) -> FloorFailureClass {
match e {
ExitSuccess => Structural { reason: "" }
ExitFailure { code: _, reason: r } => classify_failure_reason(reason: r)
}
}

fn floor_failure_blocks_merge(c: FloorFailureClass) -> Bool {
match c {
Infra { signature: _ } => false
Structural { reason: _ } => true
}
}

fn floor_exit_blocks_merge(e: ProcessExit) -> Bool {
match e {
ExitSuccess => false
ExitFailure { code: _, reason: _ } =>
floor_failure_blocks_merge(c: classify_floor_exit(e: e))
}
}
5 changes: 3 additions & 2 deletions dsl/gunbc/ci_spec.dag
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import gunbc.ci_layer_roots {
}
import tools.build_step { BuildArtifact, verifications_script }
import extdeps.languages.bash.program { Concat, lit, var_ref }
import gunbc.ci_failure_class { infra_retry_grep_alternation }

type Gate
= RustMonolithGate
Expand Down Expand Up @@ -82,7 +83,7 @@ fn ci_cargo_eagain_retry_core(command: String) -> String {
concat(
concat(
concat(
" if grep -qiE 'Resource temporarily unavailable|failed to spawn|sccache: encountered fatal error' \"$BUILD_LOG\"; then\n",
concat(concat(" if grep -qiE '", infra_retry_grep_alternation()), "' \"$BUILD_LOG\"; then\n"),
" echo \"::warning::cargo/sccache EAGAIN under fleet pressure; cold retry CARGO_BUILD_JOBS=1 (keep sccache)\"\n"
),
concat(
Expand All @@ -93,7 +94,7 @@ fn ci_cargo_eagain_retry_core(command: String) -> String {
concat(
concat(
concat(
" if grep -qiE 'Resource temporarily unavailable|failed to spawn|sccache:' \"$RETRY_LOG\"; then\n",
concat(concat(" if grep -qiE '", infra_retry_grep_alternation()), "' \"$RETRY_LOG\"; then\n"),
" echo \"::warning::cargo/sccache still failing; retry without RUSTC_WRAPPER\"\n"
),
concat(" env -u RUSTC_WRAPPER CARGO_BUILD_JOBS=1 ", concat(command, " || exit 1\n"))
Expand Down
Loading
Loading