Skip to content

developer-experience: fix verdictColor() ANSI leak through --no-color (issue #102) - #103

Draft
ruvnet wants to merge 2 commits into
mainfrom
dream/2026-09-09-developer-experience
Draft

developer-experience: fix verdictColor() ANSI leak through --no-color (issue #102)#103
ruvnet wants to merge 2 commits into
mainfrom
dream/2026-09-09-developer-experience

Conversation

@ruvnet

@ruvnet ruvnet commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Nightly Dream Cycle, 2026-09-09. DEEP=developer-experience, SCAN=cli,tui. Full report: docs/dream-cycle/2026-09-09-developer-experience-report.md. Issue: #102.

Hypothesis

Given renderDashboard(ledgerMd, { noColor: true }), when verdictColor() is changed to resolve its return value through the same no-color-aware color map (c) that every other cell in the same row already uses — instead of closing over the raw, always-colored C constant — then noColor: true output should contain zero ANSI escape sequences for any verdict value (ACCEPT, REJECT, INCONCLUSIVE, or any other string, including a HALT: budget row), while noColor: false output stays byte-identical to today. Subject to: 0 regressions, change confined to verdictColor's color-source dispatch. Frozen before implementation. Confirmed.

Candidate

packages/cli/src/tui.tsverdictColor(v: string): stringverdictColor(v: string, c: typeof C): string, body changed from C.green/C.red/C.yellow/C.gray to c.green/c.red/c.yellow/c.gray; its one call site now passes c. +2 regression tests in packages/cli/src/index.test.ts. One conceptual change, 4-line function + 1-line call site, 16 lines of tests.

This exact defect was flagged as a known, unfixed follow-up in PR #21 (2026-08-19, merged) — "verdictColor() bypasses the no-color proxy, leaking a raw ANSI escape into --no-color output." Reproduced live tonight against main @ 3edd426 before writing the fix:

$ node -e "... renderDashboard(md, { noColor: true }) ..."
contains ANSI escape with noColor:true -> true
"est                 \x1b[32mACCEPT         "

Evaluation Receipt

Real evaluator: npm test (vitest run && npm run test:governance).

Baseline (3edd426) Candidate
vitest 616 618 (+2, 0 removed/modified)
governance 81 81
Total 697 699
Lint clean clean
Build clean (7 pkgs) clean (7 pkgs)

Live: post-fix noColor: true output contains zero ANSI escapes; colored (noColor: false) output unchanged. Self-hosted dream.config.json compile output byte-identical before/after (out of scope for this tui-only change, verified anyway).

Baseline

Parent commit 3edd426f6c9c4b1e80235f7447dc863e749345cc (main). 697/697 tests green (616 vitest + 81 governance), clean build, clean lint.

Darwin Lineage

Not run — DARWIN=not-applicable. Single deterministic color-dispatch parameterization, no evolvable population — same judgment as every prior single-function developer-experience/compiler-parity fix in this ledger (#11, #21, #29, #33, #46).

Evidence

OBSERVATION (grade A, live, pre-fix code read + reproduction) → OBSERVATION (grade A, PR #21's own body names this exact defect "not fixed") → MEASUREMENT (grade A, live noColor leak before/after) → MEASUREMENT (grade A, 697→699 tests, 0 regressions) → DECISION (parameter-thread c rather than add a second no-color check inside verdictColor, avoiding a second place the no-color decision could drift).

Reward-Hack Check

Independent critic (fresh subagent, no shared authoring context, given only git diff + full repo read access): CLEAR.

  • Confirmed the bug is real by reading the pre-fix code and reproducing the leak first-hand, not on faith.
  • Confirmed the fix fully closes the leak: grepped tui.ts post-fix for any remaining direct C.xxx reference inside renderDashboard or its helpers — zero found.
  • No reward-hacking signals: no weakened/skipped/.only tests, no gold-data edits, no threshold changes, no evaluator exploitation.
  • Verified by hand both new tests are bug-exercising: the noColor regression test fails against pre-fix code (reproduces the raw escape), passes post-fix; the colored-path test passes on both, correctly proving no regression rather than exercising the bug.
  • No regression to colored output — c === C when noColor is false, so behavior is identical to the old verdictColor(v).
  • Scope minimal: 4-line function + 1-line call site + 2 additive tests, nothing unrelated touched.

Security Review

No security-sensitive surface: no prompt-injection vector (no LLM calls in this path), no MCP/tool-authority change, no credential exposure, no filesystem/network scope change, no evaluator/gate/safety-constant touched. Pure ANSI-escape string-formatting logic.

Regression Analysis

0 pre-existing tests modified or removed. All 697 baseline tests pass unchanged; 2 new vitest tests added. npm run lint clean. npm run build clean (7 packages, no wasm/NAPI degradation to record tonight). ledger verify on the ledger with tonight's new row appended reports the same 40 pre-existing structural errors as before (already tracked by open issues #48/#58, PRs #49/#59) — tonight's row is not among them.

ADR

None — this is a bug fix to existing rendering behavior (a helper bypassing an already-established no-color chokepoint), not a new architectural decision. Same precedent as PR #21 itself.

Gist

No gh CLI or gist-creation MCP tool available this session (consistent with every prior night). GIST=LOCAL. Full report committed at docs/dream-cycle/2026-09-09-developer-experience-report.md.

Issue

#102

Witness

report_sha256 : 69a97031631f19d559ebc5c0d7601d36cb02efa7708fda9776eb1616e7372ebe
session_commit: 3edd426f6c9c4b1e80235f7447dc863e749345cc
witness       : 16c867ef6fc58d714ac698888e99b25e24e6f39a21e45bf4fcc7e03098b23ee0

Verify: sha256sum the committed report's content before its Witness section, then printf '%s%s' "<that hash>" "3edd426f6c9c4b1e80235f7447dc863e749345cc" | sha256sum must equal the witness above.

Merge Policy

Draft — human review required. This PR does not carry the automerge-safe label. The session never merges and never self-promotes.


🤖 Generated with Claude Code

https://claude.ai/code/session_01D5ee4aky1TGGQqfYpoouad


Generated by Claude Code

renderDashboard(ledgerMd, { noColor: true }) still emitted a raw ANSI
color escape in the verdict column — verdictColor() closed over the
always-colored module constant C instead of the noColor-aware color
map every other cell already goes through. Flagged unfixed in PR #21
(2026-08-19); reproduced live tonight against main before the fix.

Parameter-thread the resolved color map into verdictColor() instead
of adding a second no-color check. npm test 697->699 (+2), 0
regressions. Independent critic: CLEAR.

Dream Cycle 2026-09-09. Issue #102.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D5ee4aky1TGGQqfYpoouad
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.

2 participants