Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/dogfood-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:

- name: Validate A2ML manifests
if: steps.detect.outputs.count > 0
uses: hyperpolymath/a2ml-validate-action@05bcb78917c09702e90ed18004298a6728753914 # main
uses: hyperpolymath/a2ml-ecosystem/validate-action@aa4b836bd969df2bc58128cb8e3d20bbc88d5e79 # main
with:
path: '.'
strict: 'false'
Expand Down Expand Up @@ -105,7 +105,7 @@ jobs:

- name: Validate K9 contracts
if: steps.detect.outputs.k9_count > 0
uses: hyperpolymath/k9-validate-action@bddcd9109ee96f9ea3fdb4bf51084fe9cd0909ce # main
uses: hyperpolymath/k9-ecosystem/validate-action@89f3c2702f4f650a92aa7411502f38da06abd562 # main
with:
path: '.'
strict: 'false'
Expand Down
16 changes: 7 additions & 9 deletions .github/workflows/inbox-steward.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,12 @@ on:
types: [opened, synchronize, ready_for_review, converted_to_draft, review_requested]
pull_request_review:
types: [submitted, dismissed]
check_suite:
types: [completed]
workflow_run:
workflows:
- Dogfood Gate
- Scorecard Enforcer
- Hypatia Security Scan
- Static Analysis Gate
types: [completed]
# AMPLIFIER REMOVED 2026-07-29. check_suite:[completed] and
# workflow_run:[completed] made this fire once per CI completion on every
# watched workflow -- a multiplier on every unit of estate CI activity, and
# the mechanism behind the 3,041-notification burst of 2026-07-20/21.
# The workflow is currently disabled_manually; these triggers are removed so
# that RE-ENABLING it is safe. PR/review events and workflow_dispatch remain.
# Manual trigger
workflow_dispatch:
inputs:
Expand Down Expand Up @@ -430,6 +427,7 @@ jobs:
summary:
name: Generate stewardship summary
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [identify-passed-prs, validate-prs, auto-merge-prs]
if: always()

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/push-email-notify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
name: Email on push
if: ${{ vars.PUSH_EMAIL_ENABLED == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Send push notification email
uses: dawidd6/action-send-mail@c50dc4cc848ade21f848990889906d804fae78c5 # pinned
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
31 changes: 21 additions & 10 deletions bots/accessibilitybot/src/analyzers/contrast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ use crate::analyzers::Analyzer;
use crate::fleet::{Finding, ImpactAssessment, Severity, WcagLevel};
use regex::Regex;
use std::path::Path;
use std::sync::LazyLock;

// Compiled once. These were rebuilt on every call (hypatia expect_in_hot_path).
static COLOR_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)(?:^|;|\{)\s*color\s*:\s*([^;}\n]+)").expect("valid regex"));
static BG_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)background(?:-color)?\s*:\s*([^;}\n]+)").expect("valid regex"));
static BLOCK_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"([^{]+)\{([^}]+)\}").expect("valid regex"));
static STYLE_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#"style\s*=\s*"([^"]+)""#).expect("valid regex"));
static COLOR_RE_4: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)(?:^|;)\s*color\s*:\s*([^;]+)").expect("valid regex"));
static BG_RE_5: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)background(?:-color)?\s*:\s*([^;]+)").expect("valid regex"));

/// Contrast analyzer for CSS color pairs
pub struct ContrastAnalyzer;
Expand Down Expand Up @@ -133,15 +148,11 @@ pub fn contrast_ratio(fg: (u8, u8, u8), bg: (u8, u8, u8)) -> f64 {
/// Analyze CSS content for contrast issues
fn analyze_css(path: &Path, content: &str) -> Vec<Finding> {
let mut findings = Vec::new();
let color_re = Regex::new(
r"(?i)(?:^|;|\{)\s*color\s*:\s*([^;}\n]+)"
).expect("valid regex");
let bg_re = Regex::new(
r"(?i)background(?:-color)?\s*:\s*([^;}\n]+)"
).expect("valid regex");
let color_re = &*COLOR_RE;
let bg_re = &*BG_RE;

// Extract color/background-color pairs within CSS rule blocks
let block_re = Regex::new(r"([^{]+)\{([^}]+)\}").expect("valid regex");
let block_re = &*BLOCK_RE;

for caps in block_re.captures_iter(content) {
let selector = caps[1].trim();
Expand Down Expand Up @@ -211,9 +222,9 @@ fn analyze_css(path: &Path, content: &str) -> Vec<Finding> {
/// Analyze inline styles in HTML for contrast issues
fn analyze_inline_styles(path: &Path, content: &str) -> Vec<Finding> {
let mut findings = Vec::new();
let style_re = Regex::new(r#"style\s*=\s*"([^"]+)""#).expect("valid regex");
let color_re = Regex::new(r"(?i)(?:^|;)\s*color\s*:\s*([^;]+)").expect("valid regex");
let bg_re = Regex::new(r"(?i)background(?:-color)?\s*:\s*([^;]+)").expect("valid regex");
let style_re = &*STYLE_RE;
let color_re = &*COLOR_RE_4;
let bg_re = &*BG_RE_5;

for (line_num, line) in content.lines().enumerate() {
if let Some(style_caps) = style_re.captures(line) {
Expand Down
17 changes: 13 additions & 4 deletions bots/accessibilitybot/src/analyzers/css.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ use crate::analyzers::Analyzer;
use crate::fleet::{Finding, ImpactAssessment, Severity, WcagLevel};
use regex::Regex;
use std::path::Path;
use std::sync::LazyLock;

// Compiled once. These were rebuilt on every call (hypatia expect_in_hot_path).
static FONT_SIZE_PX_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)font-size\s*:\s*(\d+)px").expect("valid regex"));
static LINE_HEIGHT_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)line-height\s*:\s*([\d.]+)").expect("valid regex"));
static BLOCK_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"([^{]+)\{([^}]+)\}").expect("valid regex"));

/// CSS accessibility analyzer
pub struct CssAnalyzer;
Expand Down Expand Up @@ -53,7 +62,7 @@ impl Analyzer for CssAnalyzer {

/// Check for font sizes using px instead of rem/em
fn check_px_font_sizes(path: &Path, content: &str, findings: &mut Vec<Finding>) {
let font_size_px_re = Regex::new(r"(?i)font-size\s*:\s*(\d+)px").expect("valid regex");
let font_size_px_re = &*FONT_SIZE_PX_RE;

for (line_num, line) in content.lines().enumerate() {
if let Some(caps) = font_size_px_re.captures(line) {
Expand Down Expand Up @@ -83,8 +92,8 @@ fn check_px_font_sizes(path: &Path, content: &str, findings: &mut Vec<Finding>)

/// Check line-height is at least 1.5 for body text
fn check_line_height(path: &Path, content: &str, findings: &mut Vec<Finding>) {
let line_height_re = Regex::new(r"(?i)line-height\s*:\s*([\d.]+)").expect("valid regex");
let block_re = Regex::new(r"([^{]+)\{([^}]+)\}").expect("valid regex");
let line_height_re = &*LINE_HEIGHT_RE;
let block_re = &*BLOCK_RE;

for caps in block_re.captures_iter(content) {
let selector = caps[1].trim();
Expand Down Expand Up @@ -203,7 +212,7 @@ fn check_contrast_preference(path: &Path, content: &str, findings: &mut Vec<Find

/// Check for display: none being used where sr-only pattern should be used
fn check_display_none_misuse(path: &Path, content: &str, findings: &mut Vec<Finding>) {
let block_re = Regex::new(r"([^{]+)\{([^}]+)\}").expect("valid regex");
let block_re = &*BLOCK_RE;

for caps in block_re.captures_iter(content) {
let selector = caps[1].trim();
Expand Down
11 changes: 9 additions & 2 deletions bots/accessibilitybot/src/analyzers/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ use crate::fleet::{Finding, ImpactAssessment, Severity, WcagLevel};
use regex::Regex;
use scraper::{Html, Selector};
use std::path::Path;
use std::sync::LazyLock;

// Compiled once. These were rebuilt on every call (hypatia expect_in_hot_path).
static OUTLINE_NONE_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)outline\s*:\s*(none|0)\s*[;}\n]").expect("valid regex"));
static STYLE_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#"style\s*=\s*"[^"]*outline\s*:\s*(none|0)[^"]*""#).expect("valid regex"));

/// Keyboard navigation analyzer
pub struct KeyboardAnalyzer;
Expand Down Expand Up @@ -185,7 +192,7 @@ fn check_onclick_no_keyboard(
/// Check for outline: none in CSS (suppresses focus indicator)
fn check_focus_styles_css(path: &Path, content: &str) -> Vec<Finding> {
let mut findings = Vec::new();
let outline_none_re = Regex::new(r"(?i)outline\s*:\s*(none|0)\s*[;}\n]").expect("valid regex");
let outline_none_re = &*OUTLINE_NONE_RE;

for (line_num, line) in content.lines().enumerate() {
if outline_none_re.is_match(line) {
Expand Down Expand Up @@ -228,7 +235,7 @@ fn check_inline_focus_suppression(
content: &str,
findings: &mut Vec<Finding>,
) {
let style_re = Regex::new(r#"style\s*=\s*"[^"]*outline\s*:\s*(none|0)[^"]*""#).expect("valid regex");
let style_re = &*STYLE_RE;

for (line_num, line) in content.lines().enumerate() {
if style_re.is_match(line) {
Expand Down
13 changes: 9 additions & 4 deletions bots/finishingbot/src/analyzers/claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ use regex::Regex;
use std::path::Path;
use tracing::debug;
use walkdir::WalkDir;
use std::sync::LazyLock;

// Compiled once. These were rebuilt on every call (hypatia expect_in_hot_path).
static MEMBER_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#"members\s*=\s*\[([\s\S]*?)\]"#).expect("Cargo workspace members regex is a valid constant pattern"));
static PATH_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#""([^"]+)""#).expect("Cargo workspace member-path regex is a valid constant pattern"));

/// Claim verification analyzer
pub struct ClaimsAnalyzer;
Expand Down Expand Up @@ -442,12 +449,10 @@ impl ClaimsAnalyzer {
if let Ok(content) = std::fs::read_to_string(cargo_path) {
// Simple check: look for workspace members that don't exist
if content.contains("[workspace]") {
let member_re = Regex::new(r#"members\s*=\s*\[([\s\S]*?)\]"#)
.expect("Cargo workspace members regex is a valid constant pattern");
let member_re = &*MEMBER_RE;
if let Some(caps) = member_re.captures(&content) {
let members_str = &caps[1];
let path_re = Regex::new(r#""([^"]+)""#)
.expect("Cargo workspace member-path regex is a valid constant pattern");
let path_re = &*PATH_RE;
let cargo_dir = cargo_path.parent().unwrap_or_else(|| Path::new("."));
for cap in path_re.captures_iter(members_str) {
let member_path = cargo_dir.join(&cap[1]);
Expand Down
8 changes: 6 additions & 2 deletions bots/finishingbot/src/analyzers/license.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ use regex::Regex;
use std::path::Path;
use tracing::debug;
use walkdir::WalkDir;
use std::sync::LazyLock;

// Compiled once. These were rebuilt on every call (hypatia expect_in_hot_path).
static SPDX_PATTERN: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)SPDX-License-Identifier:\s*[A-Za-z0-9.-]+").expect("SPDX header regex is a valid constant pattern"));

/// Repositories that deliberately use AGPL-3.0-or-later instead of PMPL.
/// These are co-developed projects with the user's son and use AGPL by deliberate choice.
Expand Down Expand Up @@ -222,8 +227,7 @@ impl LicenseAnalyzer {

/// Check for SPDX headers in source files
fn check_spdx_headers(&self, path: &Path, config: &Config, result: &mut AnalysisResult) {
let spdx_pattern = Regex::new(r"(?i)SPDX-License-Identifier:\s*[A-Za-z0-9.-]+")
.expect("SPDX header regex is a valid constant pattern");
let spdx_pattern = &*SPDX_PATTERN;

for entry in WalkDir::new(path)
.follow_links(false)
Expand Down
21 changes: 13 additions & 8 deletions bots/glambot/src/analyzers/accessibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ use crate::config::Config;
use crate::error::Result;
use std::path::Path;
use walkdir::WalkDir;
use std::sync::LazyLock;
use regex::Regex;

// Compiled once. These were rebuilt on every call (hypatia expect_in_hot_path).
static MD_IMAGE_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"!\[(.*?)\]\((.*?)\)").expect("static markdown image regex literal is valid"));
static ADOC_IMAGE_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"image::(.*?)\[(.*?)\]").expect("static asciidoc image regex literal is valid"));
static MD_LINK_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"\[(.*?)\]\((.*?)\)").expect("static markdown link regex literal is valid"));

/// Accessibility analyzer
pub struct AccessibilityAnalyzer;
Expand Down Expand Up @@ -121,12 +131,9 @@ impl Analyzer for AccessibilityAnalyzer {

impl AccessibilityAnalyzer {
fn check_image_alt_text(&self, path: &Path, config: &Config, result: &mut AnalysisResult) {
use regex::Regex;

let md_image_re = Regex::new(r"!\[(.*?)\]\((.*?)\)")
.expect("static markdown image regex literal is valid");
let adoc_image_re = Regex::new(r"image::(.*?)\[(.*?)\]")
.expect("static asciidoc image regex literal is valid");
let md_image_re = &*MD_IMAGE_RE;
let adoc_image_re = &*ADOC_IMAGE_RE;

for entry in WalkDir::new(path)
.into_iter()
Expand Down Expand Up @@ -239,10 +246,8 @@ impl AccessibilityAnalyzer {
}

fn check_link_text(&self, path: &Path, config: &Config, result: &mut AnalysisResult) {
use regex::Regex;

let md_link_re = Regex::new(r"\[(.*?)\]\((.*?)\)")
.expect("static markdown link regex literal is valid");
let md_link_re = &*MD_LINK_RE;

let non_descriptive = ["click here", "here", "read more", "link", "more"];

Expand Down
8 changes: 6 additions & 2 deletions scripts/enroll-hypatia-fleet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,12 @@ while IFS= read -r repo_path; do

[[ -d "$repo_path/.machine_readable" ]] && has_machine_readable=true
[[ -d "$repo_path/.machine_readable/bot_directives" ]] && has_bot_directives=true
[[ -f "$repo_path/.machine_readable/META.a2ml" ]] && has_a2ml_meta=true
[[ -f "$repo_path/.machine_readable/STATE.a2ml" ]] && has_a2ml_state=true
# Canonical location is .machine_readable/6a2/ (SD004). Legacy flat layout is
# still accepted here because this script scans OTHER repos, many of which
# have not migrated yet -- narrowing to 6a2/ alone would silently mark them
# as lacking a manifest they actually have.
[[ -f "$repo_path/.machine_readable/6a2/META.a2ml" || -f "$repo_path/.machine_readable/META.a2ml" ]] && has_a2ml_meta=true
[[ -f "$repo_path/.machine_readable/6a2/STATE.a2ml" || -f "$repo_path/.machine_readable/STATE.a2ml" ]] && has_a2ml_state=true
if [[ -x "$repo_path/scripts/maintenance/run-maintenance.sh" || -x "$repo_path/run-maintenance.sh" ]]; then
has_maint_script=true
fi
Expand Down