Skip to content

Suppress banner and default stdout/stderr to failed in LLM environments - #8771

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/llm-defaults
Jun 3, 2026
Merged

Suppress banner and default stdout/stderr to failed in LLM environments#8771
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/llm-defaults

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Summary

Extends LLMEnvironmentDetector so that AI agent CLIs (Claude Code, Gemini CLI, GitHub Copilot CLI, Aider, Cursor, etc.) also get quieter output defaults out of the box. This reduces token consumption on every test run an agent triggers, without changing anything for humans or CI.

When an LLM environment is detected (existing detection — see LLMEnvironmentDetector.DetectionRules), the following defaults flip:

OptionDefault (human)Default (LLM)
Banner (--no-banner)shownsuppressed (both platform + framework banner)
--show-stdoutAllFailed
--show-stderrAllFailed

ANSI mode is already disabled in LLM environments today; this PR extends the same opt-in mechanism to the two largest contributors of run-time noise.

Any explicit user-provided value still wins (e.g. --show-stdout all overrides the new default).

Why opt-in (instead of #7664's universal change)

Issue #7664 proposes changing --show-stdout / --show-stderr defaults to Failed for everyone, which is a breaking change for human and CI workflows. This PR takes the LLM-opt-in subset:

Implementation notes

  • LLMEnvironmentDetector refactored from static to instance with an injected IEnvironment. Detection rules remain static. This makes the new behaviors testable via env-var injection.
  • TerminalOutputDevice hoists isLLMEnvironment once next to the existing inCI lookup and threads it into GetShowOutputMode.
  • DisplayBannerIfEnabledAsync short-circuits when LLM is detected (mirrors existing --no-banner / DOTNET_NOLOGO behavior; suppresses both platform and framework banners).
  • Test infrastructure (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables) broadly filters the 23 LLM env vars when spawning child processes, so existing tests are not affected by the parent agent host.
  • Help-text wording for --show-stdout / --show-stderr was intentionally not changed (still says Default is 'All'.) to keep this PR tight and avoid 13-XLF churn. Defer to a wording-only follow-up.

Tests

New acceptance tests (all green locally on net8.0 / net10.0 / net462):

  • NoBannerTests.UsingLLMEnvironmentVar_TheBannerDoesNotAppear — sets CLAUDECODE=1, asserts banner is suppressed.
  • ShowOutputOptionTests.ShowStdout_DefaultInLLMEnvironment_ShowsStandardOutputOnlyForFailedTests
  • ShowOutputOptionTests.ShowStderr_DefaultInLLMEnvironment_ShowsErrorOutputOnlyForFailedTests
  • ShowOutputOptionTests.ShowStdout_All_InLLMEnvironment_StillShowsAllStandardOutput (explicit override still wins; stderr is symmetric and covered by impl)

Existing NoBannerTests, AnsiOptionTests, HelpInfoTests, HelpInfoAllExtensionsTests, and the full ShowOutputOptionTests suite still pass.

Related: #7664

…ents
Extends LLMEnvironmentDetector to also drive user-facing output defaults so AI
agents that run tests see less noise (and therefore consume fewer tokens) out
of the box:
- `--no-banner` is implicit (both platform and framework banners suppressed).
- `--show-stdout` defaults to `Failed` instead of `All`.
- `--show-stderr` defaults to `Failed` instead of `All`.
Any explicit user value still wins. The opt-in is purely based on the existing
LLM detection (CLAUDECODE, GEMINI_CLI, GITHUB_COPILOT_CLI_MODE, etc.), so
humans and CI are unaffected.
Refactors LLMEnvironmentDetector to instance-based (takes IEnvironment) so the
new behaviors are testable via env-var injection in acceptance tests.
Test infrastructure also broadly filters the LLM env-var list when spawning
child processes so existing tests are not affected by the parent agent host.
Related to #7664 (which proposes the same defaults change as a universal
breaking change).
CopilotAI review requested due to automatic review settings June 2, 2026 16:00

CopilotAI 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

This PR extends Microsoft.Testing.Platform’s “LLM environment” detection so that known AI-agent CLI shells automatically get quieter default console output (banner suppressed; --show-stdout/--show-stderr default to Failed), while preserving existing behavior for humans/CI and ensuring explicit CLI arguments still take precedence.

Changes:

  • Refactors LLMEnvironmentDetector to be instance-based with an injected IEnvironment, enabling deterministic testing.
  • Updates console defaults in LLM environments: suppress banner and default stdout/stderr output to Failed when the option is not explicitly provided.
  • Hardens acceptance test isolation by filtering LLM-related environment variables from spawned processes, and adds new acceptance tests validating the LLM-default behaviors.
Show a summary per file
FileDescription
test/Utilities/Microsoft.Testing.TestInfrastructure/WellKnownEnvironmentVariables.csAdds an LLM env-var allowlist and filters these vars from child processes so acceptance tests aren’t impacted by an ambient agent shell.
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ShowOutputOptionTests.csAdds acceptance coverage ensuring stdout/stderr defaults flip to Failed in LLM environments and explicit overrides still win.
test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NoBannerTests.csAdds acceptance coverage ensuring the banner is suppressed when an LLM env var is set.
src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.csDefaults --show-stdout/--show-stderr to Failed when LLM is detected and the option is absent.
src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.Utilities.csSkips displaying the banner when LLM environment is detected.
src/Platform/Microsoft.Testing.Platform/Helpers/LLMEnvironmentDetector.csRefactors detector to use injected IEnvironment and supports reuse for user-facing defaults.

Copilot's findings

  • Files reviewed: 6/6 changed files
  • Comments generated: 1

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 2, 2026 16:10

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Review Summary

#DimensionResult
1Algorithmic Correctness✅ LGTM — detection logic, banner suppression, and GetShowOutputMode fallback are all correct. Explicit CLI values correctly take precedence.
2Threading & Concurrency✅ LGTM — DetectionRules is static readonly; per-call instance creation avoids shared mutable state.
3Security & IPC✅ LGTM — env-var reads only; no injection surface.
4Public API Compatibility✅ LGTM — LLMEnvironmentDetector is internal; WellKnownEnvironmentVariables is in a test-utilities project with no PublicAPI analyzer.
5Performance⚠️ MODERATE — IsLLMEnvironment()GetLLMEnvironment() collects all matches into a string?[] and joins them, only to check is not null. The old code cached the result in a static property; the new instance design re-evaluates all 19+ rules on every call (at least twice per test startup). See inline comment on LLMEnvironmentDetector.cs:70.
6Cross-TFM Compatibility✅ LGTM — only standard IEnvironment and LINQ used.
7Resource Management✅ LGTM — no disposables introduced.
8Defensive Coding✅ LGTM — null guards on constructor parameters.
9Localization & Resources✅ LGTM — no new user-facing strings added to resources.
10Test Isolation✅ LGTM — ToSkipEnvironmentVariables spreads LLMEnvironmentVariables so acceptance tests are isolated from ambient agent shells; new tests set the env var explicitly.
11Assertion Quality✅ LGTM — AssertOutputContains / AssertOutputDoesNotContain are correct for the behavior under test.
12Flakiness Patterns✅ LGTM — no timing-dependent code.
13Test Completeness✅ LGTM — banner, stdout default, stderr default, and explicit-override tests are all covered. Stdout and stderr coverage is symmetric.
14Data-Driven Coverage✅ LGTM — AllForDynamicData covers all TFMs.
15Code Structure✅ LGTM — clear guard-clause structure.
16Naming & Conventions✅ LGTM.
17Documentation Accuracyi️ NIT — --show-stdout/--show-stderr help text still says "Default is 'All'." which is wrong in LLM environments. Intentionally deferred but should be tracked. See inline comment on TerminalOutputDevice.cs:241.
18Analyzer & Code Fix QualityN/A — no src/Analyzers/ changes.
19IPC Wire CompatibilityN/A — no serialized types changed.
20Build Infrastructure✅ LGTM — no new dependencies.
21Scope & PR Discipline✅ LGTM — OR_APP_NAME is stripped entirely from child-process env even though LLM detection only triggers for specific values (Aider, plandex, OpenHands); this is acceptable since the variable is tool-specific, and enumerating every possible value is not feasible.

19 dimensions clean · 1 moderate · 1 nit · 0 blocking

The PR is well-structured and the core logic is correct. The one actionable item before merge is the performance concern: collapsing IsLLMEnvironment() to a short-circuiting Any(...) call (or adding a per-instance cached field) avoids unnecessary allocations and re-evaluation. The help-text gap should be tracked in a follow-up issue with a TODO(#XXXX) comment per the repo's TODO policy.

Generated by Expert Code Review (on open) for issue #8771 · sonnet46 2.8M

CopilotAI 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.

Copilot's findings

  • Files reviewed: 6/6 changed files
  • Comments generated: 2

Comment threadsrc/Platform/Microsoft.Testing.Platform/Helpers/LLMEnvironmentDetector.cs Outdated
- Collapse IsLLMEnvironment() to short-circuiting Any() (no string allocation/join)
- Guard against null IEnvironment in LLMEnvironmentDetector constructor
- Expose LLMEnvironmentVariables as IReadOnlyList<string> instead of mutable array
- Add TODO(#8772) for --show-stdout/--show-stderr help-text wording follow-up
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) June 2, 2026 16:36
@Evangelink
Amaury Levé (Evangelink) merged commit 587a041 into mainJun 3, 2026
62 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/llm-defaults branch June 3, 2026 07:57
Sign up for freeto 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.

3 participants

@Evangelink@JanKrivanek