Route bv narration to standard error, keeping standard output for deliverables - #327
Conversation
Closes Tenacom#315. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #327 +/- ##
==========================================
+ Coverage 47.27% 49.85% +2.57%
==========================================
Files 126 130 +4
Lines 4044 4016 -28
Branches 714 714
==========================================
+ Hits 1912 2002 +90
+ Misses 2045 1925 -120
- Partials 87 89 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- New rule file .claude/rules/testing.md: code gets tested by default; [ExcludeFromCodeCoverage] is reserved for environment-owned code, with a mandatory justification, at the smallest scope, in source rather than in Codecov configuration. - Exclude VirtualTerminal (P/Invoke over console state), Program (process composition root), and ConsoleReporter.DetectColor/IsNoColorSet (process-global console and environment probes). - Move --verbosity parsing from Program to VerbosityParser in the CommandLine layer; tests pin the accepted aliases and the error message. - Cover ConsoleReporter's unknown-level throw and the double-dispose idempotence of activity scopes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Foreground now derives the escape sequence from ConsoleColor's bit layout (the same red/blue bit swap the BCL performs on Unix) instead of a 16-arm switch. The literal 16-color table moves to AnsiEscapesTests, where it acts as an independent oracle rather than a restatement of the implementation; out-of-range throws are pinned at the -1/16 boundaries. As a bonus, the merge-damage-prone switch is gone from shipping code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@claude please review this PR. |
|
Claude finished @rdeago's task in 6m 17s —— View job Review — routing
|
An explicit --color skipped auto-detection and with it the only call to VirtualTerminal.TryEnableOnStandardError, so on legacy conhost the reporter wrote raw escape sequences into a buffer that does not interpret them. Attempt the enable whenever the override forces color on; the override stays authoritative even when enabling fails. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The non-Windows branch returned true unconditionally, so auto-detected color emitted escape sequences even under TERM=dumb or an unset TERM, where Console.ForegroundColor (via terminfo) used to emit nothing. Checking TERM restores that parity; it lives here rather than in DetectColor because TERM is normally unset on Windows and a check there would misfire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Console.Error auto-flushes, so writing a leveled line in up to five pieces cost as many write syscalls and left the line non-atomic at the OS level against writers outside the reporter lock (the logo, Spectre on stdout). Build the whole line first and issue one WriteLine; output is byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Close the honest-coverage gaps flagged in review: out-of-order activity disposal must not pop the wrong scope (observable through the next sibling still nesting at depth 2), child output/error lines pass when the minimum verbosity is satisfied, ChildError ignores verbosity when no minimum is given (the ChildOutput twin already existed), and VerbosityParser.Parse(null) throwing ArgumentNullException is part of the contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address the review nits: document ArgumentNullException on VerbosityParser.Parse; make the AnsiEscapes remarks claim color-index parity with Console.ForegroundColor rather than byte-for-byte parity (the Unix BCL may emit terminfo-derived sequences); note that the VT mode change outlives the process and is deliberately not restored; and state why SetLastError = true stays on imports whose last error nobody reads yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
All findings addressed in five commits, one nit declined with rationale, and the optional refactor now has a home. Item by item: 1. 2. Non-Windows assumes any terminal understands VT — fixed in 3. A leveled line is up to five auto-flushed writes — fixed in Tests and coverage — closed in
Nits —
State after the five commits: 530 tests passing, zero warnings, ReSharper solution-wide inspection clean. @claude please take another look. |
|
Claude finished @rdeago's task in 2m 55s —— View job Re-review — follow-up commits
All three findings are genuinely fixed, and the tests added are behavior tests rather than coverage padding. CI is green on 1 — forced color now enables VT ( 2 — 3 — single composed write ( Tests. The out-of-order test is the real thing, not a coverage stub: the One follow-on from fix 2
Two honest options, and the rule prefers the second: extend the justification to name the environment read, or extract the decision as a pure helper ( Two notes, no action needed
On the declined nit
Everything I raised last round is resolved. The |
VirtualTerminal's class-level coverage exclusion dated from when the non-Windows branch was unconditionally true; the TERM check added since is pure decision logic, which the coverage policy says gets tested. The decision now lives in IsNonWindowsTerminalCapable, tested directly, and the exclusions are re-scoped to what is genuinely environment-owned: the method that reads TERM and manipulates the console, and the P/Invoke imports (whose generated stubs inherit the attribute from the partial declarations). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Follow-on from fix 2 — agreed on both the diagnosis and which option the coverage rule prefers: the justification described a branch that no longer existed, and the 6770b62 extracts the decision as
State: 535 tests passing, zero warnings, ReSharper inspection clean. @claude please take another look. |
|
Claude finished @rdeago's task in 3m 4s —— View job Re-review —
|
Closes #315.
What changed
bvnow follows the prevailing CLI convention (git, npm, cargo, kubectl, …): results go to standard output, narration goes to standard error, so results stay pipeable at any verbosity.ConsoleReporterwrites all leveled diagnostics (error:/warning:/info:/detail:/trace:) and activity header/outcome lines to standard error.ChildOutput(childdotnetstandard output — the payload of build commands) stays on standard output;ChildErrorwas already on standard error.bv --versionoutput and help remain standard-output deliverables.Console.IsErrorRedirected(plusNO_COLORand, on Windows, whether virtual-terminal processing can be enabled on the standard error handle).--color/--no-colorstill override everything.Design notes
AnsiEscapesclass) instead ofConsole.ForegroundColor. The BCL implementsForegroundColoragainst standard output — on Unix it emits the escape sequences to standard output — so with diagnostics on standard error it would corrupt a redirected deliverable stream (e.g.bv build > log).AnsiEscapes.Foreground(ConsoleColor)maps all 16ConsoleColors to the SGR codes the BCL uses on Unix, soerror:/warning:render exactly as before.VirtualTerminal.TryEnableOnStandardError()enablesENABLE_VIRTUAL_TERMINAL_PROCESSINGon the Windows standard error handle (ConPTY hosts like Windows Terminal already interpret VT; legacy conhost needs the explicit opt-in). On failure, auto-detected color turns off. Returnstrueoutright on non-Windows.ConsoleReporterfinally has unit tests (newBuildvana.Core.ConsoleOutput.Testsproject, 43 tests): per-level stream routing, verbosity gating, label-only coloring, activity lines, child passthrough.Breaking change
Scripts and CI steps that captured diagnostics from
bv's standard output must now capture standard error (e.g. via2>&1). Changelog entry added under Changes to existing features.Coverage follow-up
Codecov flagged the patch coverage of the first push. Rather than just chase the number, this PR also establishes the project's coverage policy, now codified in
.claude/rules/testing.md: testable code gets tested; code whose behavior is owned by the environment is excluded in source via[ExcludeFromCodeCoverage(Justification = ...)], never through Codecov configuration.Applied here:
AnsiEscapes.Foregroundnow computes the SGR code from theConsoleColorbit layout (the same red/blue bit swap the BCL uses on Unix) instead of a 16-arm switch; newAnsiEscapesTestshold the literal 16-color table as an independent oracle, plus boundary out-of-range throws.--verbosityparsing moved fromProgramto a newVerbosityParserin the CommandLine layer, with tests pinning the accepted aliases and the error message.ConsoleReportertests cover the unknown-MessageLevelthrow and the double-dispose idempotence of activity scopes.VirtualTerminal(P/Invoke over console state),Program(composition root),ConsoleReporter.DetectColor/IsNoColorSet(process-global console/environment probes).🤖 Generated with Claude Code