Skip to content

perf(ci): take advisory unit-selection evidence off the gate - #23776

Merged
nwparker merged 1 commit into
mainfrom
nwparker/ci-vitest-pool
Sep 29, 2026
Merged

nwparker merged 1 commit into
mainfrom
nwparker/ci-vitest-pool

Conversation

@nwparker

@nwparker nwparker commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​16 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​16
Prod 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​49 $\color{#cf222e}{\Huge{\mathbf{−}}}$​29 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​20

ELI5

After the tests finish, one extra job compares "which tests we would have picked" against "all the tests we actually ran" and files a report. It can't fail your PR — it's purely a note for us. But the green checkmark was waiting ~36 seconds for it anyway. Now it runs alongside instead of in front.

What Changed

selection_evidence moves out of unit-tests.yml into its own unit-selection-evidence.yml, called from pr.yml as a sibling with needs: [test].

Why it was on the critical path. The job is continue-on-error: true on both the job and its comparison step, so it can never fail a PR. But a caller's needs: test waits for every job in the called workflow, and verify needs test — so verify sat behind an advisory job.

From a recent run, the tail of the chain was: last shard finishes → selection_evidence 36s → verify 3s. Only the last of those gates anything.

It still runs on every PR and still uploads its unit-selection-review-attempt-N artifact. The only change is that the required check no longer waits for it.

Why

This is the second instance of the same structural issue — a caller's needs gating an entire reusable workflow, making independent work serial. #23743 fixed the front of the chain by hoisting plan out; this fixes the back by hoisting selection_evidence out. Same mechanism, same fix.

unit_selection_evidence is deliberately absent from verify's needs, which is the whole point, and a contract test pins that along with its advisory status so it cannot quietly become blocking again.

Things I checked and rejected while looking for this

Recording these so nobody re-treads them:

  • Shard rebalancing. The last run's shards spread 306–375s, so perfect balance would take ~33s off the slowest — but config/scripts/ci-shard-timings.json was refreshed 5 hours ago, so the spread is inherent to file-level granularity, not stale weights. No lever here.
  • pool: threads for vitest. There is a real ~20% prize (a full shard: 70.55s forks → 56.49s threads locally), and the blocker is solvable — execArgv: ['--no-experimental-webstorage', '--expose-gc'] is incompatible with worker_threads, which is why a naive --pool=threads runs zero tests; V8 flags are per-process so moving them to NODE_OPTIONS makes gc() work inside threads. But threads share process.env and process.cwd(), and 9 files / 33 tests fail deterministically as a result (e.g. ssh-known-hosts-source.test.ts stubs HOME and gets the real one back). One of them, ai-vault-service-ready-retention.test.ts, asserts a WeakRef was collected and cannot work in a shared heap at all. Worth pursuing as its own deliberate change — it needs ~7 test files reworked plus a standing invariant that tests not mutate process globals — but it is not a config flip and does not belong here.
  • Expanding unit-test selection. selected mode is already built, but gated on event.pull_request.draft === true, so it only applies to drafts. Widening it is the single biggest lever in the whole workflow and also the riskiest; it is clearly being staged deliberately (shadow → drafts → …) and is not mine to accelerate.

Linked Issue

N/A — internal CI performance work, no pre-existing issue.

Visual Proof

N/A — CI configuration only, no user-facing surface.

Testing

  • 111 contract tests pass across pr-workflow-parallelism, pr-code-change-scope, ci-background-step-barriers and pr-e2e-gate-contract.

  • Added a new test, keeps advisory unit-selection evidence off the gate, asserting the sibling job exists with needs: ['test'], that verify.needs does not contain it, that unit-tests.yml no longer defines it, and that it is still continue-on-error: true. That last assertion matters: if someone later makes it blocking, the reason it was moved evaporates and the test should fail.

  • Confirmed nothing else referenced the job: no contract test did, and node-next-compat.yml never called it (it is PR-specific by nature — comparing selection against full results only makes sense on a PR diff), so the daily lane is unaffected.

  • Artifacts still resolve: the shard reports are run-scoped, so a sibling called workflow in the same run downloads them exactly as before.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • The one behavioral difference: the sibling is gated on needs.test.result == 'success', so it no longer runs when the shards fail. Previously if: !cancelled() meant it ran even then and compared partial results. That seemed like the right trade — a selection comparison against a failed run is not evidence of anything — but it is a deliberate narrowing and worth a reviewer's agreement.
  • Concurrency: no change in job count; the same ubuntu-slim job runs, just not in front of the gate.
  • Backwards compatibility / Security / SSH / Mobile: CI wiring only, no runtime code or artifact format change.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

selection_evidence is continue-on-error on both the job and its comparison step,
so it can never fail a PR -- it downloads the shard reports, compares selection
against the full results and uploads a review artifact. But a caller's
`needs: test` waits for every job in the called workflow, so living inside
unit-tests.yml it held verify for ~36s after the last shard finished.

It moves to its own reusable workflow called as a sibling, so it still runs on
every PR and still uploads its artifact, but verify no longer waits for it. It is
deliberately absent from verify's needs, and a contract test pins both that and
its advisory status so it cannot drift back onto the critical path.

Measured on a recent run: the shards finished, then selection_evidence ran 36s,
then verify 3s. Only the last of those gates anything.
@greptile-apps

greptile-apps Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[High risk] Restructures CI workflow job dependencies and parallelism.

The PR appears safe to merge, though failed test workflows will no longer produce potentially useful selection evidence.

Findings

  1. P2 Failed tests lose selection evidence ▶

Summary

Moves advisory unit-selection comparison from the unit-test reusable workflow into a sibling PR job so verify need not wait for its report.

  • Preserves the comparison and artifact-upload steps.
  • Adds a contract test for the sibling dependency and its exclusion from the gate.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Plan[Unit plan] --> Test[Test workflow]
  Test --> Verify[Required verify gate]
  Test --> Evidence[Advisory selection evidence]
Loading

Reviews (1) · Last reviewed commit: "perf(ci): take advisory unit-selection e..."

Comment thread .github/workflows/pr.yml
# after the last shard. Deliberately absent from verify's needs for the same reason.
unit_selection_evidence:
needs: [test]
if: ${{ !cancelled() && needs.test.result == 'success' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Failed tests lose selection evidence

If a unit shard fails, or the separate relay integration job fails after the shards finish, needs.test.result is failure and this condition skips the evidence workflow. The shards upload their evidence even on failure, and the comparison can identify failing tests outside the proposed selection. Skipping it loses that advisory report when it could be most useful, though it does not affect the required gate.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@pullfrog pullfrog 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.

ℹ️ No critical issues — one scope change worth confirming before merge.

Reviewed changes

  • Advisory job hoisted out of unit-tests.yml — selection_evidence now lives in a new reusable unit-selection-evidence.yml, called from pr.yml as a sibling job (needs: [test]) so verify no longer waits on it.
  • Sibling gated on needs.test.result == 'success' — narrows it to successful runs; previously if: !cancelled() compared partial results after a failure.
  • Contract test pinned — new keeps advisory unit-selection evidence off the gate asserts the sibling exists, is absent from verify.needs, is gone from unit-tests.yml, and stays continue-on-error.

The mechanism is sound: the called workflow's only job is continue-on-error, so the caller job's result is success, shard artifacts remain run-scoped (a same-run sibling resolves them as before), and the new workflow file doesn't trip any workflow census test.

ℹ️ The daily x86 lane loses its selection evidence

The PR notes state that node-next-compat.yml "never called it" and that "the daily lane is unaffected." That is not accurate: node-next-compat.yml calls unit-tests.yml, which defined selection_evidence, so the daily lane did run it and uploaded unit-selection-review-attempt-N — with complete shadow-mode references for both Node 24 and 26. Moving the job out of unit-tests.yml silently stops that collection. It doesn't block anything, but it contradicts the stated rationale; either add a sibling call to node-next-compat.yml or correct the notes.

Technical details
# Daily lane selection evidence

## Affected sites
- `.github/workflows/node-next-compat.yml:40-45` — `test:` calls `./.github/workflows/unit-tests.yml`, which previously produced `selection_evidence`; no equivalent sibling exists here after this PR.
- `.github/workflows/unit-tests.yml` — the removed job was the only producer of `unit-selection-review-attempt-N` in that caller's run.
- `config/scripts/ci-unit-selection-review.mjs:66-67` — in `shadow` mode (the daily lane's default via `unit-plan.yml`) a full reference run yields `completeFullRun: true`, so those artifacts are exactly the "representative complete references" the script asks for.

## Required outcome
- Either restore daily evidence (e.g. sibling `needs: [test]` call in `node-next-compat.yml`) or drop the "daily lane is unaffected" claim so the behavior change is recorded truthfully.

## Open questions for the human
- Is daily `unit-selection-review` evidence intentionally retired, or is the daily lane meant to remain an evidence source for promoting selection out of shadow mode?

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@coderabbitai

coderabbitai Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 02e9285d-fe91-45d1-bc32-b8e474f7cc6e

📥 Commits

Reviewing files that changed from the base of the PR and between a5ce825 and 35c60b9.

📒 Files selected for processing (4)
  • .github/workflows/pr.yml
  • .github/workflows/unit-selection-evidence.yml
  • .github/workflows/unit-tests.yml
  • config/scripts/pr-workflow-parallelism.test.mjs
💤 Files with no reviewable changes (1)
  • .github/workflows/unit-tests.yml

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The PR workflow now runs unit-selection evidence through a separate reusable workflow after the test job succeeds. The evidence job does not gate verify. The reusable workflow compares unit-shard artifacts for the current run attempt and attempts to upload a JSON artifact, retaining it for 30 days. A workflow-configuration test checks this wiring.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 35c60

The advisory evidence report remains available after successful tests without delaying the required verify check. No issue identified here blocks merging.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 35c60

The required test gate remains intact and the evidence workflow gains no apparent privileges. However, evidence is now skipped when tests fail, removing the report that can identify failures the proposed test selection would have missed.

Retained concerns

  • Low · reliability · observed: Requiring test success before starting advisory evidence suppresses reports on failed test runs. Those runs can contain the failures-outside-selection signal used to evaluate whether proposed test selection is safe, weakening that diagnostic without changing the current required test verdict.
Security review details

Security Blast Radius

  • inferred — The changed execution path affects advisory CI reporting, not the required test verdict or a newly privileged deployment path.

Trust Boundaries and Controls

  • observed — The evidence workflow consumes run-attempt-scoped shard artifacts under read-only workflow permission and does not persist checkout credentials.

Resilience and Maintainability Implications

  • inferred — Failed required tests still reach verify through its test dependency, but skipping their advisory report reduces visibility into whether a future selection policy would have missed those failures.

Hardening Proposals

  • proposed — Consider allowing the sibling evidence job to attempt a report after failed tests while keeping it advisory and outside verify dependencies; continue to mark partial inputs as incomplete.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: advisory unit-selection evidence is removed from the required CI gate.
Description check ✅ Passed The description follows the required template. It explains the change and reason, documents testing, notes the deliberate behavior change, and completes the checklist. The N/A linked issue is reasonab…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 1…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR

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.

@nwparker
nwparker merged commit 2ea3fb1 into main Sep 29, 2026
37 checks passed
brennanb2025 added a commit that referenced this pull request Sep 29, 2026
…3700, #22943, #23720, #23793, #23776, #23796) into #23059

#23573 (keep a turn's bar on the prompt that opened it) is ported onto the
turn-record membership instead of kept as a second owner lookup:
- The running turn's bar comes from nativeChatTurnMembership, which now
  returns barTurnKey beside liveTurnKey. Where the host states each row's
  turn they are the same key: the running root turn's anchor, and a
  message handed over mid-turn is already scoped to that turn. Where the
  host states no scope, rows still group by position, so barTurnKey is the
  user entry the running record names (through the same anchors) while
  liveTurnKey keeps the positional live group, as #23573 kept row liveness
  on the newest prompt.
- activeTurnOpenedBy, selectStructuredAgentTurnBars' openedBy and
  selectNativeChatActiveTurnKey are removed; the desktop list, the slot
  builder and both turn-status hooks read barTurnKey.
- The queued-send clock (queuedUntil: a turn counts from the previous
  turn's end when its send waited behind it) is kept, read in the same
  one-pass walk over root turn records that keys timing by anchor.
- Its tests are adapted to pass a journal: each runs on a host that
  states no scope (the original expectations), and the desktop and mobile
  bar tests also run on a scoped host.

#23621 (a message Claude folds into its running turn no longer splits the
turn) is host-only and merged as is: the translator contract carries both
openTurnInLiveProviderCycle and #23059's command-turn members.

Both sides grew the Claude translator past its line budget, so its result
branch moves unchanged into claude-result-journaling.ts, beside
claude-message-journaling.ts (the pre-commit lint refuses the merge
without it).

#23801's Codex child path (no error-driven end) and #23059's Codex /compact
guard (a command turn ends only on turn/completed) both stand.
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.

1 participant