Skip to content

feat: read evals from usage artifact instead of dedicated evals artifact#45797

Merged
pelikhan merged 6 commits into
mainfrom
copilot/update-audit-logs-evals
Jul 15, 2026
Merged

feat: read evals from usage artifact instead of dedicated evals artifact#45797
pelikhan merged 6 commits into
mainfrom
copilot/update-audit-logs-evals

Conversation

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

evals.jsonl is now included in the usage artifact (conclusion job copies it from the evals job). The client-side --evals flag was still requesting a dedicated evals artifact download and the cache bypass was keyed on the artifact name, which broke silently after the mapping change.

Changes

  • ArtifactSetEvals → resolves to usage (logs_artifact_set.go): --artifacts evals now downloads the usage artifact. runHasEvals already probes usage/evals.jsonl; this aligns the download side with detection.

  • applyEvalsArtifact skips when usage already present (logs_artifact_set.go): The logs default is --artifacts usage; previously --evals appended a redundant evals entry. Now it short-circuits when usage is already in the list.

  • Thread evalsOnly through the download pipeline (logs_run_processor.go, logs_orchestrator.go, logs_orchestrator_stdin.go): The cache bypass in tryLoadCachedRunResult was checking slices.Contains(artifactFilter, "evals") — always false after the mapping change. Replaced with an explicit params.evalsOnly field propagated from the callers.

  • Flag descriptions and comments updated in audit.go and logs_command.go to reflect the usage artifact as the evals source.

  • Test: Added ResolveArtifactFilter(["evals"]) == ["usage"] assertion; updated all downloadRunArtifactsConcurrent call sites with the new evalsOnly parameter.

evals.jsonl is now included in the usage artifact (copied there by the
conclusion job). Update the client-side code accordingly:

- ArtifactSetEvals now resolves to the usage artifact name so that
  --artifacts evals downloads usage (which contains evals.jsonl)
- applyEvalsArtifact no longer appends evals when usage is already in
  the artifacts list, since usage already provides evals
- Thread evalsOnly bool through concurrentRunDownloadParams so the cache
  bypass in tryLoadCachedRunResult uses the explicit flag rather than
  inspecting the artifact filter for a name that no longer matches
- Update --evals flag descriptions in audit and logs commands to reflect
  that evals are read from the usage artifact
- Add TestResolveArtifactFilter case asserting evals→usage resolution

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug
Risk 🟢 Low
Priority Score 65/100
Action fast_track

Score breakdown: Impact 28/50 · Urgency 20/30 · Quality 17/20

Rationale: Fixes evals artifact resolution regression — --artifacts evals was silently broken after mapping change. Targeted fix (9 files, net -46 lines). Clear description with test. CI not yet run.

Generated by 🔧 PR Triage Agent · 36.7 AIC · ⌖ 7.39 AIC · ⊞ 5.6K ·

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot download evals if not found in usage artifact

Copilot AI and others added 3 commits July 15, 2026 18:57
…tifact

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

@copilot download evals if not found in usage artifact

Done. Added tryDownloadEvalsArtifactFallback in processSingleRunDownload: after downloading the usage artifact, if evals.jsonl is not found, it retries with the dedicated evals artifact. This handles older runs where evals.jsonl was uploaded as a standalone artifact instead of being included in usage.

The fallback is triggered by a new evalsArtifactRequested field (true when --evals is set OR when --artifacts evals was explicitly listed), so both cases are covered. The fallback uses perRunParams to ensure per-run repo context (owner/repo resolved from the run URL) is used, not the empty defaults.

@pelikhan
pelikhan marked this pull request as ready for review July 15, 2026 19:51
Copilot AI review requested due to automatic review settings July 15, 2026 19:51
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #45797 does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (99 additions, threshold is 100).

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates eval retrieval to use the usage artifact and adds legacy fallback handling.

Changes:

  • Maps the evals artifact set to usage.
  • Propagates eval-request context through download orchestration.
  • Updates CLI descriptions and tests.
Show a summary per file
File Description
pkg/cli/logs_run_processor.go Adds eval request tracking and legacy fallback.
pkg/cli/logs_parallel_test.go Updates download helper calls.
pkg/cli/logs_orchestrator.go Passes eval context to downloads.
pkg/cli/logs_orchestrator_test.go Updates orchestrator test calls.
pkg/cli/logs_orchestrator_stdin.go Propagates stdin eval options.
pkg/cli/logs_command.go Updates the eval flag description.
pkg/cli/logs_artifact_set.go Maps evals to usage and deduplicates selection.
pkg/cli/logs_artifact_set_test.go Tests evals-to-usage resolution.
pkg/cli/audit.go Updates audit messaging and comments.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 9/9 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment thread pkg/cli/logs_run_processor.go Outdated
ensureEvalsResultsFromBranch(ctx, summary.Run, runOutputDir, params.dlOwner, params.dlRepo, params.dlHost, params.verbose)
if evalsRequested && !hasEvals {
logsOrchestratorLog.Printf("Cache bypass for run %d: evals artifact requested but not present locally", run.DatabaseID)
if params.evalsOnly && !hasEvals {
Comment on lines +94 to +95
// evals: evals results are now included in the usage artifact.
ArtifactSetEvals: {constants.UsageArtifactName},
@github-actions github-actions Bot mentioned this pull request Jul 15, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: feat: read evals from usage artifact instead of dedicated evals artifact

The overall approach is correct — remapping ArtifactSetEvals to resolve to the usage artifact and introducing a fallback for older runs is well-designed. One blocking bug was found.

Blocking issue: In tryLoadCachedRunResult (logs_run_processor.go:271), the cache bypass condition was changed from the old evalsRequested (which fired for both --evals and --artifacts evals) to params.evalsOnly (only fires for --evals). The PR already introduces evalsArtifactRequested that covers both cases and uses it correctly in processSingleRunDownload. The same field should be used in tryLoadCachedRunResult for consistency and correctness.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 40.3 AIC · ⌖ 6.87 AIC · ⊞ 5K

Comment thread pkg/cli/logs_run_processor.go Outdated
ensureEvalsResultsFromBranch(ctx, summary.Run, runOutputDir, params.dlOwner, params.dlRepo, params.dlHost, params.verbose)
if evalsRequested && !hasEvals {
logsOrchestratorLog.Printf("Cache bypass for run %d: evals artifact requested but not present locally", run.DatabaseID)
if params.evalsOnly && !hasEvals {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: cache bypass uses evalsOnly instead of evalsArtifactRequested

The old code used evalsRequested := slices.Contains(params.artifactFilter, constants.EvalsArtifactName) which was true for both --evals and --artifacts evals. The new code replaces this with params.evalsOnly, which is only true when --evals is passed.

As a result, when a user passes --artifacts evals (without --evals), the cache bypass no longer fires, and a stale cache without evals.jsonl will be returned instead of triggering a fresh download.

The PR already introduces evalsArtifactRequested (which covers both cases) and uses it correctly in processSingleRunDownload line 218. The same field should be used here:

// before
if params.evalsOnly && !hasEvals {

// after
if params.evalsArtifactRequested && !hasEvals {

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 90/100 — Excellent

Analyzed 14 test(s): 14 design, 0 implementation, 0 violation(s).

📊 Metrics (14 tests)
Metric Value
Analyzed 14 (Go: 14, JS: 0)
✅ Design 14 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 14 (100%)
Duplicate clusters 0
Inflation YES (orchestrator 14:1, justified by concurrency)
🚨 Violations 0
Test File Classification Coverage
TestResolveArtifactFilter (evals case) logs_artifact_set_test.go:161-164 design_test NEW: Validates evals→usage artifact resolution
TestDownloadRunArtifactsConcurrent_EmptyRuns logs_orchestrator_test.go:20 design_test Edge: Empty input handling
TestDownloadRunArtifactsConcurrent_ResultOrdering logs_orchestrator_test.go:28 design_test Concurrent result completeness
TestDownloadRunArtifactsConcurrent_AllProcessed logs_orchestrator_test.go:59 design_test Batch processing guarantee
TestDownloadRunArtifactsConcurrent_ContextCancellation logs_orchestrator_test.go:91 design_test Error: context cancellation
TestDownloadRunArtifactsConcurrent_PartialCancellation logs_orchestrator_test.go:117 design_test Edge: timeout during execution
TestDownloadRunArtifactsConcurrent_NoResourceLeaks logs_orchestrator_test.go:154 design_test Goroutine leak detection
TestDownloadRunArtifactsConcurrent_ConcurrencyLimit logs_orchestrator_test.go:184 design_test Config: concurrency enforcement
TestDownloadRunArtifactsConcurrent_LogsPath logs_orchestrator_test.go:264 design_test Output path validation
TestDownloadRunArtifactsConcurrent_ErrorHandling logs_orchestrator_test.go:287 design_test Error propagation
TestDownloadRunArtifactsConcurrent_MixedConclusions logs_orchestrator_test.go:314 design_test All run conclusion types
TestDownloadRunArtifactsConcurrent_VerboseMode logs_orchestrator_test.go:345 design_test Mode invariants
TestDownloadRunArtifactsConcurrent_ResultStructure logs_orchestrator_test.go:379 design_test Data structure fields
TestDownloadRunArtifactsConcurrent_PanicRecovery logs_orchestrator_test.go:425 design_test Goroutine pool safety
i️ Test Inflation Analysis

logs_orchestrator_test.go: 14 lines added / 1 line in logs_orchestrator.go = 14:1 ratio (exceeds 2:1 threshold)

Justification: All 13 new integration tests cover concurrent download behavior with careful attention to:

  • Context cancellation and timeout edge cases
  • Goroutine resource management and leak prevention
  • Result ordering and completeness guarantees
  • Configuration enforcement (concurrency limits)
  • Error handling across multiple run conclusion types

This is justified complexity: concurrency bugs are silent and hard to reproduce, requiring comprehensive testing of race conditions.

Verdict

Passed. 0% implementation tests (threshold: 30%). No coding guideline violations detected.

Strengths:

  • All 14 tests validate user-visible behavioral contracts (design tests)
  • Comprehensive edge-case and error coverage (cancellation, timeouts, goroutine leaks, empty inputs)
  • No mock library violations; proper Go build tags on all files
  • Clear assertion messages with failure context
  • Feature directly tested: new test case validates "evals resolves to usage artifact" behavior from PR title

Notes:

  • High test count (13 new integration tests) is appropriate for concurrent download feature with complex error conditions
  • Single-line production change in logs_artifact_set.go is well-tested by the new test case
  • Test inflation justified by concurrency testing requirements

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 27.2 AIC · ⌖ 9.76 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 90/100 (Excellent). 0% implementation tests (threshold: 30%). All 14 tests are design tests with comprehensive edge-case coverage and no coding guideline violations.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /codebase-design and /tdd — requesting changes on one correctness bug and two test-coverage gaps.

📋 Key Themes & Highlights

Key Themes

  • Flag asymmetry (/codebase-design): tryLoadCachedRunResult bypasses the cache only when params.evalsOnly is true, but processSingleRunDownload triggers the fallback whenever params.evalsArtifactRequested is true. These two flags differ when --artifacts evals is passed without --evals, creating a silent correctness hole for cached runs.
  • Untested fallback (/tdd): tryDownloadEvalsArtifactFallback has no unit test. It is a correctness-critical path whose "fail silently" contract should be pinned.
  • Missing deduplication test (/tdd): evals and usage now both resolve to constants.UsageArtifactName; ["evals", "usage"] should be tested to ensure it does not trigger a double download.

Positive Highlights

  • ✅ Clean redirection of ArtifactSetEvals → UsageArtifactName in the map — minimal blast radius
  • ✅ Backward-compatibility fallback for older runs is well-documented and error-swallowing intentionally
  • ✅ All existing downloadRunArtifactsConcurrent call sites updated consistently
  • ✅ The new applyEvalsArtifact guard for ArtifactSetUsage correctly prevents double-appending

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 51.2 AIC · ⌖ 4.74 AIC · ⊞ 6.7K
Comment /matt to run again

Comment thread pkg/cli/logs_run_processor.go Outdated
ensureEvalsResultsFromBranch(ctx, summary.Run, runOutputDir, params.dlOwner, params.dlRepo, params.dlHost, params.verbose)
if evalsRequested && !hasEvals {
logsOrchestratorLog.Printf("Cache bypass for run %d: evals artifact requested but not present locally", run.DatabaseID)
if params.evalsOnly && !hasEvals {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/codebase-design] Cache-bypass uses params.evalsOnly (line 271) but the fallback download uses params.evalsArtifactRequested (line 218) — the two conditions are asymmetric. A user who passes --artifacts evals without --evals will trigger the fallback on a fresh download but not get a cache bypass, so a stale cached run that lacks evals will be served without attempting the dedicated-artifact fallback.

💡 Suggested fix

Change the cache-bypass guard to match the fallback condition:

// was: if params.evalsOnly && !hasEvals {
if params.evalsArtifactRequested && !hasEvals {
    logsOrchestratorLog.Printf("Cache bypass for run %d: evals requested but not present locally", run.DatabaseID)
    return nil, false
}

Keeping these two branches in sync ensures a cached run without evals is never served when --artifacts evals was passed.

@copilot please address this.

// evals: BinEval evaluation results uploaded by the evals job.
ArtifactSetEvals: {constants.EvalsArtifactName},
// evals: evals results are now included in the usage artifact.
ArtifactSetEvals: {constants.UsageArtifactName},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/tdd] The evals and usage sets now resolve to the same artifact (constants.UsageArtifactName). There is no test asserting that ResolveArtifactFilter(["usage", "evals"]) deduplicates to ["usage"] (a single download) rather than downloading the usage artifact twice. The deduplication likely happens further downstream, but it should be explicitly covered.

💡 Suggested test

Add to TestResolveArtifactFilter:

{
    name:     "evals + usage deduplicates to single usage artifact download",
    sets:     []string{"evals", "usage"},
    expected: []string{"usage"},
},

This guards against double-downloading if callers pass both sets.

@copilot please address this.

// that predate the conclusion-job copy), fall back to the dedicated evals
// artifact so those runs are not silently skipped. This applies both when
// --evals is set and when --artifacts evals was explicitly listed.
if params.evalsArtifactRequested && !runHasEvals(runOutputDir, params.verbose) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/tdd] tryDownloadEvalsArtifactFallback is called but never unit-tested. The fallback is a correctness-critical code path for older runs — if it silently fails, those runs are served without evals. A test that stubs downloadRunArtifacts to return an error (or succeed) would pin this behaviour.

💡 Suggested approach

Add a test in logs_run_processor_test.go (or a new file) that:

  1. Creates a run output dir without evals.jsonl
  2. Calls tryDownloadEvalsArtifactFallback with a mock/stub
  3. Asserts the log output and that errors are swallowed (not propagated)

This ensures the "fail silently, continue" contract is explicit and protected.

@copilot please address this.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two correctness issues warrant fixes before merge

1. Cache bypass uses a narrower predicate than the download fallback (logs_run_processor.go:271): tryLoadCachedRunResult bypasses the cache only when evalsOnly is true, but processSingleRunDownload triggers the evals fallback on the broader evalsArtifactRequested. These predicates must match or a cached run with missing evals is returned from cache without triggering the fallback when a user passes --artifacts evals without --evals.

2. applyEvalsArtifact called without the len > 0 guard in logs_command.go (lines 175 and 330): the function's own doc comment mandates this guard; audit.go applies it correctly; neither logs_command.go call site does. Passing --artifacts "" (empty slice) with --evals silently reduces the download to usage-only, dropping agent/activation/firewall artifacts.

Further details

Both issues are in the changed diff. The remainder of the change (artifact remapping, evalsOnly propagation, fallback download logic) is sound.

🔎 Code quality review by PR Code Quality Reviewer · 96.3 AIC · ⌖ 4.62 AIC · ⊞ 5.6K
Comment /review to run again

Comments that could not be inline-anchored

pkg/cli/logs_run_processor.go:271

Cache bypass predicate is inconsistent with the download fallback condition: the cache bypass checks only params.evalsOnly but the fallback download (line 218) triggers on the broader params.evalsArtifactRequested. A user who passes --artifacts evals without --evals gets the fallback download after a fresh download, but if the run is already cached they get a stale cache hit with no evals — silently missing evals data.

<details>
<summary>💡 Suggested fix</summary>

Use the same pre…

pkg/cli/logs_command.go:175

Missing len(artifacts) &gt; 0 guard mirrors the hazard documented in applyEvalsArtifact's own doc comment: the function's comment explicitly warns callers that treat empty as "all" to guard with len(artifacts) &gt; 0. audit.go does this correctly. Both call sites in logs_command.go (lines 175 and 330) do not. If a user passes --artifacts &quot;&quot; (empty string, which StringSlice parses as an empty slice), applyEvalsArtifact will append &quot;evals&quot; and the effective filter becomes `["usage…

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

pr-sous-chef
@copilot please run the pr-finisher skill, address the blocking review feedback (cache bypass vs evals fallback parity, the missing len(artifacts) > 0 guards around applyEvalsArtifact, and the requested regression coverage), then rerun checks once complete.

Run: https://github.com/github/gh-aw/actions/runs/29447716606

Generated by 👨‍🍳 PR Sous Chef · 4.78 AIC · ⌖ 6.15 AIC · ⊞ 7.5K ·
Comment /souschef to run again

Copilot AI and others added 2 commits July 15, 2026 20:42
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

pr-sous-chef
@copilot please run the pr-finisher skill, address the blocking review feedback (cache bypass vs evals fallback parity, the missing len(artifacts) > 0 guards around `applyEvalsArtif...

Addressed in 7561ec5 and follow-up cleanup 9d43240.

  • Fixed cache bypass parity to honor explicit --artifacts evals (not just --evals).
  • Added audit-path legacy fallback to download dedicated evals when missing from usage, including cache-bypass parity there.
  • Added/updated regression coverage for evals-request detection and cache-bypass behavior.
  • Added the requested len(artifacts) > 0 guards around applyEvalsArtifact call sites.

Validation rerun:

  • Targeted tests for new/changed behavior pass.
  • parallel_validation (Code Review + CodeQL) rerun; no security findings.
  • Repository-wide make test-unit still fails in pre-existing bootstrap tests (TestRunBootstrapWithRuntime_*) unrelated to these changes, same as baseline.

Copilot AI requested a review from gh-aw-bot July 15, 2026 20:48
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill and ensure this PR is ready for maintainer review.

Generated by 👨‍🍳 PR Sous Chef · 5.48 AIC · ⌖ 7.22 AIC · ⊞ 7.5K ·
Comment /souschef to run again

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill and ensure this PR is ready for maintainer review.

...

Ran pr-finisher pass for PR #45797 on HEAD 9d43240.

  • Reviews: blocking feedback is already addressed by commits 7561ec5 and 9d43240; no additional code changes were needed in this pass.
  • Checks (local): make fmt ✅, make lint ✅, make recompile ✅. make test-unit/make test still fail only in pre-existing bootstrap runtime tests due blocked external API (TestRunBootstrapWithRuntime_*, DNS monitoring proxy 403), same baseline noted previously.
  • Checks (CI): latest PR-head pull_request runs are action_required/stale for agent commits; no new failed jobs were reported in those runs.
  • Mergeability: PR remains open; final green CI on current HEAD requires maintainer-triggered rerun.

This is ready for maintainer review from the agent side. Hand-off: please re-trigger CI from a maintainer context (close/reopen PR, workflow_dispatch, or maintainer push) before merge.

@pelikhan
pelikhan merged commit 4470da1 into main Jul 15, 2026
30 checks passed
@pelikhan
pelikhan deleted the copilot/update-audit-logs-evals branch July 15, 2026 21:26
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.10

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants