Skip to content

feat: mode=release advisory tier — gate on correctness, not the perf-flake tail #74

Description

@Jammy2211

Overview

workspace-validation.yml mode=release currently conflates correctness and perf-flakiness into a single RED. After the 2026-07-13 tail burn-down (PyAutoHeart#72) fixed every real bug, the integrate stage still can't reach a clean verdict because a population of genuinely-slow real-search scripts (real Nautilus fits + finite-difference JAX gradients) flakes around the per-script timeout cap — the failing set shifts every run (shared_preloads literally flip-flopped between re-val #3 and #4). Raising the cap and adding no_run SLOW-skips one script at a time never converges and erodes coverage.

This introduces an advisory tier: a timeout on a declared-slow script is downgraded to YELLOW (loud, tracked), while a timeout on any undeclared script stays RED. This is the path that lets mode=release reach its normal shippable GREEN/YELLOW once the real bugs are fixed, instead of being held hostage by the perf tail.

Plan

  • Add a companion config/build/advisory.yaml per workspace — same flat-list + inline-comment format as no_run.yaml, but the listed scripts still run and report; only their timeouts are advisory. Growing this list never erodes coverage, so it converges where SLOW-skips whack-a-mole. Absent file → today's behaviour exactly.
  • Build (runner mechanism): a new TIMEOUT_ADVISORY result status; the runner tags a timeout as advisory when the script matches advisory.yaml; has_failures / aggregate ready exclude advisory timeouts; a non-advisory timeout still fails the run.
  • Heart (policy): the stage report and validation_report carry an advisory_timeouts count; readiness maps advisory_timeouts > 0 to a YELLOW reason (not RED), keeping the release GREEN→YELLOW rather than RED-blocked.
  • Keep it loud: advisory timeouts surface in the aggregate report, the CI step summary, and Heart's YELLOW reasons — a tracked backlog, with durable speedups owned by the Profiling Agent.
  • Ship the mechanism in PyAutoHeart + PyAutoBuild first (self-contained, unit-tested, graceful fallback); seed advisory.yaml in the *_test workspaces from the re-val PyAutoPulse v1.3 — stop --all (recover orphan daemon) #3/PyAutoPulse v1.4 — classify generated json/png as dirty-noise #4 flip-flop population as a ship_workspace follow-up.
Detailed implementation plan

Affected Repositories

  • PyAutoHeart (primary) — policy: heart/validate.py, heart/readiness.py, .github/workflows/workspace-validation.yml, docs/release_validation.md
  • PyAutoBuild — runner mechanism: autobuild/result_collector.py, autobuild/run_python.py, autobuild/build_util.py, autobuild/aggregate_results.py

Branch Survey

Repository Current Branch Dirty?
./PyAutoHeart main clean
./PyAutoBuild main clean

No worktree conflict (active claims are all on the science libraries). Suggested branch: feature/release-advisory-tier-slow-scripts

The failure path being severed

build_util timeout → Status.TIMEOUTRunReport.has_failuresaggregate_results.ready=false → workflow "Fail if not ready" + emit_release_report status=fail → validate.py stage integrate=fail → release_ready=falsereadiness.py hit("validation_failed")RED.

Implementation Steps

PyAutoBuild (runner mechanism):

  1. autobuild/result_collector.py — add Status.TIMEOUT_ADVISORY = "timeout_advisory"; RunReport.has_failures excludes it; summary/to_dict carry it.
  2. autobuild/run_python.py — load config/build/advisory.yaml (workspace override → empty default, mirroring the no_run.yaml resolution), parse its reasons, thread advisory_list/advisory_reasons into execute_scripts_in_folder.
  3. autobuild/build_util.py — in execute_script (and execute_notebook for symmetry), on subprocess.TimeoutExpired, if the file matches advisory_list (reuse should_skip path semantics) record TIMEOUT_ADVISORY with the advisory reason instead of TIMEOUT.
  4. autobuild/aggregate_results.pytimeout_advisory is not in the failure set (so ready is unaffected); add an advisory_timeouts list + a loud "## Advisory-Tier Timeouts (slow real-search; not release-blocking)" markdown section. A non-advisory timeout still flips ready=false.

PyAutoHeart (policy):
5. heart/validate.pyto_stage_report carries advisory_timeouts (count + entries) from the aggregate into the stage report (NOT folded into failures); _Accumulator + report schema sum it into the top-level validation_report; status still derives from aggregate.ready. Bump the schema docstring.
6. heart/readiness.py — in the release-validation gate, when the report has advisory_timeouts > 0, append a YELLOW reason ("N advisory-tier script timeout(s) in release validation (slow real-search; not release-blocking)") + hit("validation_advisory_timeout") with a _SCORES entry. This turns a correctness-clean run GREEN→YELLOW instead of RED.
7. .github/workflows/workspace-validation.yml — comment the tier; echo the advisory-timeout count into $GITHUB_STEP_SUMMARY. No gate-logic change (already keys off ready).
8. docs/release_validation.md — document the advisory tier and the # ADVISORY <date> - reason marker convention.

Tests

  • PyAutoBuild/tests/test_result_collector.py (+ new aggregate test): advisory-matched timeout → TIMEOUT_ADVISORY, has_failures false, ready true, advisory_timeouts populated; non-advisory timeout → ready false.
  • PyAutoHeart/tests/test_validate.py: to_stage_report carries the advisory count and stage status stays pass; readiness maps advisory_timeouts > 0 → YELLOW (not RED).

Key Files

  • PyAutoBuild/autobuild/result_collector.pyStatus, RunReport.has_failures
  • PyAutoBuild/autobuild/build_util.pyexecute_script/execute_notebook timeout branch, should_skip
  • PyAutoBuild/autobuild/run_python.py — advisory.yaml load + thread-through
  • PyAutoBuild/autobuild/aggregate_results.pyaggregate() failure set + report section
  • PyAutoHeart/heart/validate.pyto_stage_report, _Accumulator, schema
  • PyAutoHeart/heart/readiness.py — release-validation gate YELLOW reason
  • PyAutoHeart/.github/workflows/workspace-validation.yml — step summary
  • config/build/advisory.yaml (new, per *_test workspace) — the declared-slow registry

Design notes

  • advisory.yaml vs a duration threshold: an explicit list is reviewable and stable; a duration threshold would re-capture exactly the borderline near-cap scripts that flip-flop. Chosen: explicit list.
  • Advisory downgrades timeouts only — a genuine FAILED (exception) on an advisory script stays RED, preserving the correctness signal.
  • Cross-ref: PyAutoHeart#72 (release tail epic), draft/feature/profiling/profiling_agent_jax_compile_time_scope.md (durable speedups).

Original Prompt

Click to expand starting prompt

mode=release should tier slow real-search scripts as advisory, not RED-block on the perf tail

Type: feature
Target: PyAutoHeart
Repos:

  • PyAutoHeart
  • PyAutoBuild
    Difficulty: medium
    Autonomy: supervised
    Priority: high
    Status: formalised

The 2026-07-13 release-validation burn-down (PyAutoHeart#72) fixed every real
correctness bug in the tail (A–H + the jax_grad config gap). But the
workspace-validation.yml mode=release integrate stage still cannot reach a clean
verdict
, because it runs the FULL script set — including a population of genuinely
slow real-search scripts (PYAUTO_TEST_MODE=0/1 real Nautilus fits +
finite-difference JAX gradients) that flake around the per-script timeout cap. The
failure set shifts run to run:

Raising BUILD_SCRIPT_TIMEOUT (300→1800) and adding no_run SLOW-skips one script at
a time does not converge — each run surfaces a different handful of borderline
scripts, and de-gating them individually is whack-a-mole that also erodes coverage.

The structural problem: mode=release gates a RELEASE on the full-set run, but a
big fraction of that set is genuinely-slow real work whose pass/fail near a per-script
cap is nondeterministic (CI runner load). Correctness and perf-flakiness are conflated
into one RED. (This is the "mode=release full-tail RED policy" question first raised on
#72 comment-4966797130.)

Proposal — tier the run so integrate gates on CORRECTNESS, not the perf tail:

  • Introduce a slow/advisory tier for known-slow real-search scripts (a
    slow: true marker in config/build/no_run.yaml or a companion slow.yaml, or a
    duration threshold): these still RUN and are reported, but a timeout on an
    advisory-tier script is advisory (yellow), not an integrate-RED.
  • Alternatively/additionally, run the slow tier as a separate off-critical-path job
    with a large budget (e.g. 3600s+), so a slow-tier flake never blocks the release
    verdict while still surfacing regressions.
  • A timeout on a non-advisory script is still RED (a script that should be fast
    timing out IS a signal). Only the declared-slow population is de-gated.
  • Keep it loud: advisory failures surface in the report + Heart's YELLOW reasons
    (same spirit as the existing SLOW/NEEDS_FIX banners), so they're a tracked backlog,
    not silently hidden — the durable speedups are the Profiling Agent's job
    (draft/feature/profiling/profiling_agent_jax_compile_time_scope.md).

Outcome: after the real bugs are fixed, mode=release reaches GREEN/YELLOW on
correctness and the release-decision gate stops being held hostage by real-search
perf-flakes. Design in PyAutoHeart (workspace-validation.yml + heart/validate.py
tiering) with the runner support in PyAutoBuild (run_python.py/build_util.py).
Cross-ref project_release_2026_07_13_blocked_3bugs and PyAutoHeart#72.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions