From 521fb382cce51c419839bf2961bc746d129a890a Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 9 Sep 2026 23:14:32 +0100 Subject: [PATCH 01/12] fix(triage): identify contradictory descriptile and empty workflow gates --- crates/squabble-fight/src/lib.rs | 2 + crates/squabble-fight/src/workflows.rs | 102 +++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/crates/squabble-fight/src/lib.rs b/crates/squabble-fight/src/lib.rs index 7b284de..f1b9923 100644 --- a/crates/squabble-fight/src/lib.rs +++ b/crates/squabble-fight/src/lib.rs @@ -327,6 +327,8 @@ mod tests { job_names: vec![], reusable_repos: reusable.iter().map(|s| s.to_string()).collect(), path_filtered, + retired_descriptile_policy: false, + empty_jobs: false, kind, } } diff --git a/crates/squabble-fight/src/workflows.rs b/crates/squabble-fight/src/workflows.rs index 4b310dd..0f259a2 100644 --- a/crates/squabble-fight/src/workflows.rs +++ b/crates/squabble-fight/src/workflows.rs @@ -45,6 +45,10 @@ pub struct WorkflowInfo { pub reusable_repos: Vec, /// True if the file declares an `on.*.paths` trigger filter. pub path_filtered: bool, + /// Executable policy contradicts the canonical descriptile location. + pub retired_descriptile_policy: bool, + /// A bare jobs block contains only whitespace or commented examples. + pub empty_jobs: bool, pub kind: WorkflowKind, } @@ -118,6 +122,25 @@ impl WorkflowFacts { let name = check.required_context.as_str(); let w = self.find_emitting(name)?; + if w.retired_descriptile_policy { + return Some(Move::FlagNonFunctionalGate { + check: name.to_string(), + evidence: format!( + "`{}` requires a retired descriptile path; reconcile its policy with .machine_readable/descriptiles/ and SD004 before retrying", + w.file + ), + }); + } + if w.empty_jobs { + return Some(Move::FlagNonFunctionalGate { + check: name.to_string(), + evidence: format!( + "`{}` contains only commented jobs; GitHub cannot create a check from this template", + w.file + ), + }); + } + // 1. Owned upstream: the job delegates to a reusable workflow living in // another repo. The fix belongs there, not on this PR. if let Some(repo) = w.reusable_repos.iter().find(|r| r.as_str() != slug) { @@ -252,10 +275,52 @@ fn parse_workflow(file: &str, text: &str) -> WorkflowInfo { job_names, reusable_repos, path_filtered, + retired_descriptile_policy: has_retired_descriptile_policy(text), + empty_jobs: has_empty_jobs(text), kind, } } +fn has_retired_descriptile_policy(text: &str) -> bool { + text.lines().any(|line| { + let line = line.trim(); + !line.starts_with('#') + && (line.contains("-f ") || line.contains("-e ") || line.contains("check_file ")) + && [ + "STATE", + "META", + "ECOSYSTEM", + "AGENTIC", + "NEUROSYM", + "PLAYBOOK", + "ANCHOR", + ] + .iter() + .any(|name| { + line.contains(&format!(".machine_readable/{name}.a2ml")) + || line.contains(&format!(".machine_readable/6a2/{name}.a2ml")) + }) + }) +} + +fn has_empty_jobs(text: &str) -> bool { + let mut in_jobs = false; + for line in text.lines() { + let trimmed = line.trim(); + if trimmed.is_empty() || trimmed.starts_with('#') { + continue; + } + if in_jobs { + // A non-comment indented value is outside this narrow diagnosis. + return !line.starts_with(char::is_whitespace); + } + if line == "jobs:" { + in_jobs = true; + } + } + in_jobs +} + /// Extract `owner/repo` from a reusable-workflow `uses:` line, i.e. one whose /// target contains `/.github/workflows/`. Action uses (`owner/repo@sha`) are /// ignored — they are not gate-emitting reusables. @@ -536,4 +601,41 @@ jobs: ) .is_none()); } + #[test] + fn retired_policy_is_a_gate_conflict_with_a_canonical_negative_control() { + let bad = "name: Compliance\njobs:\n compliance:\n steps:\n - run: test -f .machine_readable/STATE.a2ml\n"; + let parsed = parse_workflow("compliance.yml", bad); + assert!(parsed.retired_descriptile_policy); + let facts = WorkflowFacts { + workflows: vec![parsed], + }; + assert!(matches!( + facts.classify(&req("compliance", CheckRun::Missing), "owner/repo"), + Some(Move::FlagNonFunctionalGate { .. }) + )); + let fixed = bad.replace( + ".machine_readable/STATE", + ".machine_readable/descriptiles/STATE", + ); + assert!(!parse_workflow("compliance.yml", &fixed).retired_descriptile_policy); + assert!(!has_retired_descriptile_policy( + "# test -f .machine_readable/STATE.a2ml" + )); + } + + #[test] + fn commented_jobs_cannot_supply_a_check() { + let template = "name: E2E\njobs:\n # test:\n # runs-on: ubuntu-latest\n"; + let parsed = parse_workflow("e2e.yml", template); + assert!(parsed.empty_jobs); + let facts = WorkflowFacts { + workflows: vec![parsed], + }; + assert!(matches!( + facts.classify(&req("E2E", CheckRun::Missing), "owner/repo"), + Some(Move::FlagNonFunctionalGate { .. }) + )); + assert!(!has_empty_jobs("jobs:\n test:\n steps: []\n")); + assert!(!has_empty_jobs("# jobs:\n")); + } } From c9ccbaac61750f32d5d646c1a09a37ba6fdd261e Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 9 Sep 2026 23:38:45 +0100 Subject: [PATCH 02/12] fix(build): remove duplicate CLI import and satisfy workspace formatting --- crates/squabble-cli/src/fight.rs | 3 +-- crates/squabble-core/src/polarity.rs | 16 ++++++++++++---- crates/squabble-fight/src/gate_triage.rs | 11 +++++++++-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/crates/squabble-cli/src/fight.rs b/crates/squabble-cli/src/fight.rs index 0455ad2..96b818f 100644 --- a/crates/squabble-cli/src/fight.rs +++ b/crates/squabble-cli/src/fight.rs @@ -13,9 +13,8 @@ use crate::fetch; use squabble_core::gate::Gate; use squabble_core::moves::Move; use squabble_core::outcome::Escalation; -use squabble_core::polarity::Evidence; use squabble_core::outcome::Outcome; -use squabble_core::polarity::{Applicability, Evidence, RepoDeclaration}; +use squabble_core::polarity::Evidence; use squabble_fight::context::RepoContext; use std::path::PathBuf; use std::process::ExitCode; diff --git a/crates/squabble-core/src/polarity.rs b/crates/squabble-core/src/polarity.rs index eead5fa..eafe953 100644 --- a/crates/squabble-core/src/polarity.rs +++ b/crates/squabble-core/src/polarity.rs @@ -183,7 +183,9 @@ pub struct SignatureSet { impl SignatureSet { /// Build a set from `(scanner, signature)` pairs. pub fn new(entries: Vec) -> Self { - SignatureSet { signatures: entries } + SignatureSet { + signatures: entries, + } } /// A one-scanner set — the shape the host had before multi-scanner @@ -687,7 +689,7 @@ mod tests { #[test] fn declared_and_unmatched_is_not_applicable() { let v = classify( - &[], // uninspectable; Axis 0 answers before steps are consulted + &[], // uninspectable; Axis 0 answers before steps are consulted &sigset(), &Applicability { runs_for_operator_types: vec!["platform_maintainer".into()], @@ -940,8 +942,14 @@ mod tests { assert!(set.is_usable()); assert_eq!( set.matching(&[ - StepOutcome { name: "Run Hypatia scan".into(), conclusion: StepConclusion::Skipped }, - StepOutcome { name: "Create stub findings".into(), conclusion: StepConclusion::Success }, + StepOutcome { + name: "Run Hypatia scan".into(), + conclusion: StepConclusion::Skipped + }, + StepOutcome { + name: "Create stub findings".into(), + conclusion: StepConclusion::Success + }, ]) .map(|s| s.scanner.as_str()), Some("hypatia") diff --git a/crates/squabble-fight/src/gate_triage.rs b/crates/squabble-fight/src/gate_triage.rs index 6675f5b..a86ba00 100644 --- a/crates/squabble-fight/src/gate_triage.rs +++ b/crates/squabble-fight/src/gate_triage.rs @@ -254,7 +254,10 @@ census = "33/33" // per key, every scanner would get hypatia's steps. let set = parse_signatures(DIRECTIVE); let pa = &set.signatures[1].signature; - assert_eq!(pa.skipped_steps, vec!["Run panic-attack assail".to_string()]); + assert_eq!( + pa.skipped_steps, + vec!["Run panic-attack assail".to_string()] + ); assert!( !pa.skipped_steps.contains(&"Run Hypatia scan".to_string()), "panic-attack must not inherit hypatia's steps" @@ -329,7 +332,11 @@ signature-success-steps = ["Create stub findings (when Hypatia unavailable)"] both.push_str("\nsignature-skipped-steps = [\"Run Hypatia scan\"]\n"); both.push_str("signature-success-steps = [\"Create stub findings\"]\n"); let set = parse_signatures(&both); - assert_eq!(set.signatures.len(), 2, "the legacy pair must not add a third"); + assert_eq!( + set.signatures.len(), + 2, + "the legacy pair must not add a third" + ); } // ---- ground truth ------------------------------------------------------ From 188ba43e01752a5fc117521720ba9209e41800ab Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Thu, 10 Sep 2026 01:02:22 +0100 Subject: [PATCH 03/12] fix(fight): accept GitHub commit statuses and avoid quoted policy false positives --- crates/squabble-cli/src/fetch.rs | 33 ++++++++++++++ crates/squabble-fight/src/workflows.rs | 63 +++++++++++++++++++------- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/crates/squabble-cli/src/fetch.rs b/crates/squabble-cli/src/fetch.rs index 0ec3c47..6545421 100644 --- a/crates/squabble-cli/src/fetch.rs +++ b/crates/squabble-cli/src/fetch.rs @@ -24,8 +24,12 @@ use std::process::Command; #[derive(Debug, Deserialize)] struct RollupEntry { + // GitHub's rollup is a union: commit statuses use context/state, while + // check runs use name/conclusion. CodeRabbit commonly supplies a status. + #[serde(alias = "context")] name: String, status: Option, + #[serde(alias = "state")] conclusion: Option, /// `https://github.com/O/R/actions/runs//job/` — the only place /// the rollup exposes a job id, which is what the jobs API needs. @@ -270,6 +274,35 @@ pub fn run_with_greens(slug: &str, pr: &str) -> Result<(Gate, Vec), mod tests { use super::*; + #[test] + fn mixed_check_runs_and_commit_statuses_parse_without_losing_failures() { + let json = r#"{"baseRefName":"main","statusCheckRollup":[ + {"__typename":"CheckRun","name":"CI","status":"COMPLETED","conclusion":"SUCCESS"}, + {"__typename":"StatusContext","context":"CodeRabbit","state":"SUCCESS"}, + {"__typename":"StatusContext","context":"External review","state":"FAILURE"}, + {"__typename":"StatusContext","context":"Pending review","state":"PENDING"} + ]}"#; + let parsed: PrView = serde_json::from_str(json).expect("both GitHub rollup variants"); + assert_eq!(parsed.status_check_rollup.len(), 4); + assert_eq!( + parse_rollup(&parsed.status_check_rollup[0]), + CheckRun::Passed + ); + assert_eq!( + parse_rollup(&parsed.status_check_rollup[1]), + CheckRun::Passed + ); + assert_eq!( + parse_rollup(&parsed.status_check_rollup[2]), + CheckRun::Failed + ); + assert_eq!( + parse_rollup(&parsed.status_check_rollup[3]), + CheckRun::Pending + ); + assert!(greens_from_rollup(&parsed.status_check_rollup).is_empty()); + } + fn entry(name: &str, status: Option<&str>, conclusion: Option<&str>) -> RollupEntry { RollupEntry { name: name.to_string(), diff --git a/crates/squabble-fight/src/workflows.rs b/crates/squabble-fight/src/workflows.rs index 0f259a2..21f035f 100644 --- a/crates/squabble-fight/src/workflows.rs +++ b/crates/squabble-fight/src/workflows.rs @@ -284,22 +284,50 @@ fn parse_workflow(file: &str, text: &str) -> WorkflowInfo { fn has_retired_descriptile_policy(text: &str) -> bool { text.lines().any(|line| { let line = line.trim(); - !line.starts_with('#') - && (line.contains("-f ") || line.contains("-e ") || line.contains("check_file ")) - && [ - "STATE", - "META", - "ECOSYSTEM", - "AGENTIC", - "NEUROSYM", - "PLAYBOOK", - "ANCHOR", - ] - .iter() - .any(|name| { - line.contains(&format!(".machine_readable/{name}.a2ml")) - || line.contains(&format!(".machine_readable/6a2/{name}.a2ml")) - }) + let line = line + .strip_prefix("- run:") + .or_else(|| line.strip_prefix("run:")) + .unwrap_or(line) + .trim(); + let mut words = line.split_whitespace().peekable(); + if matches!(words.peek(), Some(&"if" | &"elif" | &"while" | &"until")) { + words.next(); + } + if words.peek() == Some(&"!") { + words.next(); + } + let target = match words.next() { + Some("check_file") => words.next(), + Some("test" | "[" | "[[") => { + if words.peek() == Some(&"!") { + words.next(); + } + if matches!(words.next(), Some("-f" | "-e")) { + words.next() + } else { + None + } + } + _ => None, + }; + let Some(target) = target else { + return false; + }; + let target = target.trim_end_matches(';').trim_matches(['\'', '"']); + [ + "STATE", + "META", + "ECOSYSTEM", + "AGENTIC", + "NEUROSYM", + "PLAYBOOK", + "ANCHOR", + ] + .iter() + .any(|name| { + target == format!(".machine_readable/{name}.a2ml") + || target == format!(".machine_readable/6a2/{name}.a2ml") + }) }) } @@ -621,6 +649,9 @@ jobs: assert!(!has_retired_descriptile_policy( "# test -f .machine_readable/STATE.a2ml" )); + assert!(!has_retired_descriptile_policy( + "- run: echo 'test -f .machine_readable/STATE.a2ml'" + )); } #[test] From 2ad1902decf9275253dc3c6c0d2470e3b880d8d6 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Thu, 10 Sep 2026 01:58:35 +0100 Subject: [PATCH 04/12] fix(fight): decode YAML run scalars and commented job headers --- Cargo.lock | 42 ++++++++++++++++++++++++ Cargo.toml | 1 + crates/squabble-fight/Cargo.toml | 1 + crates/squabble-fight/src/workflows.rs | 44 +++++++++++++++++++++++--- 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7657665..2ffb575 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,12 @@ dependencies = [ "syn", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -183,6 +189,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "http" version = "1.4.2" @@ -366,6 +378,16 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itoa" version = "1.0.18" @@ -595,6 +617,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml_ng" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4db627b98b36d4203a7b458cf3573730f2bb591b28871d916dfa9efabfd41f" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "shlex" version = "2.0.1" @@ -665,6 +700,7 @@ dependencies = [ name = "squabble-fight" version = "0.1.0" dependencies = [ + "serde_yaml_ng", "squabble-core", ] @@ -817,6 +853,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index b6c6edd..f22686c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,5 @@ squabble-core = { path = "crates/squabble-core" } squabble-fight = { path = "crates/squabble-fight" } serde = { version = "1", features = ["derive"] } serde_json = "1" +serde_yaml_ng = "0.10" thiserror = "2" diff --git a/crates/squabble-fight/Cargo.toml b/crates/squabble-fight/Cargo.toml index e42827a..c336f42 100644 --- a/crates/squabble-fight/Cargo.toml +++ b/crates/squabble-fight/Cargo.toml @@ -12,3 +12,4 @@ rust-version.workspace = true [dependencies] squabble-core = { workspace = true } +serde_yaml_ng = { workspace = true } diff --git a/crates/squabble-fight/src/workflows.rs b/crates/squabble-fight/src/workflows.rs index 21f035f..0b0f534 100644 --- a/crates/squabble-fight/src/workflows.rs +++ b/crates/squabble-fight/src/workflows.rs @@ -284,11 +284,26 @@ fn parse_workflow(file: &str, text: &str) -> WorkflowInfo { fn has_retired_descriptile_policy(text: &str) -> bool { text.lines().any(|line| { let line = line.trim(); - let line = line + let scalar = line .strip_prefix("- run:") - .or_else(|| line.strip_prefix("run:")) - .unwrap_or(line) - .trim(); + .or_else(|| line.strip_prefix("run:")); + // Decode YAML quoting before interpreting the shell command. Stripping + // delimiters alone loses escapes and can turn quoted prose into code. + let decoded; + let line = if let Some(scalar) = scalar { + let scalar = scalar.trim(); + if scalar.starts_with(['\'', '"']) { + let Ok(value) = serde_yaml_ng::from_str::(scalar) else { + return false; + }; + decoded = value; + decoded.trim() + } else { + scalar + } + } else { + line + }; let mut words = line.split_whitespace().peekable(); if matches!(words.peek(), Some(&"if" | &"elif" | &"while" | &"until")) { words.next(); @@ -342,7 +357,10 @@ fn has_empty_jobs(text: &str) -> bool { // A non-comment indented value is outside this narrow diagnosis. return !line.starts_with(char::is_whitespace); } - if line == "jobs:" { + if line.strip_prefix("jobs:").is_some_and(|rest| { + let rest = rest.trim(); + rest.is_empty() || rest.starts_with('#') + }) { in_jobs = true; } } @@ -652,6 +670,17 @@ jobs: assert!(!has_retired_descriptile_policy( "- run: echo 'test -f .machine_readable/STATE.a2ml'" )); + for scalar in [ + r#"run: "test -f .machine_readable/STATE.a2ml""#, + r#"run: 'test -f .machine_readable/STATE.a2ml'"#, + r#"run: "test\x20-f\u0020.machine_readable/STATE.a2ml""#, + r#"run: "test -f \".machine_readable/STATE.a2ml\"""#, + ] { + assert!(has_retired_descriptile_policy(scalar), "{scalar}"); + } + assert!(!has_retired_descriptile_policy( + r#"- run: "printf '%s\n' '# test -f .machine_readable/STATE.a2ml'""# + )); } #[test] @@ -668,5 +697,10 @@ jobs: )); assert!(!has_empty_jobs("jobs:\n test:\n steps: []\n")); assert!(!has_empty_jobs("# jobs:\n")); + assert!(has_empty_jobs("jobs: # template\n # test:\n")); + assert!(!has_empty_jobs( + "jobs: # real jobs\n test:\n steps: []\n" + )); + assert!(!has_empty_jobs("jobs: { test: {} }\n")); } } From 9f08d792a3e97189da515bac3bcb303c4e5b1462 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Thu, 10 Sep 2026 02:52:31 +0100 Subject: [PATCH 05/12] fix(ci): retain Hypatia warning counts and current source contracts --- .github/workflows/actions.lock | 134 ++++++++++++++++++++ .github/workflows/codeql.yml | 7 +- .github/workflows/container-build.yml | 3 +- .github/workflows/dependabot-automerge.yml | 5 +- .github/workflows/dogfood-gate.yml | 13 +- .github/workflows/e2e.yml | 3 +- .github/workflows/estate-rules.yml | 3 +- .github/workflows/governance.yml | 1 + .github/workflows/guix-policy.yml | 3 +- .github/workflows/hypatia-scan.yml | 3 +- .github/workflows/instant-sync.yml | 1 + .github/workflows/label-triage.yml | 1 + .github/workflows/labels.yml | 1 + .github/workflows/mirror.yml | 1 + .github/workflows/openssf-compliance.yml | 3 +- .github/workflows/pages.yml | 9 +- .github/workflows/push-email-notify.yml | 3 +- .github/workflows/quality.yml | 7 +- .github/workflows/release.yml | 13 +- .github/workflows/rhodibot.yml | 3 +- .github/workflows/runtime-policy.yml | 3 +- .github/workflows/rust-ci.yml | 1 + .github/workflows/scorecard.yml | 3 +- .github/workflows/secret-scanner.yml | 1 + .github/workflows/security-policy.yml | 3 +- .github/workflows/sonarqube.yml | 5 +- .github/workflows/static-analysis-gate.yml | 29 +++-- .github/workflows/wellknown-enforcement.yml | 3 +- .github/workflows/workflow-linter.yml | 3 +- 29 files changed, 215 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/actions.lock diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock new file mode 100644 index 0000000..5251abc --- /dev/null +++ b/.github/workflows/actions.lock @@ -0,0 +1,134 @@ +# This file is machine-generated by `gh actions-lock`. +# Do not edit by hand; run `gh actions-lock` to update. +# Docs: https://gh.io/actions-lockfile +version: 'v0.0.2' +workflows: + '.github/workflows/codeql.yml': + - 'actions/checkout@v7.0.1' + - 'github/codeql-action@v4.37.9' + '.github/workflows/container-build.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/dependabot-automerge.yml': + - 'dependabot/fetch-metadata@v3.1.0' + '.github/workflows/dogfood-gate.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/e2e.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/estate-rules.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/guix-policy.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/openssf-compliance.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/pages.yml': + - 'actions/checkout@v7.0.1' + - 'actions/deploy-pages@v5.0.1' + - 'actions/upload-pages-artifact@v5.0.0' + '.github/workflows/push-email-notify.yml': + - 'hyperpolymath/smtp-notify-action@v0.2.0' + '.github/workflows/quality.yml': + - 'actions/checkout@v7.0.1' + - 'editorconfig-checker/action-editorconfig-checker@v3.0.0' + '.github/workflows/release.yml': + - 'actions/attest-build-provenance@v4.2.2' + - 'actions/checkout@v7.0.1' + - 'actions/upload-artifact@v7.0.1' + - 'softprops/action-gh-release@v3.0.3' + '.github/workflows/rhodibot.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/runtime-policy.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/security-policy.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/sonarqube.yml': + - 'actions/checkout@v7.0.1' + - 'sonarsource/sonarqube-scan-action@v8.2.1' + '.github/workflows/static-analysis-gate.yml': + - 'actions/checkout@v7.0.1' + - 'actions/download-artifact@v8.0.1' + - 'actions/upload-artifact@v7.0.1' + - 'erlef/setup-beam@v1.24.1' + '.github/workflows/wellknown-enforcement.yml': + - 'actions/checkout@v7.0.1' + '.github/workflows/workflow-linter.yml': + - 'actions/checkout@v7.0.1' +dependencies: + 'actions/attest-build-provenance@v4.2.2': + ref: 'v4.2.2' + commit: 'sha1-4d101475d8b20a2381f78447822ac1eab6504dd8' + owner_id: 44036562 + repo_id: 760702757 + uses: + - 'actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d' + 'actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d': + ref: 'v4.2.1' + commit: 'sha1-508db95dd578ae2727ebd6217d5ba78e4fbda05d' + owner_id: 44036562 + repo_id: 760701061 + 'actions/checkout@v7.0.1': + ref: 'v7.0.1' + commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' + owner_id: 44036562 + repo_id: 197814629 + 'actions/deploy-pages@v5.0.1': + ref: 'v5.0.1' + commit: 'sha1-368f82528645a54fb793d4d04e342629a3f51346' + owner_id: 44036562 + repo_id: 438112499 + 'actions/download-artifact@v8.0.1': + ref: 'v8.0.1' + commit: 'sha1-3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c' + owner_id: 44036562 + repo_id: 192626254 + 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f': + ref: 'v7.0.0' + commit: 'sha1-bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' + owner_id: 44036562 + repo_id: 192625955 + 'actions/upload-artifact@v7.0.1': + ref: 'v7.0.1' + commit: 'sha1-043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' + owner_id: 44036562 + repo_id: 192625955 + 'actions/upload-pages-artifact@v5.0.0': + ref: 'v5.0.0' + commit: 'sha1-fc324d3547104276b827a68afc52ff2a11cc49c9' + owner_id: 44036562 + repo_id: 496012378 + uses: + - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' + 'dependabot/fetch-metadata@v3.1.0': + ref: 'v3.1.0' + commit: 'sha1-25dd0e34f4fe68f24cc83900b1fe3fe149efef98' + owner_id: 27347476 + repo_id: 371068214 + 'editorconfig-checker/action-editorconfig-checker@v3.0.0': + ref: 'v3.0.0' + commit: 'sha1-51f63319f592f97930c73d9c46184d20bd206393' + owner_id: 26415196 + repo_id: 297874902 + 'erlef/setup-beam@v1.24.1': + ref: 'v1.24.1' + commit: 'sha1-54075bcc5e249e4758d363f27d099f55d843f124' + owner_id: 47606891 + repo_id: 331103973 + 'github/codeql-action@v4.37.9': + ref: 'v4.37.9' + commit: 'sha1-cdf488f595d80d6e07e03d4674febd5ab45fa938' + owner_id: 9919 + repo_id: 259445878 + 'hyperpolymath/smtp-notify-action@v0.2.0': + ref: 'v0.2.0' + commit: 'sha1-ede1191ef6ff3ac02c4f4d9efdf837ee517e11d7' + owner_id: 6759885 + repo_id: 1352485172 + 'softprops/action-gh-release@v3.0.3': + ref: 'v3.0.3' + commit: 'sha1-efb35369e0ad2afab669f228072c1b0d510eae64' + owner_id: 2242 + repo_id: 204253808 + 'sonarsource/sonarqube-scan-action@v8.2.1': + ref: 'v8.2.1' + commit: 'sha1-22918119ff8e1ca75a623e15c8296b6ea4fbe28f' + owner_id: 545988 + repo_id: 366408409 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a71856c..65d84d9 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: CodeQL Security Analysis on: @@ -34,13 +35,13 @@ jobs: build-mode: none steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Initialize CodeQL - uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v3 + uses: github/codeql-action/init@v4.37.9 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v3 + uses: github/codeql-action/analyze@v4.37.9 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml index e578cd2..fa6c089 100644 --- a/.github/workflows/container-build.yml +++ b/.github/workflows/container-build.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: container build on: @@ -31,7 +32,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 - name: Tooling check run: | diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 652ace4..27c43a2 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # # dependabot-automerge.yml — enable GitHub's native auto-merge on @@ -49,13 +50,13 @@ permissions: jobs: automerge: # Only run for PRs actually authored by Dependabot. - if: github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]' + if: github.actor_id == '49699333' && github.event.pull_request.user.login == 'dependabot[bot]' runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Fetch Dependabot metadata id: meta - uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 + uses: dependabot/fetch-metadata@v3.1.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} # --- Policy gate ------------------------------------------------------- diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index 1358cd1..ebe3202 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # @@ -30,7 +31,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Check for A2ML files id: detect @@ -71,7 +72,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Check for K9 files id: detect @@ -117,7 +118,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Scan for invisible characters id: lint @@ -213,7 +214,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Check for Groove manifest id: groove @@ -272,7 +273,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Check and validate eclexiaiser manifest id: eclex @@ -324,7 +325,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Generate dogfooding scorecard run: | diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 39da946..b3b60a2 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # @@ -43,7 +44,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Run E2E harness run: | if [ -f tests/e2e.sh ]; then diff --git a/.github/workflows/estate-rules.yml b/.github/workflows/estate-rules.yml index 140ca4c..38b523f 100644 --- a/.github/workflows/estate-rules.yml +++ b/.github/workflows/estate-rules.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # @@ -26,7 +27,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Root shape allowlist run: bash scripts/check-root-shape.sh . - name: AsciiDoc by default (no .md under docs/) diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 724d9ed..0133d28 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Governance diff --git a/.github/workflows/guix-policy.yml b/.github/workflows/guix-policy.yml index f8e3fff..2ddf657 100644 --- a/.github/workflows/guix-policy.yml +++ b/.github/workflows/guix-policy.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Guix Package Policy on: @@ -21,7 +22,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 - name: Enforce Guix-only package policy run: | # Guix is the sole package manager estate-wide. Guix is BANNED. diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index fe0ed88..a51dbd3 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # # Standalone Hypatia security scan (push / PR / weekly). This is NOT a duplicate @@ -26,4 +27,4 @@ permissions: jobs: scan: - uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@8f2ee50841e216cd8c192eeb68953118190f105c + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 diff --git a/.github/workflows/instant-sync.yml b/.github/workflows/instant-sync.yml index 3c67981..bf3f085 100644 --- a/.github/workflows/instant-sync.yml +++ b/.github/workflows/instant-sync.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Instant Forge Sync - Triggers propagation to all forges on push/release name: Instant Sync diff --git a/.github/workflows/label-triage.yml b/.github/workflows/label-triage.yml index 9886e92..814a192 100644 --- a/.github/workflows/label-triage.yml +++ b/.github/workflows/label-triage.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Label Triage diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index c80b676..83ab941 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Labels diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 9a96808..ebec909 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Mirror to Git Forges on: diff --git a/.github/workflows/openssf-compliance.yml b/.github/workflows/openssf-compliance.yml index 5061adb..0eba1ed 100644 --- a/.github/workflows/openssf-compliance.yml +++ b/.github/workflows/openssf-compliance.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # OpenSSF Best Practices compliance gate — blocks PRs and pushes that lack # required files or still contain unfilled placeholder tokens. @@ -21,7 +22,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 with: persist-credentials: false - name: Check SECURITY.md exists and has substance diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index aa6a989..71d0e28 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: GitHub Pages (Ddraig SSG) on: @@ -20,9 +21,9 @@ jobs: image: ghcr.io/stefan-hoeck/idris2-pack@sha256:f0758996a931fb35d9ecb1de273c4d59dabe2a09b433afc7e357f65a08b7e1ff steps: - name: Checkout Site - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + uses: actions/checkout@v7.0.1 - name: Checkout Ddraig SSG - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + uses: actions/checkout@v7.0.1 with: repository: hyperpolymath/ddraig-ssg path: .ddraig-ssg @@ -39,7 +40,7 @@ jobs: fi ./.ddraig-ssg/build/exec/ddraig build src _site https://hyperpolymath.github.io/${GITHUB_REPOSITORY#*/} - name: Upload artifact - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5 + uses: actions/upload-pages-artifact@v5.0.0 with: path: '_site' deploy: @@ -52,4 +53,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5 + uses: actions/deploy-pages@v5.0.1 diff --git a/.github/workflows/push-email-notify.yml b/.github/workflows/push-email-notify.yml index 0689291..676f498 100644 --- a/.github/workflows/push-email-notify.yml +++ b/.github/workflows/push-email-notify.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Dormant push-email notification. ARMED by setting the repo variable # PUSH_EMAIL_ENABLED=true (the single on/off switch). Addresses are pre-filled; @@ -39,7 +40,7 @@ jobs: timeout-minutes: 5 steps: - name: Send push notification email - uses: hyperpolymath/smtp-notify-action@ede1191ef6ff3ac02c4f4d9efdf837ee517e11d7 # v0.2.0 + uses: hyperpolymath/smtp-notify-action@v0.2.0 with: server_address: ${{ secrets.SMTP_HOST }} server_port: ${{ secrets.SMTP_PORT }} diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 669fd2b..5f56e93 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Code Quality on: @@ -22,7 +23,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 - name: Check file permissions run: | find . -type f -perm /111 -name "*.sh" | head -10 || true @@ -34,7 +35,7 @@ jobs: run: | find . -type f -size +1M -not -path "./.git/*" | head -10 || echo "No large files" - name: EditorConfig check - uses: editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393 # v3.0.0 + uses: editorconfig-checker/action-editorconfig-checker@v3.0.0 continue-on-error: true docs: runs-on: ubuntu-latest @@ -42,7 +43,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 - name: Check documentation run: | MISSING="" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fa0bb6b..6631a1f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # @@ -23,7 +24,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 - name: Detect project type and build id: build run: | @@ -85,7 +86,7 @@ jobs: changelog: ${{ steps.cliff.outputs.content }} version: ${{ steps.version.outputs.version }} steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 with: fetch-depth: 0 - name: Extract version from tag @@ -110,7 +111,7 @@ jobs: run: | git cliff --output CHANGELOG.md - name: Upload updated CHANGELOG.md - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@v7.0.1 with: name: changelog path: CHANGELOG.md @@ -125,7 +126,7 @@ jobs: id-token: write # mint the OIDC token attestation provenance is signed with attestations: write # write the build-provenance attestation (the "claim") steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 # TODO: Download build artifacts if uploading to the release (pin # actions/download-artifact to a full commit SHA when enabling): # - uses: actions/download-artifact@ # vX.Y.Z @@ -133,7 +134,7 @@ jobs: # name: release-artifacts # path: artifacts/ - name: Create GitHub Release - uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v2 + uses: softprops/action-gh-release@v3.0.3 with: body: ${{ needs.changelog.outputs.changelog }} draft: false @@ -154,6 +155,6 @@ jobs: # (must match the `files:` uploaded above, e.g. artifacts/*). - name: Attest build provenance if: ${{ hashFiles('artifacts/*') != '' }} # skip until real artifacts are wired - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + uses: actions/attest-build-provenance@v4.2.2 with: subject-path: 'artifacts/*' diff --git a/.github/workflows/rhodibot.yml b/.github/workflows/rhodibot.yml index d7aabe9..26ca7a0 100644 --- a/.github/workflows/rhodibot.yml +++ b/.github/workflows/rhodibot.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # rhodibot.yml — RSR compliance CANARY (report-only) # @@ -34,7 +35,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 with: fetch-depth: 1 - name: Rhodibot — detect drift (no mutations) diff --git a/.github/workflows/runtime-policy.yml b/.github/workflows/runtime-policy.yml index 8727561..950aa8a 100644 --- a/.github/workflows/runtime-policy.yml +++ b/.github/workflows/runtime-policy.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Runtime and package-manager policy check. # @@ -36,7 +37,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 - name: Report runtime tier and reject mixed toolchains run: | diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index ba61ffe..bc73d4c 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Rust CI — thin wrapper calling the shared estate reusable in # hyperpolymath/standards. Configure once, propagate everywhere. diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 7aae3c2..7b7adf4 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: OSSF Scorecard @@ -15,7 +16,7 @@ permissions: jobs: scorecard: - uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@8f2ee50841e216cd8c192eeb68953118190f105c + uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 permissions: contents: read security-events: write diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index aad0cde..bb1df33 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Secret Scanner on: diff --git a/.github/workflows/security-policy.yml b/.github/workflows/security-policy.yml index 57ffa67..b71532d 100644 --- a/.github/workflows/security-policy.yml +++ b/.github/workflows/security-policy.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Security Policy on: @@ -21,7 +22,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 - name: Security checks run: | FAILED=false diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml index 3690484..c44845b 100644 --- a/.github/workflows/sonarqube.yml +++ b/.github/workflows/sonarqube.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # SonarQube Cloud (SonarCloud) static analysis. Analysis scope + exclusions live # in sonar-project.properties. Requires the SONAR_TOKEN repository secret @@ -24,10 +25,10 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 with: fetch-depth: 0 # full history for accurate new-code detection - name: SonarQube Scan - uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 + uses: SonarSource/sonarqube-scan-action@v8.2.1 env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/static-analysis-gate.yml b/.github/workflows/static-analysis-gate.yml index 8179fae..3ce342b 100644 --- a/.github/workflows/static-analysis-gate.yml +++ b/.github/workflows/static-analysis-gate.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Static Analysis Gate — Required by branch protection rules. # Runs panic-attack and hypatia, deposits findings for gitbot-fleet learning. @@ -23,7 +24,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 with: fetch-depth: 0 - name: Install panic-attack (if available) @@ -70,7 +71,7 @@ jobs: TOTAL=$(jq '. | length' panic-attack-findings.json 2>/dev/null || echo 0) CRITICAL=$(jq '[.[] | select(.severity == "critical")] | length' panic-attack-findings.json 2>/dev/null || echo 0) HIGH=$(jq '[.[] | select(.severity == "high")] | length' panic-attack-findings.json 2>/dev/null || echo 0) - MEDIUM=$(jq '[.[] | select(.severity == "medium")] | length' panic-attack-findings.json 2>/dev/null || echo 0) + MEDIUM=$(jq '[.[] | select(.severity == "medium" or .severity == "warn")] | length' panic-attack-findings.json 2>/dev/null || echo 0) LOW=$(jq '[.[] | select(.severity == "low")] | length' panic-attack-findings.json 2>/dev/null || echo 0) echo "total=$TOTAL" >> "$GITHUB_OUTPUT" @@ -120,7 +121,7 @@ jobs: echo "" >> "$GITHUB_STEP_SUMMARY" echo "Skipped: panic-attack not available in this environment." >> "$GITHUB_STEP_SUMMARY" - name: Upload panic-attack findings - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@v7.0.1 with: name: panic-attack-findings path: panic-attack-findings.json @@ -147,13 +148,13 @@ jobs: timeout-minutes: 15 steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 with: fetch-depth: 0 - name: Setup Elixir for Hypatia scanner id: beam continue-on-error: true - uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.18.2 + uses: erlef/setup-beam@v1.24.1 with: elixir-version: '1.19.4' otp-version: '28.3' @@ -206,7 +207,7 @@ jobs: TOTAL=$(jq '. | length' hypatia-findings.json 2>/dev/null || echo 0) CRITICAL=$(jq '[.[] | select(.severity == "critical")] | length' hypatia-findings.json 2>/dev/null || echo 0) HIGH=$(jq '[.[] | select(.severity == "high")] | length' hypatia-findings.json 2>/dev/null || echo 0) - MEDIUM=$(jq '[.[] | select(.severity == "medium")] | length' hypatia-findings.json 2>/dev/null || echo 0) + MEDIUM=$(jq '[.[] | select(.severity == "medium" or .severity == "warn")] | length' hypatia-findings.json 2>/dev/null || echo 0) LOW=$(jq '[.[] | select(.severity == "low")] | length' hypatia-findings.json 2>/dev/null || echo 0) echo "total=$TOTAL" >> "$GITHUB_OUTPUT" @@ -254,7 +255,7 @@ jobs: echo "" >> "$GITHUB_STEP_SUMMARY" echo "Skipped: Hypatia scanner not available in this environment." >> "$GITHUB_STEP_SUMMARY" - name: Upload hypatia findings - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@v7.0.1 with: name: hypatia-findings path: hypatia-findings.json @@ -273,7 +274,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 with: fetch-depth: 0 - name: Install panic-attack (if available) @@ -335,7 +336,7 @@ jobs: echo "" >> "$GITHUB_STEP_SUMMARY" echo "Skipped: panic-attack not available in this environment." >> "$GITHUB_STEP_SUMMARY" - name: Upload bridge report - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@v7.0.1 with: name: bridge-report path: bridge-report.json @@ -357,17 +358,17 @@ jobs: if: always() steps: - name: Download panic-attack findings - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v4 + uses: actions/download-artifact@v8.0.1 with: name: panic-attack-findings path: findings/ - name: Download hypatia findings - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v4 + uses: actions/download-artifact@v8.0.1 with: name: hypatia-findings path: findings/ - name: Download bridge report - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v4 + uses: actions/download-artifact@v8.0.1 with: name: bridge-report path: findings/ @@ -418,7 +419,7 @@ jobs: TOTAL=$(jq '.findings | length' findings/unified-findings.json) CRITICAL=$(jq '[.findings[] | select(.severity == "critical")] | length' findings/unified-findings.json) HIGH=$(jq '[.findings[] | select(.severity == "high")] | length' findings/unified-findings.json) - MEDIUM=$(jq '[.findings[] | select(.severity == "medium")] | length' findings/unified-findings.json) + MEDIUM=$(jq '[.findings[] | select(.severity == "medium" or .severity == "warn")] | length' findings/unified-findings.json) LOW=$(jq '[.findings[] | select(.severity == "low")] | length' findings/unified-findings.json) echo "total=$TOTAL" >> "$GITHUB_OUTPUT" @@ -427,7 +428,7 @@ jobs: echo "medium=$MEDIUM" >> "$GITHUB_OUTPUT" echo "low=$LOW" >> "$GITHUB_OUTPUT" - name: Upload unified findings (fleet scanner picks these up) - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@v7.0.1 with: name: unified-findings path: findings/unified-findings.json diff --git a/.github/workflows/wellknown-enforcement.yml b/.github/workflows/wellknown-enforcement.yml index 06e8f2d..6ce6a1f 100644 --- a/.github/workflows/wellknown-enforcement.yml +++ b/.github/workflows/wellknown-enforcement.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Well-Known Standards (RFC 9116 + RSR) on: @@ -26,7 +27,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v7.0.1 - name: RFC 9116 security.txt validation run: | SECTXT="" diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml index db1795d..b81e49c 100644 --- a/.github/workflows/workflow-linter.yml +++ b/.github/workflows/workflow-linter.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # workflow-linter.yml - Validates GitHub workflows against RSR security standards # This workflow can be copied to other repos for consistent enforcement @@ -28,7 +29,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@v7.0.1 - name: Check SPDX Headers run: | From dca455e0cc568aade361ab3fd6941ba2076743e5 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:37:22 +0100 Subject: [PATCH 06/12] fix: resolve retired path scalar matching and add harden-runner --- .github/workflows/actions.lock | 7 ++ .github/workflows/dependabot-automerge.yml | 4 ++ .github/workflows/sonarqube.yml | 4 ++ crates/squabble-fight/src/workflows.rs | 78 ++++++++++++++++------ 4 files changed, 73 insertions(+), 20 deletions(-) diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 5251abc..fe3bd9a 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -10,6 +10,7 @@ workflows: - 'actions/checkout@v7.0.1' '.github/workflows/dependabot-automerge.yml': - 'dependabot/fetch-metadata@v3.1.0' + - 'step-security/harden-runner@v2.9.1' '.github/workflows/dogfood-gate.yml': - 'actions/checkout@v7.0.1' '.github/workflows/e2e.yml': @@ -43,6 +44,7 @@ workflows: '.github/workflows/sonarqube.yml': - 'actions/checkout@v7.0.1' - 'sonarsource/sonarqube-scan-action@v8.2.1' + - 'step-security/harden-runner@v2.9.1' '.github/workflows/static-analysis-gate.yml': - 'actions/checkout@v7.0.1' - 'actions/download-artifact@v8.0.1' @@ -132,3 +134,8 @@ dependencies: commit: 'sha1-22918119ff8e1ca75a623e15c8296b6ea4fbe28f' owner_id: 545988 repo_id: 366408409 + 'step-security/harden-runner@v2.9.1': + ref: 'v2.9.1' + commit: 'sha1-5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde' + owner_id: 88700172 + repo_id: 422287306 diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 27c43a2..501af2c 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -54,6 +54,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: + - name: Harden Runner + uses: step-security/harden-runner@v2.9.1 + with: + egress-policy: audit - name: Fetch Dependabot metadata id: meta uses: dependabot/fetch-metadata@v3.1.0 diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml index c44845b..22c7a03 100644 --- a/.github/workflows/sonarqube.yml +++ b/.github/workflows/sonarqube.yml @@ -24,6 +24,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: + - name: Harden Runner + uses: step-security/harden-runner@v2.9.1 + with: + egress-policy: audit - name: Checkout uses: actions/checkout@v7.0.1 with: diff --git a/crates/squabble-fight/src/workflows.rs b/crates/squabble-fight/src/workflows.rs index 0b0f534..dd62d11 100644 --- a/crates/squabble-fight/src/workflows.rs +++ b/crates/squabble-fight/src/workflows.rs @@ -281,30 +281,68 @@ fn parse_workflow(file: &str, text: &str) -> WorkflowInfo { } } +enum BlockState { + None, + Run(usize), + Other(usize), +} + fn has_retired_descriptile_policy(text: &str) -> bool { + let mut state = BlockState::None; + text.lines().any(|line| { - let line = line.trim(); - let scalar = line - .strip_prefix("- run:") - .or_else(|| line.strip_prefix("run:")); - // Decode YAML quoting before interpreting the shell command. Stripping - // delimiters alone loses escapes and can turn quoted prose into code. - let decoded; - let line = if let Some(scalar) = scalar { - let scalar = scalar.trim(); - if scalar.starts_with(['\'', '"']) { - let Ok(value) = serde_yaml_ng::from_str::(scalar) else { - return false; - }; - decoded = value; - decoded.trim() - } else { - scalar + if line.trim().is_empty() { + return false; + } + let indent = line.chars().take_while(|c| c.is_whitespace()).count(); + let trimmed = line[indent..].trim_end(); + + let check_line = match state { + BlockState::Run(min_indent) if indent > min_indent => { + Some(trimmed.to_string()) + } + BlockState::Other(min_indent) if indent > min_indent => { + return false; + } + _ => { + state = BlockState::None; + + let is_run_key = trimmed.starts_with("- run:") || trimmed.starts_with("run:"); + let is_block_start = trimmed.ends_with('|') || trimmed.ends_with('>') || trimmed.ends_with("|-") || trimmed.ends_with(">-"); + + if is_run_key { + let scalar = trimmed.strip_prefix("- run:").or_else(|| trimmed.strip_prefix("run:")).unwrap().trim_start(); + if scalar.starts_with('|') || scalar.starts_with('>') { + state = BlockState::Run(indent); + None + } else { + let mut decoded = String::new(); + let scalar_trim = scalar.trim(); + if scalar_trim.starts_with(['\'', '"']) { + if let Ok(value) = serde_yaml_ng::from_str::(scalar_trim) { + decoded = value; + } else { + return false; + } + } else { + decoded = scalar_trim.to_string(); + } + Some(decoded.trim().to_string()) + } + } else { + if is_block_start { + state = BlockState::Other(indent); + } + None + } } - } else { - line }; - let mut words = line.split_whitespace().peekable(); + + let Some(check_line) = check_line else { + return false; + }; + + let mut words = check_line.split_whitespace().peekable(); if matches!(words.peek(), Some(&"if" | &"elif" | &"while" | &"until")) { words.next(); } From d99d5a13bfbdc38d8e90bc7c8cd1f013cf498362 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:40:24 +0100 Subject: [PATCH 07/12] security: pin all github actions to full commit SHAs --- .github/workflows/codeql.yml | 2 +- .github/workflows/container-build.yml | 2 +- .github/workflows/dependabot-automerge.yml | 4 ++-- .github/workflows/dogfood-gate.yml | 12 +++++------ .github/workflows/e2e.yml | 2 +- .github/workflows/estate-rules.yml | 2 +- .github/workflows/guix-policy.yml | 2 +- .github/workflows/openssf-compliance.yml | 2 +- .github/workflows/pages.yml | 8 ++++---- .github/workflows/push-email-notify.yml | 2 +- .github/workflows/quality.yml | 6 +++--- .github/workflows/release.yml | 12 +++++------ .github/workflows/rhodibot.yml | 2 +- .github/workflows/runtime-policy.yml | 2 +- .github/workflows/security-policy.yml | 2 +- .github/workflows/sonarqube.yml | 6 +++--- .github/workflows/static-analysis-gate.yml | 22 ++++++++++----------- .github/workflows/wellknown-enforcement.yml | 2 +- .github/workflows/workflow-linter.yml | 2 +- 19 files changed, 47 insertions(+), 47 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 65d84d9..7b09dd5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -35,7 +35,7 @@ jobs: build-mode: none steps: - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Initialize CodeQL uses: github/codeql-action/init@v4.37.9 with: diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml index fa6c089..e60ed7a 100644 --- a/.github/workflows/container-build.yml +++ b/.github/workflows/container-build.yml @@ -32,7 +32,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Tooling check run: | diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 501af2c..222c697 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -55,12 +55,12 @@ jobs: timeout-minutes: 15 steps: - name: Harden Runner - uses: step-security/harden-runner@v2.9.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde with: egress-policy: audit - name: Fetch Dependabot metadata id: meta - uses: dependabot/fetch-metadata@v3.1.0 + uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 with: github-token: ${{ secrets.GITHUB_TOKEN }} # --- Policy gate ------------------------------------------------------- diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index ebe3202..1a2bb06 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Check for A2ML files id: detect @@ -72,7 +72,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Check for K9 files id: detect @@ -118,7 +118,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Scan for invisible characters id: lint @@ -214,7 +214,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Check for Groove manifest id: groove @@ -273,7 +273,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Check and validate eclexiaiser manifest id: eclex @@ -325,7 +325,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Generate dogfooding scorecard run: | diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index b3b60a2..55e5150 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -44,7 +44,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Run E2E harness run: | if [ -f tests/e2e.sh ]; then diff --git a/.github/workflows/estate-rules.yml b/.github/workflows/estate-rules.yml index 38b523f..e955adf 100644 --- a/.github/workflows/estate-rules.yml +++ b/.github/workflows/estate-rules.yml @@ -27,7 +27,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Root shape allowlist run: bash scripts/check-root-shape.sh . - name: AsciiDoc by default (no .md under docs/) diff --git a/.github/workflows/guix-policy.yml b/.github/workflows/guix-policy.yml index 2ddf657..34630e7 100644 --- a/.github/workflows/guix-policy.yml +++ b/.github/workflows/guix-policy.yml @@ -22,7 +22,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Enforce Guix-only package policy run: | # Guix is the sole package manager estate-wide. Guix is BANNED. diff --git a/.github/workflows/openssf-compliance.yml b/.github/workflows/openssf-compliance.yml index 0eba1ed..e9d482e 100644 --- a/.github/workflows/openssf-compliance.yml +++ b/.github/workflows/openssf-compliance.yml @@ -22,7 +22,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: persist-credentials: false - name: Check SECURITY.md exists and has substance diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 71d0e28..ee10ad7 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -21,9 +21,9 @@ jobs: image: ghcr.io/stefan-hoeck/idris2-pack@sha256:f0758996a931fb35d9ecb1de273c4d59dabe2a09b433afc7e357f65a08b7e1ff steps: - name: Checkout Site - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Checkout Ddraig SSG - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: repository: hyperpolymath/ddraig-ssg path: .ddraig-ssg @@ -40,7 +40,7 @@ jobs: fi ./.ddraig-ssg/build/exec/ddraig build src _site https://hyperpolymath.github.io/${GITHUB_REPOSITORY#*/} - name: Upload artifact - uses: actions/upload-pages-artifact@v5.0.0 + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 with: path: '_site' deploy: @@ -53,4 +53,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v5.0.1 + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 diff --git a/.github/workflows/push-email-notify.yml b/.github/workflows/push-email-notify.yml index 676f498..a400e27 100644 --- a/.github/workflows/push-email-notify.yml +++ b/.github/workflows/push-email-notify.yml @@ -40,7 +40,7 @@ jobs: timeout-minutes: 5 steps: - name: Send push notification email - uses: hyperpolymath/smtp-notify-action@v0.2.0 + uses: hyperpolymath/smtp-notify-action@ede1191ef6ff3ac02c4f4d9efdf837ee517e11d7 with: server_address: ${{ secrets.SMTP_HOST }} server_port: ${{ secrets.SMTP_PORT }} diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 5f56e93..dcaa422 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -23,7 +23,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Check file permissions run: | find . -type f -perm /111 -name "*.sh" | head -10 || true @@ -35,7 +35,7 @@ jobs: run: | find . -type f -size +1M -not -path "./.git/*" | head -10 || echo "No large files" - name: EditorConfig check - uses: editorconfig-checker/action-editorconfig-checker@v3.0.0 + uses: editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393 continue-on-error: true docs: runs-on: ubuntu-latest @@ -43,7 +43,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Check documentation run: | MISSING="" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6631a1f..3317672 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Detect project type and build id: build run: | @@ -86,7 +86,7 @@ jobs: changelog: ${{ steps.cliff.outputs.content }} version: ${{ steps.version.outputs.version }} steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: fetch-depth: 0 - name: Extract version from tag @@ -111,7 +111,7 @@ jobs: run: | git cliff --output CHANGELOG.md - name: Upload updated CHANGELOG.md - uses: actions/upload-artifact@v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: changelog path: CHANGELOG.md @@ -126,7 +126,7 @@ jobs: id-token: write # mint the OIDC token attestation provenance is signed with attestations: write # write the build-provenance attestation (the "claim") steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # TODO: Download build artifacts if uploading to the release (pin # actions/download-artifact to a full commit SHA when enabling): # - uses: actions/download-artifact@ # vX.Y.Z @@ -134,7 +134,7 @@ jobs: # name: release-artifacts # path: artifacts/ - name: Create GitHub Release - uses: softprops/action-gh-release@v3.0.3 + uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 with: body: ${{ needs.changelog.outputs.changelog }} draft: false @@ -155,6 +155,6 @@ jobs: # (must match the `files:` uploaded above, e.g. artifacts/*). - name: Attest build provenance if: ${{ hashFiles('artifacts/*') != '' }} # skip until real artifacts are wired - uses: actions/attest-build-provenance@v4.2.2 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 with: subject-path: 'artifacts/*' diff --git a/.github/workflows/rhodibot.yml b/.github/workflows/rhodibot.yml index 26ca7a0..c5d0f20 100644 --- a/.github/workflows/rhodibot.yml +++ b/.github/workflows/rhodibot.yml @@ -35,7 +35,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: fetch-depth: 1 - name: Rhodibot — detect drift (no mutations) diff --git a/.github/workflows/runtime-policy.yml b/.github/workflows/runtime-policy.yml index 950aa8a..d2ad2d5 100644 --- a/.github/workflows/runtime-policy.yml +++ b/.github/workflows/runtime-policy.yml @@ -37,7 +37,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Report runtime tier and reject mixed toolchains run: | diff --git a/.github/workflows/security-policy.yml b/.github/workflows/security-policy.yml index b71532d..99d98f1 100644 --- a/.github/workflows/security-policy.yml +++ b/.github/workflows/security-policy.yml @@ -22,7 +22,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Security checks run: | FAILED=false diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml index 22c7a03..9117b31 100644 --- a/.github/workflows/sonarqube.yml +++ b/.github/workflows/sonarqube.yml @@ -25,14 +25,14 @@ jobs: timeout-minutes: 15 steps: - name: Harden Runner - uses: step-security/harden-runner@v2.9.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde with: egress-policy: audit - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: fetch-depth: 0 # full history for accurate new-code detection - name: SonarQube Scan - uses: SonarSource/sonarqube-scan-action@v8.2.1 + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/static-analysis-gate.yml b/.github/workflows/static-analysis-gate.yml index 3ce342b..bb8e678 100644 --- a/.github/workflows/static-analysis-gate.yml +++ b/.github/workflows/static-analysis-gate.yml @@ -24,7 +24,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: fetch-depth: 0 - name: Install panic-attack (if available) @@ -121,7 +121,7 @@ jobs: echo "" >> "$GITHUB_STEP_SUMMARY" echo "Skipped: panic-attack not available in this environment." >> "$GITHUB_STEP_SUMMARY" - name: Upload panic-attack findings - uses: actions/upload-artifact@v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: panic-attack-findings path: panic-attack-findings.json @@ -148,13 +148,13 @@ jobs: timeout-minutes: 15 steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: fetch-depth: 0 - name: Setup Elixir for Hypatia scanner id: beam continue-on-error: true - uses: erlef/setup-beam@v1.24.1 + uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 with: elixir-version: '1.19.4' otp-version: '28.3' @@ -255,7 +255,7 @@ jobs: echo "" >> "$GITHUB_STEP_SUMMARY" echo "Skipped: Hypatia scanner not available in this environment." >> "$GITHUB_STEP_SUMMARY" - name: Upload hypatia findings - uses: actions/upload-artifact@v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: hypatia-findings path: hypatia-findings.json @@ -274,7 +274,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout repository - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: fetch-depth: 0 - name: Install panic-attack (if available) @@ -336,7 +336,7 @@ jobs: echo "" >> "$GITHUB_STEP_SUMMARY" echo "Skipped: panic-attack not available in this environment." >> "$GITHUB_STEP_SUMMARY" - name: Upload bridge report - uses: actions/upload-artifact@v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: bridge-report path: bridge-report.json @@ -358,17 +358,17 @@ jobs: if: always() steps: - name: Download panic-attack findings - uses: actions/download-artifact@v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: name: panic-attack-findings path: findings/ - name: Download hypatia findings - uses: actions/download-artifact@v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: name: hypatia-findings path: findings/ - name: Download bridge report - uses: actions/download-artifact@v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: name: bridge-report path: findings/ @@ -428,7 +428,7 @@ jobs: echo "medium=$MEDIUM" >> "$GITHUB_OUTPUT" echo "low=$LOW" >> "$GITHUB_OUTPUT" - name: Upload unified findings (fleet scanner picks these up) - uses: actions/upload-artifact@v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: unified-findings path: findings/unified-findings.json diff --git a/.github/workflows/wellknown-enforcement.yml b/.github/workflows/wellknown-enforcement.yml index 6ce6a1f..1640c49 100644 --- a/.github/workflows/wellknown-enforcement.yml +++ b/.github/workflows/wellknown-enforcement.yml @@ -27,7 +27,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: RFC 9116 security.txt validation run: | SECTXT="" diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml index b81e49c..b304353 100644 --- a/.github/workflows/workflow-linter.yml +++ b/.github/workflows/workflow-linter.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Check SPDX Headers run: | From 52a09432499ab8eabe0ec3c230f2b92595c30209 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:00:37 +0000 Subject: [PATCH 08/12] docs(fight): document workflow gate classifiers --- crates/squabble-fight/src/workflows.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/squabble-fight/src/workflows.rs b/crates/squabble-fight/src/workflows.rs index dd62d11..6516d70 100644 --- a/crates/squabble-fight/src/workflows.rs +++ b/crates/squabble-fight/src/workflows.rs @@ -118,6 +118,9 @@ impl WorkflowFacts { /// workflow never triggered off-path), so the appliable pass-through move is /// proposed only then — a check that actually ran and *Failed* is a /// different problem the filter cannot explain. + /// + /// A workflow that checks a retired descriptile path, or has only commented + /// jobs, is classified as a non-functional gate regardless of [`CheckRun`]. pub fn classify(&self, check: &RequiredCheck, slug: &str) -> Option { let name = check.required_context.as_str(); let w = self.find_emitting(name)?; @@ -287,6 +290,11 @@ enum BlockState { Other(usize), } +/// Return whether a `run` scalar directly checks a known descriptile at either +/// retired `.machine_readable` location. +/// +/// Quoted inline scalars are YAML-decoded. Text outside `run` scalars and +/// commands that do not begin with a supported file-existence check are ignored. fn has_retired_descriptile_policy(text: &str) -> bool { let mut state = BlockState::None; @@ -384,6 +392,7 @@ fn has_retired_descriptile_policy(text: &str) -> bool { }) } +/// Return whether a bare top-level `jobs:` block contains no uncommented job. fn has_empty_jobs(text: &str) -> bool { let mut in_jobs = false; for line in text.lines() { From 17a154587ffc78de03af69a2ceb4a6276a064d9d Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:06:44 +0000 Subject: [PATCH 09/12] fix(fight): detect retired policy in folded run scalars --- crates/squabble-fight/src/workflows.rs | 198 +++++++++++++++---------- 1 file changed, 119 insertions(+), 79 deletions(-) diff --git a/crates/squabble-fight/src/workflows.rs b/crates/squabble-fight/src/workflows.rs index 6516d70..be38c1a 100644 --- a/crates/squabble-fight/src/workflows.rs +++ b/crates/squabble-fight/src/workflows.rs @@ -286,7 +286,7 @@ fn parse_workflow(file: &str, text: &str) -> WorkflowInfo { enum BlockState { None, - Run(usize), + Run { min_indent: usize, scalar: String }, Other(usize), } @@ -298,97 +298,117 @@ enum BlockState { fn has_retired_descriptile_policy(text: &str) -> bool { let mut state = BlockState::None; - text.lines().any(|line| { + for line in text.lines() { if line.trim().is_empty() { - return false; + if let BlockState::Run { scalar, .. } = &mut state { + scalar.push('\n'); + } + continue; } let indent = line.chars().take_while(|c| c.is_whitespace()).count(); let trimmed = line[indent..].trim_end(); - let check_line = match state { - BlockState::Run(min_indent) if indent > min_indent => { - Some(trimmed.to_string()) + match &mut state { + BlockState::Run { min_indent, scalar } if indent > *min_indent => { + scalar.push_str(line); + scalar.push('\n'); + continue; } - BlockState::Other(min_indent) if indent > min_indent => { - return false; + BlockState::Other(min_indent) if indent > *min_indent => { + continue; } - _ => { - state = BlockState::None; - - let is_run_key = trimmed.starts_with("- run:") || trimmed.starts_with("run:"); - let is_block_start = trimmed.ends_with('|') || trimmed.ends_with('>') || trimmed.ends_with("|-") || trimmed.ends_with(">-"); - - if is_run_key { - let scalar = trimmed.strip_prefix("- run:").or_else(|| trimmed.strip_prefix("run:")).unwrap().trim_start(); - if scalar.starts_with('|') || scalar.starts_with('>') { - state = BlockState::Run(indent); - None - } else { - let mut decoded = String::new(); - let scalar_trim = scalar.trim(); - if scalar_trim.starts_with(['\'', '"']) { - if let Ok(value) = serde_yaml_ng::from_str::(scalar_trim) { - decoded = value; - } else { - return false; - } - } else { - decoded = scalar_trim.to_string(); - } - Some(decoded.trim().to_string()) - } - } else { - if is_block_start { - state = BlockState::Other(indent); - } - None + BlockState::Run { scalar, .. } => { + if decoded_scalar_has_retired_policy(scalar) { + return true; } } - }; - - let Some(check_line) = check_line else { - return false; - }; - - let mut words = check_line.split_whitespace().peekable(); - if matches!(words.peek(), Some(&"if" | &"elif" | &"while" | &"until")) { - words.next(); + BlockState::None | BlockState::Other(_) => {} } - if words.peek() == Some(&"!") { - words.next(); - } - let target = match words.next() { - Some("check_file") => words.next(), - Some("test" | "[" | "[[") => { - if words.peek() == Some(&"!") { - words.next(); - } - if matches!(words.next(), Some("-f" | "-e")) { - words.next() + state = BlockState::None; + + let is_run_key = trimmed.starts_with("- run:") || trimmed.starts_with("run:"); + let is_block_start = trimmed.ends_with('|') + || trimmed.ends_with('>') + || trimmed.ends_with("|-") + || trimmed.ends_with(">-"); + + if is_run_key { + let scalar = trimmed + .strip_prefix("- run:") + .or_else(|| trimmed.strip_prefix("run:")) + .unwrap() + .trim_start(); + if scalar.starts_with('|') || scalar.starts_with('>') { + state = BlockState::Run { + min_indent: indent, + scalar: format!("{scalar}\n"), + }; + } else { + let scalar_trim = scalar.trim(); + let decoded = if scalar_trim.starts_with(['\'', '"']) { + let Ok(value) = serde_yaml_ng::from_str::(scalar_trim) else { + continue; + }; + value } else { - None + scalar_trim.to_string() + }; + if command_has_retired_policy(decoded.trim()) { + return true; } } - _ => None, - }; - let Some(target) = target else { - return false; - }; - let target = target.trim_end_matches(';').trim_matches(['\'', '"']); - [ - "STATE", - "META", - "ECOSYSTEM", - "AGENTIC", - "NEUROSYM", - "PLAYBOOK", - "ANCHOR", - ] - .iter() - .any(|name| { - target == format!(".machine_readable/{name}.a2ml") - || target == format!(".machine_readable/6a2/{name}.a2ml") - }) + } else if is_block_start { + state = BlockState::Other(indent); + } + } + + matches!(state, BlockState::Run { ref scalar, .. } if decoded_scalar_has_retired_policy(scalar)) +} + +fn decoded_scalar_has_retired_policy(scalar: &str) -> bool { + serde_yaml_ng::from_str::(scalar) + .is_ok_and(|decoded| decoded.lines().any(command_has_retired_policy)) +} + +fn command_has_retired_policy(command: &str) -> bool { + let mut words = command.split_whitespace().peekable(); + if matches!(words.peek(), Some(&"if" | &"elif" | &"while" | &"until")) { + words.next(); + } + if words.peek() == Some(&"!") { + words.next(); + } + let target = match words.next() { + Some("check_file") => words.next(), + Some("test" | "[" | "[[") => { + if words.peek() == Some(&"!") { + words.next(); + } + if matches!(words.next(), Some("-f" | "-e")) { + words.next() + } else { + None + } + } + _ => None, + }; + let Some(target) = target else { + return false; + }; + let target = target.trim_end_matches(';').trim_matches(['\'', '"']); + [ + "STATE", + "META", + "ECOSYSTEM", + "AGENTIC", + "NEUROSYM", + "PLAYBOOK", + "ANCHOR", + ] + .iter() + .any(|name| { + target == format!(".machine_readable/{name}.a2ml") + || target == format!(".machine_readable/6a2/{name}.a2ml") }) } @@ -730,6 +750,26 @@ jobs: )); } + #[test] + fn folded_retired_policy_command_is_a_non_functional_gate() { + let workflow = r#"name: Compliance +jobs: + compliance: + steps: + - run: > + test -f + .machine_readable/STATE.a2ml +"#; + let facts = WorkflowFacts { + workflows: vec![parse_workflow("compliance.yml", workflow)], + }; + + assert!(matches!( + facts.classify(&req("compliance", CheckRun::Missing), "owner/repo"), + Some(Move::FlagNonFunctionalGate { .. }) + )); + } + #[test] fn commented_jobs_cannot_supply_a_check() { let template = "name: E2E\njobs:\n # test:\n # runs-on: ubuntu-latest\n"; From e0aba4264d529c32e21011c5d3a145cec721971f Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 13:08:04 +0100 Subject: [PATCH 10/12] Document workflow policy detection helpers (#81) Add Rust doc comments explaining retired descriptile-policy detection, empty jobs-section detection, and when workflow classification marks policies non-functional. Validation was not run; this is a documentation-only change. [View coding task](https://app.coderabbit.ai/code/tasks/33e5b845-a843-5c5b-ba3e-70b2c7dd1f81?source=coding_agent_github_pr_description) Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- crates/squabble-fight/src/workflows.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/squabble-fight/src/workflows.rs b/crates/squabble-fight/src/workflows.rs index be38c1a..4435e16 100644 --- a/crates/squabble-fight/src/workflows.rs +++ b/crates/squabble-fight/src/workflows.rs @@ -112,6 +112,10 @@ impl WorkflowFacts { /// workflow could be attributed — the caller then falls back to the pure /// engine's conservative default rather than guessing. /// + /// Workflows marked as using a retired descriptile policy or lacking + /// uncommented job definitions are flagged as non-functional before + /// ownership and lane classification. + /// /// `slug` is the current repo's `owner/repo`; a reusable workflow whose /// `owner/repo` differs is owned upstream. The check's realised [`CheckRun`] /// matters: the path-filter trap only manifests as a *Missing* check (the From fc47d39823590ca45f7b14a361ee672f18deb6d3 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:12:03 +0000 Subject: [PATCH 11/12] docs(fight): document retired-path policy helpers --- crates/squabble-fight/src/workflows.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/squabble-fight/src/workflows.rs b/crates/squabble-fight/src/workflows.rs index 4435e16..a3c529a 100644 --- a/crates/squabble-fight/src/workflows.rs +++ b/crates/squabble-fight/src/workflows.rs @@ -369,11 +369,16 @@ fn has_retired_descriptile_policy(text: &str) -> bool { matches!(state, BlockState::Run { ref scalar, .. } if decoded_scalar_has_retired_policy(scalar)) } +/// Return whether a YAML block scalar contains a recognised retired-path check. +/// Invalid scalars and scalars without a matching command line return `false`. fn decoded_scalar_has_retired_policy(scalar: &str) -> bool { serde_yaml_ng::from_str::(scalar) .is_ok_and(|decoded| decoded.lines().any(command_has_retired_policy)) } +/// Return whether a command starts with a supported existence check for a +/// retired descriptile path. Shell condition keywords and negation are allowed +/// before `check_file`, `test`, `[` or `[[` checks. fn command_has_retired_policy(command: &str) -> bool { let mut words = command.split_whitespace().peekable(); if matches!(words.peek(), Some(&"if" | &"elif" | &"while" | &"until")) { From 9eebae69aa60b5c88fc37e1a2c089676c204eccd Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 15 Sep 2026 13:33:12 +0100 Subject: [PATCH 12/12] fix(ci): delete the actions.lock that startup-killed twelve workflows Resolving the merge conflicts let this PR build a merge ref, so Actions finally ran at all -- and revealed a second, independent break: twelve of the sixteen runs ended in `startup_failure` with zero jobs and no check emitted, which is invisible in the UI and indistinguishable from a gate that simply does not exist. Cause: `.github/workflows/actions.lock`, a file this branch adds and main does not have. GitHub validates every `uses:` against that lockfile at workflow startup, before any job is created. The lockfile was generated by `gh actions-lock` at version v0.0.2 and names actions by TAG (`actions/checkout@v7.0.1`) while the workflows pin by SHA, under a repo policy of `sha_pinning_required: true`. It also lists `sonarqube.yml`, which main deleted in #69. The measurement is unambiguous. Of the sixteen runs at the merge head, all twelve that startup-failed are named in the lockfile and all four that ran -- Governance, Hypatia, Rust CI, Secret Scanner -- are not. Those four are the reusable callers, which reference hyperpolymath/standards and appear nowhere in it. Twelve for twelve and four for four, with main as the control: the same self-contained workflows succeed there, where no lockfile exists. Deletes the lockfile, restoring parity with main. Also strips the `# This workflow is managed by gh actions-lock.` header the same tool prepended to all twenty-five workflows: with the lockfile gone the claim is false, and left in place it invites the next reader to regenerate the file and re-break all twelve. Not fixed here, and worth a separate pass: the same tool stripped the `# vX.Y.Z` comment from every pinned SHA, so the pins are still correct but no longer self-documenting. Verified: 27/27 workflows parse (yq, with a negative control confirming it rejects conflict markers), 0 conflict-marker files, 0 non-SHA `uses:` refs, and actionlint reports 23 issues -- exactly main's count, all pre-existing shellcheck style notes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WgqXnnNWBkiKMyUeLqzcuN --- .github/workflows/actions.lock | 141 -------------------- .github/workflows/container-build.yml | 1 - .github/workflows/dependabot-automerge.yml | 1 - .github/workflows/dogfood-gate.yml | 1 - .github/workflows/e2e.yml | 1 - .github/workflows/estate-rules.yml | 1 - .github/workflows/governance.yml | 1 - .github/workflows/guix-policy.yml | 1 - .github/workflows/hypatia-scan.yml | 1 - .github/workflows/instant-sync.yml | 1 - .github/workflows/label-triage.yml | 1 - .github/workflows/labels.yml | 1 - .github/workflows/mirror.yml | 1 - .github/workflows/openssf-compliance.yml | 1 - .github/workflows/pages.yml | 1 - .github/workflows/quality.yml | 1 - .github/workflows/release.yml | 1 - .github/workflows/rhodibot.yml | 1 - .github/workflows/runtime-policy.yml | 1 - .github/workflows/rust-ci.yml | 1 - .github/workflows/scorecard.yml | 1 - .github/workflows/secret-scanner.yml | 1 - .github/workflows/security-policy.yml | 1 - .github/workflows/static-analysis-gate.yml | 1 - .github/workflows/wellknown-enforcement.yml | 1 - .github/workflows/workflow-linter.yml | 1 - 26 files changed, 166 deletions(-) delete mode 100644 .github/workflows/actions.lock diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock deleted file mode 100644 index fe3bd9a..0000000 --- a/.github/workflows/actions.lock +++ /dev/null @@ -1,141 +0,0 @@ -# This file is machine-generated by `gh actions-lock`. -# Do not edit by hand; run `gh actions-lock` to update. -# Docs: https://gh.io/actions-lockfile -version: 'v0.0.2' -workflows: - '.github/workflows/codeql.yml': - - 'actions/checkout@v7.0.1' - - 'github/codeql-action@v4.37.9' - '.github/workflows/container-build.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/dependabot-automerge.yml': - - 'dependabot/fetch-metadata@v3.1.0' - - 'step-security/harden-runner@v2.9.1' - '.github/workflows/dogfood-gate.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/e2e.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/estate-rules.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/guix-policy.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/openssf-compliance.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/pages.yml': - - 'actions/checkout@v7.0.1' - - 'actions/deploy-pages@v5.0.1' - - 'actions/upload-pages-artifact@v5.0.0' - '.github/workflows/push-email-notify.yml': - - 'hyperpolymath/smtp-notify-action@v0.2.0' - '.github/workflows/quality.yml': - - 'actions/checkout@v7.0.1' - - 'editorconfig-checker/action-editorconfig-checker@v3.0.0' - '.github/workflows/release.yml': - - 'actions/attest-build-provenance@v4.2.2' - - 'actions/checkout@v7.0.1' - - 'actions/upload-artifact@v7.0.1' - - 'softprops/action-gh-release@v3.0.3' - '.github/workflows/rhodibot.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/runtime-policy.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/security-policy.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/sonarqube.yml': - - 'actions/checkout@v7.0.1' - - 'sonarsource/sonarqube-scan-action@v8.2.1' - - 'step-security/harden-runner@v2.9.1' - '.github/workflows/static-analysis-gate.yml': - - 'actions/checkout@v7.0.1' - - 'actions/download-artifact@v8.0.1' - - 'actions/upload-artifact@v7.0.1' - - 'erlef/setup-beam@v1.24.1' - '.github/workflows/wellknown-enforcement.yml': - - 'actions/checkout@v7.0.1' - '.github/workflows/workflow-linter.yml': - - 'actions/checkout@v7.0.1' -dependencies: - 'actions/attest-build-provenance@v4.2.2': - ref: 'v4.2.2' - commit: 'sha1-4d101475d8b20a2381f78447822ac1eab6504dd8' - owner_id: 44036562 - repo_id: 760702757 - uses: - - 'actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d' - 'actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d': - ref: 'v4.2.1' - commit: 'sha1-508db95dd578ae2727ebd6217d5ba78e4fbda05d' - owner_id: 44036562 - repo_id: 760701061 - 'actions/checkout@v7.0.1': - ref: 'v7.0.1' - commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' - owner_id: 44036562 - repo_id: 197814629 - 'actions/deploy-pages@v5.0.1': - ref: 'v5.0.1' - commit: 'sha1-368f82528645a54fb793d4d04e342629a3f51346' - owner_id: 44036562 - repo_id: 438112499 - 'actions/download-artifact@v8.0.1': - ref: 'v8.0.1' - commit: 'sha1-3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c' - owner_id: 44036562 - repo_id: 192626254 - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f': - ref: 'v7.0.0' - commit: 'sha1-bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' - owner_id: 44036562 - repo_id: 192625955 - 'actions/upload-artifact@v7.0.1': - ref: 'v7.0.1' - commit: 'sha1-043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' - owner_id: 44036562 - repo_id: 192625955 - 'actions/upload-pages-artifact@v5.0.0': - ref: 'v5.0.0' - commit: 'sha1-fc324d3547104276b827a68afc52ff2a11cc49c9' - owner_id: 44036562 - repo_id: 496012378 - uses: - - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' - 'dependabot/fetch-metadata@v3.1.0': - ref: 'v3.1.0' - commit: 'sha1-25dd0e34f4fe68f24cc83900b1fe3fe149efef98' - owner_id: 27347476 - repo_id: 371068214 - 'editorconfig-checker/action-editorconfig-checker@v3.0.0': - ref: 'v3.0.0' - commit: 'sha1-51f63319f592f97930c73d9c46184d20bd206393' - owner_id: 26415196 - repo_id: 297874902 - 'erlef/setup-beam@v1.24.1': - ref: 'v1.24.1' - commit: 'sha1-54075bcc5e249e4758d363f27d099f55d843f124' - owner_id: 47606891 - repo_id: 331103973 - 'github/codeql-action@v4.37.9': - ref: 'v4.37.9' - commit: 'sha1-cdf488f595d80d6e07e03d4674febd5ab45fa938' - owner_id: 9919 - repo_id: 259445878 - 'hyperpolymath/smtp-notify-action@v0.2.0': - ref: 'v0.2.0' - commit: 'sha1-ede1191ef6ff3ac02c4f4d9efdf837ee517e11d7' - owner_id: 6759885 - repo_id: 1352485172 - 'softprops/action-gh-release@v3.0.3': - ref: 'v3.0.3' - commit: 'sha1-efb35369e0ad2afab669f228072c1b0d510eae64' - owner_id: 2242 - repo_id: 204253808 - 'sonarsource/sonarqube-scan-action@v8.2.1': - ref: 'v8.2.1' - commit: 'sha1-22918119ff8e1ca75a623e15c8296b6ea4fbe28f' - owner_id: 545988 - repo_id: 366408409 - 'step-security/harden-runner@v2.9.1': - ref: 'v2.9.1' - commit: 'sha1-5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde' - owner_id: 88700172 - repo_id: 422287306 diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml index e60ed7a..0986016 100644 --- a/.github/workflows/container-build.yml +++ b/.github/workflows/container-build.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: container build on: diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 222c697..7136e09 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # # dependabot-automerge.yml — enable GitHub's native auto-merge on diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index a226a0a..22f8784 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 55e5150..646dfa0 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # diff --git a/.github/workflows/estate-rules.yml b/.github/workflows/estate-rules.yml index e955adf..5cf9350 100644 --- a/.github/workflows/estate-rules.yml +++ b/.github/workflows/estate-rules.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 0133d28..724d9ed 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Governance diff --git a/.github/workflows/guix-policy.yml b/.github/workflows/guix-policy.yml index 34630e7..d6c2442 100644 --- a/.github/workflows/guix-policy.yml +++ b/.github/workflows/guix-policy.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Guix Package Policy on: diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index a51dbd3..846951f 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # # Standalone Hypatia security scan (push / PR / weekly). This is NOT a duplicate diff --git a/.github/workflows/instant-sync.yml b/.github/workflows/instant-sync.yml index bf3f085..3c67981 100644 --- a/.github/workflows/instant-sync.yml +++ b/.github/workflows/instant-sync.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Instant Forge Sync - Triggers propagation to all forges on push/release name: Instant Sync diff --git a/.github/workflows/label-triage.yml b/.github/workflows/label-triage.yml index 814a192..9886e92 100644 --- a/.github/workflows/label-triage.yml +++ b/.github/workflows/label-triage.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Label Triage diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index 83ab941..c80b676 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Labels diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index ebec909..9a96808 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Mirror to Git Forges on: diff --git a/.github/workflows/openssf-compliance.yml b/.github/workflows/openssf-compliance.yml index e9d482e..d18fd1d 100644 --- a/.github/workflows/openssf-compliance.yml +++ b/.github/workflows/openssf-compliance.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # OpenSSF Best Practices compliance gate — blocks PRs and pushes that lack # required files or still contain unfilled placeholder tokens. diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index ee10ad7..3f58081 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: GitHub Pages (Ddraig SSG) on: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index dcaa422..137ee4a 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Code Quality on: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3317672..e4f4284 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # diff --git a/.github/workflows/rhodibot.yml b/.github/workflows/rhodibot.yml index c5d0f20..3a3f3c4 100644 --- a/.github/workflows/rhodibot.yml +++ b/.github/workflows/rhodibot.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # rhodibot.yml — RSR compliance CANARY (report-only) # diff --git a/.github/workflows/runtime-policy.yml b/.github/workflows/runtime-policy.yml index d2ad2d5..2e3c1c1 100644 --- a/.github/workflows/runtime-policy.yml +++ b/.github/workflows/runtime-policy.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Runtime and package-manager policy check. # diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index bc73d4c..ba61ffe 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Rust CI — thin wrapper calling the shared estate reusable in # hyperpolymath/standards. Configure once, propagate everywhere. diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 7b7adf4..55f9636 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: OSSF Scorecard diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index bb1df33..aad0cde 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Secret Scanner on: diff --git a/.github/workflows/security-policy.yml b/.github/workflows/security-policy.yml index 99d98f1..b20d25c 100644 --- a/.github/workflows/security-policy.yml +++ b/.github/workflows/security-policy.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Security Policy on: diff --git a/.github/workflows/static-analysis-gate.yml b/.github/workflows/static-analysis-gate.yml index bb8e678..b59bdda 100644 --- a/.github/workflows/static-analysis-gate.yml +++ b/.github/workflows/static-analysis-gate.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # Static Analysis Gate — Required by branch protection rules. # Runs panic-attack and hypatia, deposits findings for gitbot-fleet learning. diff --git a/.github/workflows/wellknown-enforcement.yml b/.github/workflows/wellknown-enforcement.yml index 1640c49..c7ee552 100644 --- a/.github/workflows/wellknown-enforcement.yml +++ b/.github/workflows/wellknown-enforcement.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Well-Known Standards (RFC 9116 + RSR) on: diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml index b304353..4d9b128 100644 --- a/.github/workflows/workflow-linter.yml +++ b/.github/workflows/workflow-linter.yml @@ -1,4 +1,3 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # workflow-linter.yml - Validates GitHub workflows against RSR security standards # This workflow can be copied to other repos for consistent enforcement