use crate::rules::{Evidence, Finding, Rule, Stance, Trend};
use crate::shell::Parsed;
pub const RULE: Rule = Rule {
id: "forge-status-stale-row",
default_stance: Stance::Observe,
evidence: Evidence {
per_1000: 0.8,
measured: "2026-09-10",
trend: Trend::Rare,
},
examine,
confirm: None,
};
fn is_http_client(program: &str) -> bool {
matches!(
program,
"curl" | "wget" | "http" | "https" | "httpie" | "xh"
)
}
fn is_commit_statuses_path(text: &str) -> bool {
let mut rest = text;
while let Some(i) = rest.find("/commits/") {
let after = &rest[i + "/commits/".len()..];
let seg_end = after.find('/').unwrap_or(after.len());
let tail = &after[seg_end..];
if seg_end > 0
&& (tail == "/statuses"
|| tail.starts_with("/statuses?")
|| tail.starts_with("/statuses/"))
{
return true;
}
rest = after;
}
false
}
const FIRST_WINS: &[&str] = &[
"not in seen",
"not in best",
"not in got",
"not in acc",
"setdefault(",
"map(.[0])",
"next(iter(",
"head -1",
"head -n 1",
"head -n1",
];
fn bare_first_wins(text: &str) -> bool {
for m in FIRST_WINS {
let mut from = 0usize;
while let Some(i) = text[from..].find(m) {
let abs = from + i;
let line_end = text[abs..].find('\n').map_or(text.len(), |j| abs + j);
let rest = &text[abs + m.len()..line_end];
if !rest.contains(" or ") && !rest.contains("||") {
return true;
}
from = abs + m.len();
}
}
false
}
fn examine(parsed: &Parsed) -> Option<Finding> {
for cmd in parsed.judgeable() {
let Some(client) = cmd
.words
.iter()
.find(|w| !w.quoted && is_http_client(&w.text))
.map(|w| w.text.clone())
.or_else(|| {
(cmd.program() == Some("gh") && cmd.subcommand() == Some("api"))
.then(|| "gh api".to_string())
})
else {
continue;
};
let Some(hit) = cmd.words.iter().find(|w| is_commit_statuses_path(&w.text)) else {
continue;
};
if !parsed
.clauses()
.iter()
.flat_map(|c| c.words.iter())
.any(|w| bare_first_wins(&w.text))
{
continue;
}
return Some(Finding {
reason: format!(
"`{client}` reads a commit's `/statuses` list, which is append-only — one \
row per context per TRANSITION, newest first — and this keeps the FIRST \
row seen, which reads a stale one as the present. A job that starts and \
is skipped inside one second emits `pending` and `success` with the SAME \
timestamp, and the API returns the pending row first, so that check reads \
`pending` forever and a wait built on it never exits."
),
remedy: "Dedupe by context with a TERMINAL-wins rule, not first-wins: a row \
whose status is success/failure/error/skipped always replaces a \
pending one for that context. Count `skipped` as passing — a \
path-filtered job legitimately never reports success. The roll-up \
`/commits/{sha}/status` (singular) sidesteps this, but it will not \
tell you WHICH check is red."
.to_string(),
span: hit.at..cmd.end,
});
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shell::lex;
fn fires(command: &str) -> bool {
examine(&lex(command)).is_some()
}
#[test]
fn the_incident_shape_fires() {
assert!(fires(
r#"curl -sS -H "Authorization: token $TOK" "https://git.daddyshome.fr/api/v1/repos/o/r/commits/861a02d3/statuses" | python3 -c "
import sys,json
d=json.load(sys.stdin)
seen={}
for r in d:
c=r.get('context')
if c not in seen: seen[c]=r.get('status')
""#
));
}
#[test]
fn the_until_loop_form_fires() {
assert!(fires(
r#"until curl -sS "https://forge/api/v1/repos/o/r/commits/$SHA/statuses" | python3 -c "
seen={}
for r in rows:
if r['context'] not in seen: seen[r['context']]=r['status']
"; do sleep 20; done"#
));
}
#[test]
fn the_jq_first_of_group_form_fires() {
assert!(fires(
r#"gh api "/repos/o/r/commits/abc123/statuses" | jq 'group_by(.context) | map(.[0])'"#
));
}
#[test]
fn the_singular_status_rollup_is_silent() {
assert!(!fires(
r#"curl -sS "https://forge/api/v1/repos/o/r/commits/$SHA/status" | python3 -c "
seen={}
if c not in seen: seen[c]=1
""#
));
}
#[test]
fn reading_without_a_first_wins_reduction_is_silent() {
assert!(!fires(
r#"curl -sS "https://forge/api/v1/repos/o/r/commits/$SHA/statuses" | jq -r '.[] | "\(.status) \(.context)"'"#
));
}
#[test]
fn the_terminal_wins_fix_is_silent() {
assert!(!fires(
r#"curl -sS "https://forge/api/v1/repos/o/r/commits/$SHA/statuses" | python3 -c "
TERMINAL={'success','failure','error','skipped'}
best={}
for r in rows:
c=r.get('context'); st=r.get('status')
if c not in best or (best[c] not in TERMINAL and st in TERMINAL):
best[c]=st
""#
));
}
#[test]
fn a_first_wins_reduction_elsewhere_is_silent() {
assert!(!fires(
r#"cat log.json | python3 -c "
seen={}
for r in rows:
if r['k'] not in seen: seen[r['k']]=r['v']
""#
));
assert!(!fires("git status"));
}
}