ci: run the action-matrix sample on its own; name a hang in an edit round (#1037)

The sampled pi-case hole on Body1/F13:side:l7@F14.1 became a real hole with #952 (7.5 s instead of 170 ms); beside the heavy tests on main's push runs it passed the matrix's 90 s action limit in its edit round, reported as the design not loading. The sample now takes every CPU in nextest so nothing runs beside it, and the edit round writes a start line so a hang there is reported as <action>+edit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-14 07:19:25 -04:00
1 parent 626fe6fdee
commit d506382aca
2 files changed
+18 -3

No files matched your search

+8
View File
@@ -28,3 +28,11 @@ slow-timeout = { period = "60s" }
[profile.ci.junit]
path = "junit.xml"
# The action matrix sample runs its designs in parallel worker processes, one per CPU. Run with
# other tests beside it (the heavy ones on main), a pi-case hole that takes 8 s alone (#952 makes
# it a real hole) went past the matrix's 90 s action limit. It runs on its own.
[[profile.ci.overrides]]
filter = "binary(action_matrix) & test(=action_matrix_sample)"
threads-required = "num-cpus"
+10 -3
View File
@@ -693,6 +693,8 @@ fn action_matrix_worker() {
&& let Some((name, expr)) = &edit
&& stable_hash(&format!("{k} edit")).is_multiple_of(EDIT_SAMPLE)
{
// Marked like an action, so a hang here is put down to the edit round (not to loading).
emit(json!({"start": i, "edit": true}));
let t = Instant::now();
let start = s.doc.features.len();
let r = scratch
@@ -785,7 +787,9 @@ fn run_design(design: &str, out: &Path, sample: u64) -> Vec<Value> {
.map(|f| std::io::BufReader::new(f).lines().map_while(Result::ok).filter_map(|l| serde_json::from_str(&l).ok()).collect())
.unwrap_or_default();
let done = lines.iter().any(|l| l.get("done").is_some());
let last_start = lines.iter().rev().take_while(|l| l.get("start").is_some() || l.get("design").is_none()).find_map(|l| l["start"].as_u64());
let last = lines.iter().rev().take_while(|l| l.get("start").is_some() || l.get("design").is_none()).find(|l| l.get("start").is_some());
let last_start = last.and_then(|l| l["start"].as_u64());
let in_edit = last.is_some_and(|l| l["edit"] == true);
results = lines.iter().filter(|l| l.get("design").is_some()).cloned().collect();
if done {
break;
@@ -800,7 +804,7 @@ fn run_design(design: &str, out: &Path, sample: u64) -> Vec<Value> {
};
// Which action was it? The child enumerates them; ask it by running from there with a
// marker: simplest is to record the index and let the next run name it.
extra.push(json!({"design": design, "index": i, "kind": if timed_out { "timeout" } else { "crash" },
extra.push(json!({"design": design, "index": i, "edit": in_edit, "kind": if timed_out { "timeout" } else { "crash" },
"message": if timed_out { format!("no result after {} s", ACTION_TIMEOUT.as_secs()) } else { format!("the process ended: {status:?}") }}));
from = i as usize + 1;
}
@@ -809,7 +813,10 @@ fn run_design(design: &str, out: &Path, sample: u64) -> Vec<Value> {
let names = action_names(design);
for e in &mut extra {
if let Some(i) = e.get("index").and_then(Value::as_u64) {
let (entity, action) = names.get(i as usize).cloned().unwrap_or_else(|| (format!("action{i}"), "unknown".into()));
let (entity, mut action) = names.get(i as usize).cloned().unwrap_or_else(|| (format!("action{i}"), "unknown".into()));
if e["edit"] == true {
action += "+edit";
}
*e = json!({"design": design, "entity": entity, "action": action, "ok": false, "kind": e["kind"], "message": e["message"]});
}
}