ci(email): gate releases on the full eval suite + all email tests#1990
Conversation
Builds on #1983 (which already runs the triage/drafting/action-item/briefing eval suite as a release gate) to make the gates actually block and to cover the remaining surfaces: - Enforce the drafting + perf gates (tests/fixtures/email/*_gate_thresholds.json enforce:true). A drafting-approval or perf regression now blocks the release (and the weekly eval / PR refresh), instead of report-only. NOTE: the perf bars are not yet independently hardware-confirmed — widen them in the manifest if the stx pool proves noisy. - Add the email test suite as a hard release gate: release_agent_email.yml now needs test_email_agent_unit.yml, which is expanded to also run the REST-sidecar contract + OpenAPI-conformance (spec) tests. A spec/contract or unit regression blocks a release, not just a PR. - Fold the judge-scored drafting result into the scorecard: gen_scorecard.py gains --drafting-report; email_scorecard_refresh.yml runs the drafting eval and passes it, so the committed/shipped SCORECARD.md carries draft_approval_rate as a reported metric (weight 0; aggregate stays 100 x within_one). - Move the eval workflow from nightly to weekly (Mondays 07:17 UTC) — the run now spends Claude judge tokens, and the release re-runs the suite as a hard gate. A missing ANTHROPIC_API_KEY already blocks a release: the action-item and briefing evals in the release gate hard-require the judge and fail loudly when it is absent (no fuzzy-only fallback).
|
Verdict: Approve with suggestions. This PR turns the email-agent release into a real gate: the drafting + perf eval bars now block (not just report), the release also runs the full unit/contract/spec test suite, and the judge-scored drafting result is folded into the scorecard as a reported metric. The gating plumbing is sound — I verified the One thing to fix before merge: the eval workflow that is the release gate still describes itself as "report mode / enforce:false" in several comments and echo lines — flipping the manifests made those descriptions contradict the new behavior. Not a code bug, but a maintainer reading that file will be told the gate is non-blocking when it now blocks. Worth correcting in the same PR so the gate doesn't ship self-contradicting docs. 🔍 Technical details🟡 Important — stale "report mode" text now contradicts the enforce flip
CLAUDE.md's "a functional change must update every doc that describes it" applies here — the switch changed, its descriptions didn't. Recommend a sweep of that file so its self-description matches the manifests it reads. 🟢 Minor — the new
|
…t install gaps PR #1990 flips the drafting + perf gate manifests to enforce:true (the whole point — the judged drafting eval and Strix-Halo perf bars are now hard release gates), but three CI problems left the branch red: - Two guard tests still asserted the committed manifests were report-mode (test_committed_thresholds_manifest_is_report_mode / _loads_in_report_mode), so they failed with `assert True is False`. Retarget them to the new intended state (enforce is True), rename to *_is_enforcing, and update the stale report-mode docstrings (drafting, briefing, perf class) to say hard gate. - test_email_agent_unit.yml newly runs hub/agents/python/email/tests/ (memory, inbox-profiling, behavioral-learning, preferences) but the step set a process-wide GAIA_MEMORY_DISABLED=1, forcing every agent's memory store to None and skipping memory-tool registration -> 45 failures. Those suites mock the embedder per-test and toggle the flag themselves; drop the global env var. - email_scorecard_refresh.yml runs `gaia eval benchmark`, which imports the standalone gaia_agent_email wheel (#1102), but only installed .[dev,eval,api] — so the benchmark died with ModuleNotFoundError on a clean venv. Install the local editable hub package alongside it.
|
🟡 The newly added comment in the release workflow makes a false claim about how a missing The comment (added in this PR) says:
But The comment is incorrect and may mislead maintainers into thinking the gate protects against an un-judged release when it doesn't. 🔍 Technical details
if not os.environ.get("ANTHROPIC_API_KEY"):
report = {"skipped": True, "should_fail": False, ...}
(out / "drafting_gate_report.json").write_text(...)
return 0 # ← always 0; the eval-gate step passesThe guard at line 127 ( Fix: either (a) correct the comment to say "absent → loud skip (gate not enforced)" — matching the actual behaviour and the scorecard-refresh comment — or (b) if the intent really is to require the key for a release, add a check: when Affected line: |
…nchmark test_email_agent_eval.yml runs `gaia eval benchmark`, which imports the standalone gaia_agent_email wheel (#1102), but only installed .[dev,eval,api] — so the scheduled Monday run would die with ModuleNotFoundError on a clean venv, same latent bug just fixed in email_scorecard_refresh.yml. Install the local editable hub package alongside the extras.
|
🟡 The release workflow's claim that a missing Additionally, 🔍 Technical detailsFalse enforcement claim — Conflicts directly with A skip-report causes Stale docstring — Should read |
|
Addressed the doc-accuracy review from the
Verified none of the three workflows running the judged eval trigger on fork PRs, so the key is always present in a legitimate run and the hard-fail can't spuriously break nightly/forks. Tests + lint green on top of current main (which now also carries #1989). |
…lent skip) (amd#2023) ## Why this matters Follow-up to amd#1990 (already merged), which flipped the drafting + perf gates to `enforce:true` but left two accuracy defects on main: 1. **A fail-loudly violation.** `eval_drafting_report.py` still *silently loud-skipped* when `ANTHROPIC_API_KEY` was absent — it wrote `{"skipped": true, "should_fail": false}` and returned 0, and `gen_scorecard.py` then quietly dropped the `draft_approval_rate` metric. So a keyless run shipped an **un-judged drafting gate** while the manifest claimed `enforce:true`. That contradicts CLAUDE.md's "No Silent Fallbacks — Fail Loudly" hard rule. Now a missing key is a hard failure (exit 1) with an actionable error naming the key; there is no skip report and no invented pass. 2. **Stale docs from the enforce flip.** `test_email_agent_eval.yml`, `release_agent_email.yml`, and the drafting-eval docstrings still described the perf/drafting gates as `enforce:false` / "report mode" / "loud-skip". Swept to match shipped behavior: perf + drafting + briefing enforce; only the FP/FN quality gate remains report-mode. No gating **logic** changed beyond the missing-key path — the manifests still own the enforce switch (data, not code). **Fork/nightly safety:** the three workflows that run the judged eval trigger only on `schedule` / `workflow_call` / tag-push / base-branch push — never `pull_request` from a fork — so `secrets.ANTHROPIC_API_KEY` is always present in a legitimate run and the hard-fail can't spuriously break CI. ## Test plan - [x] `pytest tests/unit/eval tests/unit/email -q` → 605 passed - [x] New: missing key → `main()` returns 1 + stderr names `ANTHROPIC_API_KEY`, writes no skip report (`test_drafting_eval_missing_key_hard_fails`) - [x] Changed: a `skipped`-marked report now raises instead of silently omitting the metric (`test_drafting_report_skip_marker_fails_loud`) - [x] `python util/lint.py --all` clean; all three workflow YAMLs parse - [x] `gen_scorecard.py` retains amd#1989's `_build_reproduction_command` alongside the fail-loudly change (clean 3-way merge)
To unblock the v0.4.0 release. The perf gate failed FIRST in every CI run and masked the three downstream judge gates, so drafting (enforce #1990) and briefing (enforce #1951) never actually completed a run — their bars have never been validated on hardware, exactly like the perf bars that just false-failed the release. Set both to report-mode (enforce:false) so a breach is logged + uploaded but doesn't block; re-enforce once each has a passing hardware baseline (tracking issue to follow). perf stays enforcing at the widened bars; action-item stays judge-required. A missing ANTHROPIC_API_KEY still fails briefing loudly.
…ated judge gates, shorten eval (amd#2038) ## Why this matters Unblocks the `agent-pkg-email-v0.4.0` release, which red-failed at the enforcing **perf gate**. Two problems, both "an enforcing bar was switched on before it was ever validated on the stx hardware": 1. **Perf gate false-failed.** The amd#1277 bars (TTFT 5s, 10 tok/s, pipeline 300s) had never run on hardware; the real numbers are TTFT 8.5s, 12.1 tok/s, ~1894s/50-email. Recalibrated above observed with margin (TTFT 15s, 8 tok/s, 2700s, 16 GB), still `enforce:true`. 2. **The perf failure masked three judge gates** (drafting, briefing, action-item) that run *after* it — so **drafting** (`enforce` amd#1990) and **briefing** (`enforce` amd#1951) never completed a CI run and their bars are likewise unvalidated. Set both to **report-mode** for now; re-enforce once each has a passing hardware baseline (amd#2039). Also **shortens the release eval**: the gate now triages 20 emails instead of 50 (~32 min → ~13 min); TTFT/throughput are per-token and the judge evals use their own seed corpora, so coverage is unchanged. Weekly nightly still runs the full 50. Net: the release gate now enforces only what's proven (perf at calibrated bars, the committed-scorecard acceptance bars, the full email test suite). Drafting/briefing/action-item still **run and upload** their reports every release — they just don't block until validated (amd#2039). ## Test plan - [x] `pytest tests/unit/eval/ tests/unit/email/test_{drafting,briefing}_corpus_integrity.py` — pass; manifest tests updated to report-mode. - [x] Manifests valid JSON: perf `enforce:true` (calibrated), drafting/briefing `enforce:false`. - [ ] **The proof is the next stx release run** — verify the full `email-eval` gate goes green end-to-end. ## To ship v0.4.0 Merge, then re-point the `agent-pkg-email-v0.4.0` tag to the merge commit (or cut `v0.4.1`) — the manifests are read at the tagged commit. Follow-up to re-enforce the judge gates: amd#2039. --------- Co-authored-by: Ovtcharov <kovtchar@amd.com>
Why this matters
An email-agent release could previously ship even if the judge-based evals regressed, the perf bars were missed, or the unit/spec tests were red. #1983 wired the eval suite (triage / drafting / action-item / briefing) into the release as a gate, but the drafting + perf gates were report-only, and the release did not run the unit or spec/contract tests at all.
After this change, a release of
@amd-gaia/agent-emailis blocked unless, at the release commit:ANTHROPIC_API_KEYblocks — the action-item + briefing evals hard-require the judge and fail loudly (no fuzzy-only fallback), so a release can't ship an un-judged suite.SCORECARD.md(draft_approval_rate, reported metric).What changed
tests/fixtures/email/{drafting,perf}_gate_thresholds.json→enforce: true).release_agent_email.yml:publishnow also needs a newemail-testsgate (uses: test_email_agent_unit.yml).test_email_agent_unit.yml: installs the email hub package and also runshub/agents/python/email/tests/(REST contract) +tests/test_email_openapi_conformance.py(spec) — fully mocked, no Lemonade.gen_scorecard.py: new--drafting-report; foldsdraft_approval_rateas a reported metric (weight 0) so the aggregate stays100 × within_one_bucket_accuracy. Loud-skip report → metric omitted; malformed non-skip → hard error.email_scorecard_refresh.yml: runs the drafting eval and passes--drafting-report, so the committed/shipped scorecard carries the drafting metric.test_email_agent_eval.yml: nightly → weekly (Mondays 07:17 UTC) — the run now spends Claude judge tokens, and the release re-runs the suite as a hard gate.Design note (deviation from the original plan preview)
The approved preview envisioned a bespoke
eval-gatejob that regenerates + ships a runner-fresh scorecard. #1983 had already landed a reusableemail-evalgate, so this adapts to that structure instead: the eval runs (and now enforces) viaemail-eval; the committed scorecard is kept honest — and gains the drafting metric — byemail_scorecard_refresh.yml. Net effect matches the requirement.Test plan
pytest tests/unit/eval/test_release_scorecard.py— 76 pass (3 new: drafting metric folded/reported/aggregate-unchanged; loud-skip omits; malformed fails loud). Windows:PYTHONUTF8=1.black --check/isort --checkclean on the changed Python.enforce: true.email-evalgate +email_scorecard_refreshrun on the self-hosted stx pool with Lemonade +ANTHROPIC_API_KEY. Verify via aworkflow_dispatchdry-run on hardware before cutting a real tag — especially that the newly-enforced perf bars pass on the pool.Dependency
Overlaps PR #1989 (docs) on
gen_scorecard.py. Trivial to resolve; recommend landing #1989 first.