Skip to content

ci: perry-codegen integration suites can no longer be silently omitted from per-PR CI (#7708) - #7748

Merged
proggeramlug merged 2 commits into
mainfrom
ci/7708-codegen-suite-coverage
Aug 10, 2026
Merged

ci: perry-codegen integration suites can no longer be silently omitted from per-PR CI (#7708)#7748
proggeramlug merged 2 commits into
mainfrom
ci/7708-codegen-suite-coverage

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #7708. Supersedes #7507 (which asked for six of these suites and was blocked on them being green).

1. Triage of the six

Re-ran cargo test -p perry-codegen suite-by-suite on main (v0.5.1432). Four of the six are fixed, by unrelated work, with nothing recording it:

test status
integer_arithmetic_array_push_omits_inbounds_layout_note_and_barrier fixed
integer_modulo::i32_counter_mod_unsafe_or_nonliteral_divisors_keep_frem fixed
proven_buffer_and_typed_array_reads_are_numeric_operands fixed
reassigned_typed_array_store_records_runtime_fallback fixed
large_object_barriers::large_local_array_push_inbounds_store_emits_precise_slot_barrier still red
native_proof_regressions::typed_f64_receiver_method_clone_raw_loads_after_composed_guards still red#7506

Four entries silently stopped being true, which is exactly the bookkeeping failure #797 recorded for the parity skip-list. That shaped the design below more than the count did.

2. The map was three suites; the crate has twenty-four

SOURCE_SUITE_MAP mapped crates/perry-codegen/src/ to shadow_slot_hygiene, scalar_replaced_slot_roots, temp_root_operand_temporaries. 23 of the 24 suites in that crate are the same kind of thing — in-process compiles of hand-built HIR with emit_ir_only: true, no perry compile subprocess, no link, no runtime. The narrowness was never a judgment about the other 20; it was what one commit had time to verify. And nothing could tell you: a suite in neither the map nor any rule is indistinguishable from a suite nobody needed to map.

Measured, one cargo test -p perry-codegen --test <suite> per row, arm64 macOS / debug:

app_window_config_options          10.34s  ok  2      native_proof_buffer_views      4.03s  ok 36
argless_builtin_extra_args          2.36s  ok  2      node_test_mock_property_...    2.88s  ok  1
class_field_store_pointer_test      2.25s  ok  4      perry_builtin_name_collision   2.62s  ok  5
class_keys_gc_root                  2.21s  ok  1      private_guard_declaring_class  2.48s  ok  1
constructor_recursion               2.20s  ok  1      scalar_replaced_slot_roots     2.93s  ok 11
destructure_call_location           2.23s  ok  2      shadow_slot_hygiene            2.47s  ok 12
i64_spec_ternary_recursion          2.16s  ok  2      static_symbol_hygiene          2.53s  ok  2
loop_safepoint_purity               3.87s  ok  8      temp_root_operand_temporaries  2.68s  ok 19
macos_bundle_chdir_gate             2.48s  ok  2      typed_feedback                 2.45s  ok 17
manifest_consistency                3.77s  ok  5      typed_shape_declared_at_alloc  2.71s  ok  9
                                                      typed_shape_descriptor         2.26s  ok  2
large_object_barriers              13.87s  FAIL 1     typed_shape_descriptors        2.44s  ok 18
native_proof_regressions           10.46s  FAIL 1

Nearly all of each number is cargo's freshness check; the test time is sub-second (shadow_slot_hygiene 12 tests / 0.10 s, native_proof_buffer_views 36 / 0.2 s). The cost is the build, which e2e-scoped already pays whenever any perry-codegen suite is in scope.

The map is now complete-by-construction: 22 suites, up from 3.

3. Omission can no longer be silent

_assert_map_covers_codegen_suites — run by --self-test, which e2e-scoped executes before it installs a toolchain — requires every crates/perry-codegen/tests/*.rs on disk to be either mapped or in the new SUITE_EXCLUSIONS. A new suite fails CI until someone classifies it. The map stays hand-written (there is still no coverage data), but an omission is no longer indistinguishable from a decision.

The sabotage case is in the self-test: it plants an unclassified suite and requires the raise. A rule that has never failed is a rule nobody has verified — CLAUDE.md hazard 4.

4. Exclusions are per-test and die when fixed

Holding out a whole 262-test suite for one failure is how 261 tests' worth of coverage went dark. So the two known failures are excluded by test name; the suites still run with --skip.

Self-invalidating in both directions:

  • --self-test rejects an entry whose suite is gone, or which is also mapped.
  • the job runs exactly the held-out tests and fails if one passes (or if no test matches that name), telling the fixer to delete the entry. A fix cannot land leaving its exclusion behind.

5. The cap would have re-created the bug one layer up

--cap (default 12) exists so a mass suite rename can't turn one PR into a multi-hour perry compile run. Applied to 22 mapped suites it would have silently truncated the set the map just completed. So the cap now applies to diff-named suites only, and the two tiers carry different per-suite bounds (MAPPED_SUITE_TIMEOUT_S=300 vs NAMED_SUITE_TIMEOUT_S=1500). The job's timeout-minutes math is updated in the same commit and the comment states the new arithmetic.

Validation

  • python3 scripts/ci_e2e_scope.py --self-test — passes, including the two new sabotage cases (unclassified suite must raise; cap must not touch mapped suites).
  • The workflow bash was executed, not eyeballed. Extracted the run step and drove it with a stubbed cargo across five scenarios: (1) all-as-recorded → exit 0, 22 suites, 2 known-failure checks, 0 errors; (2) an excluded test now passes → exit 1 with the delete-the-entry error; (3) a mapped suite regresses → exit 1; (4) a diff that names an excluded suite directly still gets --skip; (5) a non-codegen diff runs no known-failure checks.
  • The gate was run green before promotion, with real cargo against a built tree: 22 suites all green, both known-failure checks still failing as recorded, exit 0.

Note that this PR touches no crates/perry-codegen/src/ file, so e2e-scoped on this PR will select nothing — hence the local end-to-end run above. The first PR that touches codegen source is the first live exercise.

No version bump (maintainer bumps at merge).

Summary by CodeRabbit

  • Bug Fixes

    • Improved end-to-end test selection for code-generation changes, covering all mapped integration suites.
    • Replaced broad suite skips with targeted handling for known failing tests.
    • CI now detects stale exclusions or tests that unexpectedly pass, preventing inaccurate results.
  • Tests

    • Expanded per-PR coverage to 22 integration suites.
    • Added per-suite time limits and increased the overall workflow timeout to support uncapped suites.
    • Added validation to ensure suite mappings and exclusions remain complete and accurate.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 83197d3d-1b52-499f-9757-eb1fb666e80e

📥 Commits

Reviewing files that changed from the base of the PR and between 3fa5612 and e570cff.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • CLAUDE.md
  • Cargo.toml

📝 Walkthrough

Walkthrough

The scope tool now maps all 22 perry-codegen suites, validates exclusions, and emits suite-specific timeouts. The e2e workflow runs mapped suites without the cap, skips known failures, and verifies that exclusions still fail. The workspace version changes to 0.5.1438.

Changes

Codegen E2E coverage

Layer / File(s) Summary
Suite classification and selection
scripts/ci_e2e_scope.py
The source map covers 22 suites. Selection separates mapped and diff-named suites, validates exclusions, applies caps only to named suites, and emits timeout-bearing output.
Workflow execution and failure checks
.github/workflows/test.yml, changelog.d/7748-codegen-suite-coverage.md
The workflow receives exclusions, uses per-suite bounds, reruns excluded tests, and fails when exclusions pass or become invalid. The changelog records the coverage and validation changes.
Workspace version update
Cargo.toml, CLAUDE.md
The workspace package version and documented current version change from 0.5.1437 to 0.5.1438.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Possibly related PRs

  • PerryTS/perry#7509 — Both changes modify perry-codegen integration-suite CI and test handling.
  • PerryTS/perry#7599 — Both changes validate that failure exclusions remain failing and reject stale exclusions.
  • PerryTS/perry#7675 — Both changes update suite mapping and validation in scripts/ci_e2e_scope.py.

Suggested labels: tooling

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also bumps Cargo.toml and edits CLAUDE.md, which the template prohibits and which are unrelated to the CI objectives. Remove the Cargo.toml version bump and CLAUDE.md edit; leave release metadata changes to the maintainer at merge.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the CI change that prevents perry-codegen integration suites from being silently omitted.
Description check ✅ Passed The description provides detailed scope, linked issue, implementation changes, and validation, although it does not follow the template headings exactly.
Linked Issues check ✅ Passed The changes expand suite mapping, prevent omissions, retain failing-test visibility, and adjust caps and timeouts as required by #7708.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/7708-codegen-suite-coverage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/test.yml (1)

860-875: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Ensure timeout-minutes: 450 matches the platform cap.

This job uses runs-on: ubuntu-latest, so the GitHub-hosted 6-hour runner limit is the real job ceiling. A 450-minute job bound is not reachable; set the bound below the platform limit, or move this to a 5-day self-hosted runner if the scripted worst case actually needs more time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml around lines 860 - 875, Update the job-level
timeout-minutes setting in the workflow to remain below the GitHub-hosted
ubuntu-latest six-hour platform limit, while preserving sufficient coverage for
the documented worst-case suite duration. Do not move the job to a self-hosted
runner unless the calculated worst case genuinely exceeds the hosted-runner cap,
and keep the accompanying timeout rationale synchronized with the chosen bound.
🧹 Nitpick comments (2)
.github/workflows/test.yml (1)

1003-1005: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The gate and the loop use different package scopes.

Line 1003 gates the verification on perry-codegen appearing in $SUITES. The loop at line 1004 then iterates over every entry in $EXCLUSIONS, including entries for other packages. Today all exclusions are perry-codegen, so the behavior is correct. If someone adds an exclusion for another package, that entry is verified only when a perry-codegen suite is in scope, and never otherwise.

Filter each exclusion against the packages present in $SUITES instead of gating the whole block on one package name.

🛠️ Proposed change
-          if printf '%s\n' "$SUITES" | grep -q '^perry-codegen '; then
-            while read -r xpkg xsuite xtest; do
-              [ -n "$xpkg" ] || continue
+          while read -r xpkg xsuite xtest; do
+            [ -n "$xpkg" ] || continue
+            if printf '%s\n' "$SUITES" | grep -q "^$xpkg "; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml around lines 1003 - 1005, Update the
exclusion-processing block around the perry-codegen gate and its read loop so
exclusions are filtered by the package names present in $SUITES. Remove the
single-package gate, retain only entries whose package is in the active suites,
and run verification for those matching exclusions regardless of which package
is present.
scripts/ci_e2e_scope.py (1)

521-540: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the cap behavior, not only the split.

The comment states the cap applies to diff-named suites only. This case asserts select_split classification. It never applies cap. The cap is applied in main at lines 577-584, so a regression that caps mapped would still pass this self-test. Extract the truncation into a small helper and assert it here, or assert directly that mapped suites survive truncation.

♻️ Proposed additional assertion
         mapped_pairs, named_pairs = select_split(named, root)
         if mapped_pairs or len(named_pairs) != 20:
             print(
                 f"self-test FAILED: expected 0 mapped / 20 named, got "
                 f"{len(mapped_pairs)}/{len(named_pairs)}",
                 file=sys.stderr,
             )
             return 1
+        # The truncation itself: mapped is never cut, named is.
+        fake_mapped = [("perry-codegen", s) for s in _CODEGEN_SUITES]
+        capped = named_pairs[:DEFAULT_CAP]
+        if len(capped) != DEFAULT_CAP or len(fake_mapped) <= DEFAULT_CAP:
+            print("self-test FAILED: cap fixture no longer exercises truncation",
+                  file=sys.stderr)
+            return 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci_e2e_scope.py` around lines 521 - 540, Update the self-test around
select_split to exercise the cap behavior itself, not just classification:
extract the truncation logic currently in main into a small reusable helper,
then apply it to the test’s mapped and named results and assert mapped suites
remain intact while only diff-named suites are capped. Keep select_split
classification assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/test.yml:
- Around line 1007-1012: Update the excluded-test execution around the cargo
test command to preserve its exit status separately from captured output, while
still preventing immediate shell failure. Distinguish a completed test run that
reports one failure from infrastructure or execution failures such as
compilation errors, timeouts, or cargo errors; only report the SUITE_EXCLUSIONS
removal error when the test completed successfully or no matching test ran, and
emit an appropriate failure message for non-completed runs.
- Around line 976-997: Update the cargo test invocation in the suite loop to add
--exact whenever skips is non-empty, while preserving the current behavior when
no exclusions apply. Ensure each excluded test is matched by its exact name
rather than a substring.

In `@scripts/ci_e2e_scope.py`:
- Around line 82-104: Correct the rationale comment near the suite-map
declaration to use consistent counts: 24 suites exist, 22 are mapped, and 2 are
listed in SUITE_EXCLUSIONS, so 21 suites—not 23—are the in-process cases and 21
others were not part of the original narrow map. Ensure the prose matches the
changelog and allows the completeness invariant to be verified by hand.

---

Outside diff comments:
In @.github/workflows/test.yml:
- Around line 860-875: Update the job-level timeout-minutes setting in the
workflow to remain below the GitHub-hosted ubuntu-latest six-hour platform
limit, while preserving sufficient coverage for the documented worst-case suite
duration. Do not move the job to a self-hosted runner unless the calculated
worst case genuinely exceeds the hosted-runner cap, and keep the accompanying
timeout rationale synchronized with the chosen bound.

---

Nitpick comments:
In @.github/workflows/test.yml:
- Around line 1003-1005: Update the exclusion-processing block around the
perry-codegen gate and its read loop so exclusions are filtered by the package
names present in $SUITES. Remove the single-package gate, retain only entries
whose package is in the active suites, and run verification for those matching
exclusions regardless of which package is present.

In `@scripts/ci_e2e_scope.py`:
- Around line 521-540: Update the self-test around select_split to exercise the
cap behavior itself, not just classification: extract the truncation logic
currently in main into a small reusable helper, then apply it to the test’s
mapped and named results and assert mapped suites remain intact while only
diff-named suites are capped. Keep select_split classification assertions
unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7e6d48df-e029-47f6-bd97-79114f11457a

📥 Commits

Reviewing files that changed from the base of the PR and between 24031bf and 3fa5612.

📒 Files selected for processing (3)
  • .github/workflows/test.yml
  • changelog.d/7748-codegen-suite-coverage.md
  • scripts/ci_e2e_scope.py

Comment on lines +976 to 997
while read -r package suite bound; do
[ -n "$package" ] || continue
skips=""
while read -r xpkg xsuite xtest; do
[ -n "$xpkg" ] || continue
if [ "$xpkg" = "$package" ] && [ "$xsuite" = "$suite" ]; then
skips="$skips --skip $xtest"
echo "::notice::skipping known failure $package::$suite::$xtest"
fi
done <<< "$EXCLUSIONS"
echo "::group::cargo test -p $package --test $suite"
# Per-suite wall-clock bound: a hung compile must not eat the whole
# job budget and hide the other suites' results.
if ! timeout 1500 cargo test -p "$package" --test "$suite"; then
# job budget and hide the other suites' results. Mapped in-process
# suites carry a much tighter bound than a diff-named one that
# shells out to `perry compile` — see ci_e2e_scope.py.
# shellcheck disable=SC2086
if ! timeout "${bound:-1500}" cargo test -p "$package" --test "$suite" -- $skips; then
echo "::error::integration suite failed: $package --test $suite"
status=1
fi
echo "::endgroup::"
done <<< "$SUITES"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Rust libtest --skip substring match --exact flag behavior

💡 Result:

In Rust's libtest harness, the interaction between test filtering, the --skip flag, and the --exact flag works as follows: 1. --skip Behavior The --skip flag accepts a filter string and causes the test runner to skip any tests whose full path (including module structure) contains that string as a substring [1][2]. You can provide the --skip flag multiple times to exclude multiple sets of tests [3][2]. 2. --exact Flag Behavior The --exact flag modifies how filter strings are interpreted [2][4]. By default, positional arguments and filters are matched against any substring of the test's full path [1][2]. When --exact is used, it forces the harness to match the provided filter string exactly against the full path of the test function [1][2]. 3. Combined Interaction While --exact is primarily designed to control how the primary filter (the positional argument) is matched, it fundamentally changes the matching logic used by the runner. The internal implementation of libtest uses a matches_filter function to evaluate both inclusion (positional arguments) and exclusion (--skip) [3]. The --exact flag instructs the runner to use strict equality for these comparisons rather than substring containment [3][2]. In summary: - Default: Matches occur if the filter string is a substring of the test's full path [1][2]. - With --exact: Matches occur only if the filter string is identical to the test's full path [1][2]. - The --skip flag applies this matching logic in reverse, excluding tests that meet the defined criteria [3][5]. To pass these arguments via Cargo, they must be placed after the -- separator: cargo test -- --skip --exact

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate workflow files:"
fd -a 'test\.ya?ml$' .github/workflows 2>/dev/null || true

echo
echo "Relevant lines around test invocation and exclusions:"
if [ -f ".github/workflows/test.yml" ]; then
  nl -ba .github/workflows/test.yml | sed -n '940,1030p'
fi

echo
echo "Search for EXCLUSIONS and suite setup:"
rg -n 'EXCLUSIONS|SUITES|known-failure|timeout 300|--exact|--skip' .github/workflows/test.yml

Repository: PerryTS/perry

Length of output: 316


🌐 Web query:

Rust cargo test harness --exact flag does it affect multiple --skip filters substring match

💡 Result:

The --exact flag in Rust's libtest harness does affect how --skip filters are evaluated [1][2]. When --exact is used, it enforces that both inclusion filters (the positional arguments) and exclusion filters (the arguments provided to --skip) must match the full path of the test name exactly, rather than performing a substring match [1][3]. In standard behavior (without --exact), --skip matches any test whose name contains the provided filter string [1][2]. When --exact is active, any filter string passed to --skip must match a test's full path precisely to trigger the exclusion [1][4]. This is particularly useful when you want to avoid accidentally skipping multiple tests that share a common substring in their names [3]. Key points: - The --exact flag applies globally to the libtest harness arguments passed after the -- separator [1][4]. - It switches both inclusion and exclusion matching from substring-based to full-path-exact-matching [1][3]. - You can still pass multiple --skip filters when --exact is used, but each of those filters must now represent a full, exact test path [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Relevant lines around test invocation and exclusions:"
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/test.yml | sed -n '940,1030p'

echo
echo "Search for EXCLUSIONS/SUITES/known-failure matching:"
grep -nE 'EXCLUSIONS|SUITES|known-failure|timeout 300|--exact|--skip' .github/workflows/test.yml || true

Repository: PerryTS/perry

Length of output: 7625


Use exact matching for suite skips

In the suite loop, --skip <test> skips any test whose full path contains that substring, while --exact is required to skip only the named test. If another test includes an excluded test name as a substring, the suite skip list can bypass it silently. Add --exact to the cargo test invocation when skips is non-empty.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml around lines 976 - 997, Update the cargo test
invocation in the suite loop to add --exact whenever skips is non-empty, while
preserving the current behavior when no exclusions apply. Ensure each excluded
test is matched by its exact name rather than a substring.

Comment on lines +1007 to +1012
out="$(timeout 300 cargo test -p "$xpkg" --test "$xsuite" -- --exact "$xtest" 2>&1 || true)"
printf '%s\n' "$out"
if ! printf '%s\n' "$out" | grep -q '1 failed'; then
echo "::error::$xpkg::$xsuite::$xtest is listed in SUITE_EXCLUSIONS but did not fail (it passed, or no test matched that name). Delete its entry from SUITE_EXCLUSIONS in scripts/ci_e2e_scope.py and let the suite run it."
status=1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

A build or timeout failure is reported as "did not fail".

|| true at line 1007 discards the exit status. The check then relies on the string 1 failed being present in $out. A compile error, a timeout 300 kill, or a cargo invocation error all produce output without that string. The job then fails with a message that tells the author to delete the SUITE_EXCLUSIONS entry, which is the wrong instruction.

Capture the exit status and distinguish "the test ran and passed" from "the run did not complete".

🛠️ Proposed change
-              out="$(timeout 300 cargo test -p "$xpkg" --test "$xsuite" -- --exact "$xtest" 2>&1 || true)"
-              printf '%s\n' "$out"
-              if ! printf '%s\n' "$out" | grep -q '1 failed'; then
+              out="$(timeout 300 cargo test -p "$xpkg" --test "$xsuite" -- --exact "$xtest" 2>&1)" && rc=0 || rc=$?
+              printf '%s\n' "$out"
+              if [ "$rc" -eq 124 ] || ! printf '%s\n' "$out" | grep -qE '^test result:'; then
+                echo "::error::$xpkg::$xsuite::$xtest known-failure check did not complete (build error or timeout). This is not an exclusion bookkeeping problem."
+                status=1
+              elif ! printf '%s\n' "$out" | grep -q '1 failed'; then
                 echo "::error::$xpkg::$xsuite::$xtest is listed in SUITE_EXCLUSIONS but did not fail (it passed, or no test matched that name). Delete its entry from SUITE_EXCLUSIONS in scripts/ci_e2e_scope.py and let the suite run it."
                 status=1
               fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
out="$(timeout 300 cargo test -p "$xpkg" --test "$xsuite" -- --exact "$xtest" 2>&1 || true)"
printf '%s\n' "$out"
if ! printf '%s\n' "$out" | grep -q '1 failed'; then
echo "::error::$xpkg::$xsuite::$xtest is listed in SUITE_EXCLUSIONS but did not fail (it passed, or no test matched that name). Delete its entry from SUITE_EXCLUSIONS in scripts/ci_e2e_scope.py and let the suite run it."
status=1
fi
out="$(timeout 300 cargo test -p "$xpkg" --test "$xsuite" -- --exact "$xtest" 2>&1)" && rc=0 || rc=$?
printf '%s\n' "$out"
if [ "$rc" -eq 124 ] || ! printf '%s\n' "$out" | grep -qE '^test result:'; then
echo "::error::$xpkg::$xsuite::$xtest known-failure check did not complete (build error or timeout). This is not an exclusion bookkeeping problem."
status=1
elif ! printf '%s\n' "$out" | grep -q '1 failed'; then
echo "::error::$xpkg::$xsuite::$xtest is listed in SUITE_EXCLUSIONS but did not fail (it passed, or no test matched that name). Delete its entry from SUITE_EXCLUSIONS in scripts/ci_e2e_scope.py and let the suite run it."
status=1
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml around lines 1007 - 1012, Update the
excluded-test execution around the cargo test command to preserve its exit
status separately from captured output, while still preventing immediate shell
failure. Distinguish a completed test run that reports one failure from
infrastructure or execution failures such as compilation errors, timeouts, or
cargo errors; only report the SUITE_EXCLUSIONS removal error when the test
completed successfully or no matching test ran, and emit an appropriate failure
message for non-completed runs.

Comment thread scripts/ci_e2e_scope.py
Comment on lines +82 to +104
# What changed in #7708: this map listed THREE suites. `crates/perry-codegen/
# tests/` holds 24, and 23 of them are the same kind of thing — in-process
# compiles of hand-built HIR with `emit_ir_only: true`. The narrowness was not a
# judgment about the other 20; it was what one commit had time to verify. Nothing
# said so, and nothing could: a suite that is in neither the map nor any rule is
# indistinguishable from a suite nobody needed to map.
#
# So the map is now COMPLETE-BY-CONSTRUCTION for this crate. `_assert_map_covers_
# codegen_suites` (run by `--self-test`, which the `e2e-scoped` job executes
# before it does anything else) requires every `crates/perry-codegen/tests/*.rs`
# on disk to be either mapped here or named in `SUITE_EXCLUSIONS` below. A new
# suite fails CI until someone classifies it. That is the "cannot silently omit
# a suite" property #7708 asked for; the map is still hand-written, but an
# omission is no longer silent.
#
# Measured on arm64 macOS, debug, `--test-threads=1`, one `cargo test -p
# perry-codegen --test <suite>` per row: every one of the 22 mapped suites
# completes in 2.2-10.4 s wall clock, and nearly all of that is cargo's
# freshness check — the test time itself is sub-second (e.g. shadow_slot_hygiene
# 12 tests / 0.10 s, native_proof_buffer_views 36 / 0.2 s). The cost is the
# build, which `e2e-scoped` already pays whenever any perry-codegen suite is in
# scope. They are NOT like the 163 `perry` suites the general refusal protects
# against: no `perry compile` subprocess, no link, no runtime.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the suite counts in the rationale comment.

Line 83 states 24 suites on disk and that 23 are in-process compiles. The file maps 22 suites and excludes 2, which totals 24. The "23" and the "other 20" on line 85 do not match those numbers. The changelog fragment states "The other 21 were dark". Align the comment with the actual counts so a future reader can verify the completeness invariant by hand.

📝 Proposed comment correction
-# What changed in `#7708`: this map listed THREE suites. `crates/perry-codegen/
-# tests/` holds 24, and 23 of them are the same kind of thing — in-process
-# compiles of hand-built HIR with `emit_ir_only: true`. The narrowness was not a
-# judgment about the other 20; it was what one commit had time to verify. Nothing
+# What changed in `#7708`: this map listed THREE suites. `crates/perry-codegen/
+# tests/` holds 24, and all of them are the same kind of thing — in-process
+# compiles of hand-built HIR with `emit_ir_only: true`. The narrowness was not a
+# judgment about the other 21; it was what one commit had time to verify. Nothing
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# What changed in #7708: this map listed THREE suites. `crates/perry-codegen/
# tests/` holds 24, and 23 of them are the same kind of thing — in-process
# compiles of hand-built HIR with `emit_ir_only: true`. The narrowness was not a
# judgment about the other 20; it was what one commit had time to verify. Nothing
# said so, and nothing could: a suite that is in neither the map nor any rule is
# indistinguishable from a suite nobody needed to map.
#
# So the map is now COMPLETE-BY-CONSTRUCTION for this crate. `_assert_map_covers_
# codegen_suites` (run by `--self-test`, which the `e2e-scoped` job executes
# before it does anything else) requires every `crates/perry-codegen/tests/*.rs`
# on disk to be either mapped here or named in `SUITE_EXCLUSIONS` below. A new
# suite fails CI until someone classifies it. That is the "cannot silently omit
# a suite" property #7708 asked for; the map is still hand-written, but an
# omission is no longer silent.
#
# Measured on arm64 macOS, debug, `--test-threads=1`, one `cargo test -p
# perry-codegen --test <suite>` per row: every one of the 22 mapped suites
# completes in 2.2-10.4 s wall clock, and nearly all of that is cargo's
# freshness check — the test time itself is sub-second (e.g. shadow_slot_hygiene
# 12 tests / 0.10 s, native_proof_buffer_views 36 / 0.2 s). The cost is the
# build, which `e2e-scoped` already pays whenever any perry-codegen suite is in
# scope. They are NOT like the 163 `perry` suites the general refusal protects
# against: no `perry compile` subprocess, no link, no runtime.
# What changed in `#7708`: this map listed THREE suites. `crates/perry-codegen/
# tests/` holds 24, and all of them are the same kind of thing — in-process
# compiles of hand-built HIR with `emit_ir_only: true`. The narrowness was not a
# judgment about the other 21; it was what one commit had time to verify. Nothing
# said so, and nothing could: a suite that is in neither the map nor any rule is indistinguishable from a suite nobody needed to map.
#
# So the map is now COMPLETE-BY-CONSTRUCTION for this crate. `_assert_map_covers_
# codegen_suites` (run by `--self-test`, which the `e2e-scoped` job executes
# before it does anything else) requires every `crates/perry-codegen/tests/*.rs`
# on disk to be either mapped here or named in `SUITE_EXCLUSIONS` below. A new
# suite fails CI until someone classifies it. That is the "cannot silently omit
# a suite" property `#7708` asked for; the map is still hand-written, but an
# omission is no longer silent.
#
# Measured on arm64 macOS, debug, `--test-threads=1`, one `cargo test -p
# perry-codegen --test <suite>` per row: every one of the 22 mapped suites
# completes in 2.2-10.4 s wall clock, and nearly all of that is cargo's
# freshness check — the test time itself is sub-second (e.g. shadow_slot_hygiene
# 12 tests / 0.10 s, native_proof_buffer_views 36 / 0.2 s). The cost is the
# build, which `e2e-scoped` already pays whenever any perry-codegen suite is in
# scope. They are NOT like the 163 `perry` suites the general refusal protects
# against: no `perry compile` subprocess, no link, no runtime.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci_e2e_scope.py` around lines 82 - 104, Correct the rationale comment
near the suite-map declaration to use consistent counts: 24 suites exist, 22 are
mapped, and 2 are listed in SUITE_EXCLUSIONS, so 21 suites—not 23—are the
in-process cases and 21 others were not part of the original narrow map. Ensure
the prose matches the changelog and allows the completeness invariant to be
verified by hand.

@proggeramlug
proggeramlug force-pushed the ci/7708-codegen-suite-coverage branch from 3fa5612 to e570cff Compare August 10, 2026 07:52
@proggeramlug

Copy link
Copy Markdown
Contributor Author

Merging as v0.5.1438 — this closes #7708 properly rather than adding three more suites

The triage cross-checks exactly

"Four of the six are fixed, by unrelated work, with nothing recording it" matches what I measured independently today: #7730 took cargo test -p perry-codegen from 6 pre-existing failures to 2, and the two it left are precisely the two you list as still red (large_object_barriers::…precise_slot_barrier and native_proof_regressions::…composed_guards, the latter #7506). Two people arriving at the same set from different directions is the strongest form that claim could take.

And the observation that matters more than the count — four entries silently stopped being true — is the same bookkeeping failure #797 recorded for the parity skip-list. Letting that shape the design was the right call.

3 → 22, complete-by-construction

The old map wasn't a judgment about the other 20; it was what one commit had time to verify, and nothing could tell you that. A suite in neither the map nor any rule was indistinguishable from a suite nobody needed to map. _assert_map_covers_codegen_suites removes that ambiguity — and I verified it bites by planting an unclassified suite:

ci_e2e_scope: these crates/perry-codegen/tests/*.rs suites are in neither SOURCE_SUITE_MAP nor SUITE_EXCLUSIONS: zz_audit_planted_suite. … An unclassified suite is invisible to per-PR CI, which is #7708.

Naming the issue in the failure text is what makes the next person's five minutes cheap.

Two design decisions I'd have got wrong

Per-test exclusions, not per-suite. Holding out a whole 262-test suite for one failure is exactly how 261 tests' worth of coverage went dark in the first place. And they're self-invalidating in both directions — the job runs the held-out tests and fails if one passes, so a fix cannot land leaving its exclusion behind. That is the property the parity skip-list lacked.

The --cap catch is the sharpest thing here. A cap of 12 applied to 22 mapped suites would have silently truncated the set the map had just completed — re-creating #7708 one layer up, inside its own fix. Scoping the cap to diff-named suites only, with separate per-tier timeouts, is right, and the timeout-minutes arithmetic moving in the same commit is the detail that keeps it honest.

On the validation

Executing the workflow bash with a stubbed cargo across five scenarios — including "an excluded test now passes" and "a mapped suite regresses" — is the right way to test a CI change, because the usual failure mode is that the YAML is never run until it matters. Running the gate green with real cargo before promotion satisfies CLAUDE.md's corollary that a new gate must be observed green once first.

The honest note that this PR touches no crates/perry-codegen/src/ file, so e2e-scoped on this PR selects nothing is worth keeping visible: the first codegen-touching PR is the first live exercise. Worth watching that one.

Gates 19/19, --self-test clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI cannot see crates/*/tests/*.rs regressions: per-PR runs --lib --bins, and SOURCE_SUITE_MAP is hand-maintained (2 failures beyond #7494)

1 participant