From 30e976ae1e6976431fc8cfd47176757e967494d1 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Thu, 13 Aug 2026 16:43:05 +0200 Subject: [PATCH] docs,test: record the fourth managed_retry occurrence, and unblock the nested suite **The gate's own run reproduced a REGISTERED signature**, and it is recorded as a fourth occurrence rather than waved through: same selector, same `gpu`-step flavor, all three required fragments verified against the durable log. Three isolated re-runs were green, which this file's rule says establishes intermittence only. **This lane is code-neutral for `pmacs-gpu` but NOT environment-neutral**, and that distinction is the entry's point. Occurrence 3 excluded "the added GPU test is the mechanism"; this occurrence adds nothing to that binary at all, which corroborates the exclusion independently. But the lane moves `TMPDIR` off `/tmp`, taking every `tempfile::tempdir()` in the run from **tmpfs to btrfs** --- and the failing test runs a handshake against a **one-second deadline**. A slower filesystem under a timing-bounded test is a plausible mechanism that did not exist in occurrences 1-3. Booking this as "the usual flake" when the observing lane changed the conditions the flake is sensitive to is exactly the reasoning this registry exists to prevent. Also: the suite's own roots move to a short base. Rooting them under the ambient `TMPDIR` put a NESTED gate's TMPDIR near 70 bytes, which legitimately tripped its own SUN_LEN guard --- the suite failing on a configuration it created rather than on the behaviour under test. And the marker row's `.then(..).unwrap_or_else(..)` chain is gone. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --- docs/ci-red-signatures.md | 39 +++++++- tests/gate_script_acceptance.rs | 155 +++++++++++++++++++++++++------- 2 files changed, 160 insertions(+), 34 deletions(-) diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index 6cf2aea..83b4f47 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -496,7 +496,7 @@ Stage 4; the lane touches no `pmacs-gpu` code at all. | **selector** | `-p pmacs-gpu attach::tests::managed_retry_survives_transients_and_uses_the_successful_stream` | | **job / flavor** | local (Linux), `cargo test --workspace --features crdt --no-fail-fast`, i.e. under full-sweep load | | **required fragments** | `transient sequence must attach` + `Handshake(Io(` + `BrokenPipe` (or `code: 32`) | -| **status** | **THIRD OCCURRENCE 2026-08-09 — causal status still UNRESOLVED, but one candidate mechanism is now EXCLUDED** | +| **status** | **FOURTH OCCURRENCE 2026-08-13 — causal status still UNRESOLVED. A NEW candidate mechanism is introduced by the observing lane and is NOT excluded (see below)** | | **what IS established** | **three** occurrences at `pmacs-gpu/src/attach.rs:1680`, the second and third with all three fragments **verified** rather than inferred; the test drives a scripted transient-then-success sequence over a real socket pair. **The added GPU test is not the mechanism** — see the third-occurrence control below | | **what is NOT** | whether the broken pipe is the *fixture's* writer closing early or a real retry-path defect. **This row is not a claim that it is harmless** | | **rerun evidence** | occurrence 1: 6 isolated runs green, plus a full `--workspace --features crdt` sweep green (113 targets). Occurrence 2: **30 green on the observing branch** (15 isolated selector, 15 full `-p pmacs-gpu`) **plus a 15-run merge-base control, also green**. Occurrence 3: 5 isolated selector runs green, 10 full `-p pmacs-gpu` runs green **with** the added test, and **1 failure in 10 with the added test `#[ignore]`d** — the first rerun in this row's history that reproduced anything. Per the rerun rule the green runs establish intermittence only; the red control run is what carries the exclusion | @@ -537,6 +537,43 @@ one-second deadline. Contention is a plausible mechanism for a lands, **run the control with the added test removed** rather than at the merge base — that is the discriminating comparison this one was not. +**Fourth occurrence — the `scripts/gate` TMPDIR isolation lane, +2026-08-13, local (Linux). Same selector, same `gpu`-step flavor +(`PMACS_REQUIRE_GPU=1 cargo test -p pmacs-gpu`), all three fragments +verified** against the durable gate log +(`20260813T143421Z-708100/07-gpu.log`): `transient sequence must +attach: Attach(Handshake(Io(Os { code: 32, kind: BrokenPipe, message: +"Broken pipe" })))`. + +**One thing this occurrence ESTABLISHES.** The observing lane touches +**no `pmacs-gpu` file at all** (`git diff ca92796..HEAD -- pmacs-gpu/` +is empty) and adds **no test to that binary**. Occurrence 3 excluded +"the added GPU test is the mechanism" by a control; this occurrence +reproduces the signature with *nothing added to the binary*, which is +independent corroboration rather than a repeat of the same argument. + +**AND ONE THING IT INTRODUCES, NAMED RATHER THAN DISMISSED — the +observing lane is not environmentally neutral even though it is +code-neutral.** That lane moves the gate's `TMPDIR` off `/tmp`, which +on this machine changes the filesystem underneath every +`tempfile::tempdir()` in the run **from tmpfs to btrfs**. This test +creates a tempdir and runs a handshake against a **one-second +deadline** (`pmacs-gpu/src/attach.rs`). A slower filesystem under a +timing-bounded test, under full-sweep load, is a plausible mechanism +and **it did not exist in occurrences 1–3**. + +It is not established either: the failing I/O is on a `UnixStream::pair`, +not on the tempdir path, and the tempdir is created but never bound. +**What this row must not do is book the occurrence as "the usual flake" +when the observing lane changed the very conditions the flake is +sensitive to.** + +**The discriminating comparison for a fifth occurrence** is therefore +the same lane's gate run with `TMPDIR` pointed back at a tmpfs — the +one variable this lane moves — rather than another merge-base control. +Three isolated re-runs on the current tree were green, which by this +file's own rule establishes intermittence only. + **Third occurrence — worker identity Stage 1 review round 2, 2026-08-09, local (Linux). Same selector, same `gpu`-step flavor, all three fragments verified** against the durable gate log diff --git a/tests/gate_script_acceptance.rs b/tests/gate_script_acceptance.rs index a2073c0..cb9a4f5 100644 --- a/tests/gate_script_acceptance.rs +++ b/tests/gate_script_acceptance.rs @@ -39,6 +39,26 @@ fn gate() -> PathBuf { } /// Run `scripts/gate` with an isolated managed root, from `cwd`. +/// A SHORT base for the roots these tests hand the gate, independent of +/// the ambient `TMPDIR`. +/// +/// **Not `tempfile::tempdir()`'s default, and the reason is the socket +/// budget rather than taste.** When this suite runs inside a gate, the +/// ambient `TMPDIR` is already that gate's own (~47 bytes); rooting a +/// nested gate under it pushes the nested `TMPDIR` to ~70 bytes and +/// legitimately trips its own `SUN_LEN` guard. The suite would then +/// fail on a configuration it created rather than on the behaviour +/// under test — which is exactly how it failed once. +/// +/// `/tmp` is named explicitly because it is short and this suite +/// already requires a Unix environment. These roots hold synthetic +/// plans and never fixtures, so `/tmp`'s contents are irrelevant to +/// them; the rows set `PMACS_GATE_ALLOW_ANCESTOR_MARKER` for that +/// reason. +fn short_root_base() -> PathBuf { + PathBuf::from("/tmp") +} + fn run_in(cwd: &Path, root: &Path, args: &[&str]) -> (String, String, bool) { let out = Command::new(gate()) .args(args) @@ -74,7 +94,10 @@ fn run(root: &Path, args: &[&str]) -> (String, String, bool) { #[test] fn the_plan_sweeps_the_workspace_and_never_only_the_tests() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (plan, _, ok) = run(root.path(), &["--print-plan"]); assert!(ok, "--print-plan must succeed"); @@ -100,7 +123,10 @@ fn the_plan_sweeps_the_workspace_and_never_only_the_tests() { #[test] fn the_plan_runs_the_library_tests_in_both_feature_configurations() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (plan, _, _) = run(root.path(), &["--print-plan"]); assert!(plan.contains("cargo test --lib\n"), "plan was:\n{plan}"); assert!( @@ -115,7 +141,10 @@ fn the_plan_runs_the_library_tests_in_both_feature_configurations() { /// default one in place — and the default run must not carry it. #[test] fn the_crdt_workspace_sweep_is_added_by_protocol_and_absent_without_it() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let crdt_sweep = "cargo test --workspace --features crdt --no-fail-fast"; let (default_plan, _, _) = run(root.path(), &["--print-plan"]); @@ -163,7 +192,10 @@ fn the_crdt_workspace_sweep_is_added_by_protocol_and_absent_without_it() { /// below, which reads the plan in the form the runner reads it. #[test] fn the_crdt_sweep_is_immediately_preceded_by_the_build_that_produces_its_binary() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let build = "cargo build --workspace --no-default-features --features luajit,crdt"; let crdt_sweep = "cargo test --workspace --features crdt --no-fail-fast -- --skip basedpyright"; @@ -230,7 +262,10 @@ fn the_crdt_sweep_is_immediately_preceded_by_the_build_that_produces_its_binary( /// `--acceptance` refusal below exists to prevent. #[test] fn the_crdt_build_step_carries_its_own_name_and_its_exact_command() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let build = "build-crdt\tcargo build --workspace --no-default-features --features luajit,crdt"; let sweep = "sweep-crdt\tcargo test --workspace --features crdt --no-fail-fast -- --skip basedpyright"; @@ -294,7 +329,10 @@ fn the_crdt_build_step_carries_its_own_name_and_its_exact_command() { /// tab would silently run under an empty command. #[test] fn the_named_plan_is_the_printed_plan_with_its_names_removed() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); for flags in [ vec![], @@ -346,7 +384,10 @@ fn the_named_plan_is_the_printed_plan_with_its_names_removed() { /// every ordinary lane. #[test] fn the_crdt_build_is_absent_without_protocol() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (plan, _, ok) = run(root.path(), &["--print-plan"]); assert!(ok, "--print-plan must succeed"); assert!( @@ -383,7 +424,10 @@ fn the_crdt_build_is_absent_without_protocol() { /// the same defect the `--acceptance` refusal above exists to prevent. #[test] fn self_test_names_the_failing_gate_and_the_suite_continues_past_it() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (out, err, ok) = run(root.path(), &["--self-test"]); assert!( @@ -455,7 +499,10 @@ fn self_test_names_the_failing_gate_and_the_suite_continues_past_it() { /// reads a real child's environment out of a real gate log. #[test] fn the_isolated_tmpdir_reaches_a_spawned_child_under_the_managed_root() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (out, err, _ok) = run(root.path(), &["--self-test"]); let announced = out @@ -518,7 +565,10 @@ fn the_isolated_tmpdir_reaches_a_spawned_child_under_the_managed_root() { /// created by the tool itself and on every single run. #[test] fn the_isolated_tmpdir_is_reaped_when_the_run_ends() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (out, _err, _ok) = run(root.path(), &["--self-test"]); let announced = out @@ -565,14 +615,8 @@ fn a_project_marker_above_the_gate_tmpdir_is_refused() { // control what sits above the root, and `/tmp` may already carry a // marker — which would make the row pass for the wrong reason. let base = tempfile::Builder::new() - .prefix("gate-marker-") - .tempdir_in( - repo_root() - .join("target") - .exists() - .then(|| repo_root().join("target")) - .unwrap_or_else(std::env::temp_dir), - ) + .prefix("gm-") + .tempdir_in(short_root_base()) .expect("base"); let root = base.path().join("inner"); std::fs::create_dir_all(&root).expect("root"); @@ -607,7 +651,10 @@ fn a_project_marker_above_the_gate_tmpdir_is_refused() { /// handed — each one, in order. #[test] fn acceptance_suites_reach_the_plan_in_the_order_given() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (plan, _, _) = run( root.path(), &[ @@ -634,7 +681,10 @@ fn acceptance_suites_reach_the_plan_in_the_order_given() { #[test] fn printing_the_target_dir_creates_nothing() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (dir, _, ok) = run(root.path(), &["--print-target-dir"]); assert!(ok, "--print-target-dir must succeed"); assert!(!dir.trim().is_empty(), "it must print a path"); @@ -646,7 +696,10 @@ fn printing_the_target_dir_creates_nothing() { #[test] fn init_writes_the_ownership_marker_and_is_idempotent() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (dir, _, ok) = run(root.path(), &["--init"]); assert!(ok, "--init must succeed"); let dir = PathBuf::from(dir.trim()); @@ -686,8 +739,14 @@ fn init_writes_the_ownership_marker_and_is_idempotent() { /// belt-and-braces against git's behaviour not being contractual. #[test] fn a_symlinked_spelling_of_a_worktree_derives_the_same_directory() { - let root = tempfile::tempdir().expect("tempdir"); - let link_home = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); + let link_home = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let link = link_home.path().join("via-symlink"); if std::os::unix::fs::symlink(repo_root(), &link).is_err() { return; // no symlink support; nothing to assert @@ -724,7 +783,10 @@ fn prune_fixture(root: &Path) -> (PathBuf, PathBuf, PathBuf) { #[test] fn prune_is_a_dry_run_by_default_and_deletes_nothing() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (orphan, lookalike, live) = prune_fixture(root.path()); let (out, _, ok) = run(root.path(), &["--prune"]); @@ -741,7 +803,10 @@ fn prune_is_a_dry_run_by_default_and_deletes_nothing() { #[test] fn force_deletes_only_the_orphan() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (orphan, lookalike, live) = prune_fixture(root.path()); let (out, _, ok) = run(root.path(), &["--prune", "--force"]); @@ -789,8 +854,14 @@ impl Drop for WorktreePruneGuard { #[test] fn a_registered_worktree_whose_directory_was_deleted_is_prunable() { - let root = tempfile::tempdir().expect("tempdir"); - let home = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); + let home = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let wt = home.path().join("gate-prunable-probe"); let added = Command::new("git") @@ -847,9 +918,15 @@ fn a_registered_worktree_whose_directory_was_deleted_is_prunable() { /// grounds that no test noticed. #[test] fn prune_outside_a_repository_refuses_and_every_directory_survives() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (orphan, lookalike, live) = prune_fixture(root.path()); - let outside = tempfile::tempdir().expect("tempdir"); + let outside = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); // Sanity: the fixture's orphan really is eligible from inside a repo. let (inside, _, _) = run(root.path(), &["--prune"]); @@ -879,7 +956,10 @@ fn prune_outside_a_repository_refuses_and_every_directory_survives() { /// gate runs. #[test] fn acceptance_names_with_shell_metacharacters_are_refused() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let canary = root.path().join("canary"); std::fs::write(&canary, "intact").expect("write canary"); @@ -920,7 +1000,10 @@ fn acceptance_names_with_shell_metacharacters_are_refused() { /// the test above by rejecting everything. #[test] fn ordinary_acceptance_names_are_accepted() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); for name in ["m4_acceptance", "gate-script", "abc123_x"] { let (plan, err, ok) = run(root.path(), &["--acceptance", name, "--print-plan"]); assert!(ok, "{name} must be accepted; stderr was:\n{err}"); @@ -936,7 +1019,10 @@ fn ordinary_acceptance_names_are_accepted() { /// directory on the strength of a file the script did not understand. #[test] fn a_multi_line_marker_is_refused_rather_than_read_head_first() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let bad = root.path().join("bad-00000000"); std::fs::create_dir_all(&bad).expect("mkdir"); std::fs::write( @@ -964,7 +1050,10 @@ fn a_multi_line_marker_is_refused_rather_than_read_head_first() { /// is how one learns too late that the marker was never written. #[test] fn skipped_directories_are_reported_with_a_reason() { - let root = tempfile::tempdir().expect("tempdir"); + let root = tempfile::Builder::new() + .prefix("g-") + .tempdir_in(short_root_base()) + .expect("tempdir"); let (_, lookalike, _) = prune_fixture(root.path()); let (out, _, _) = run(root.path(), &["--prune"]);