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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions