From 405643f8a7fdbbdf1abddeb69a05eb18728abc56 Mon Sep 17 00:00:00 2001 From: Koshkoshinsk Date: Mon, 6 Jul 2026 16:20:59 +0300 Subject: [PATCH] =?UTF-8?q?fix(setup):=20SSF-002=20follow-up=20=E2=80=94?= =?UTF-8?q?=20step=20children:=20silence=20logger=20info,=20force=20clack?= =?UTF-8?q?=20colors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QA on the VM showed two leaks in the effect:step tee: the host logger's INFO lines (it always emits ANSI and defaults to info) landed on the wizard screen, and the child's clack card drew colorless because picocolors disables ANSI on a piped stdout, clashing with the wizard theme. hostExecStream now spawns steps with LOG_LEVEL=warn (operator-set level wins; warnings/errors still pass) and FORCE_COLOR=1 when the operator's terminal is a real TTY. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HB8rQxJN7itcFhNYVDntpm --- setup/lib/skill-driver.test.ts | 14 ++++++++++++++ setup/lib/skill-driver.ts | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/setup/lib/skill-driver.test.ts b/setup/lib/skill-driver.test.ts index 224536e22..9652f3918 100644 --- a/setup/lib/skill-driver.test.ts +++ b/setup/lib/skill-driver.test.ts @@ -148,6 +148,20 @@ describe('thin skill driver', () => { expect(out.fields.PLATFORM_ID).toBe('telegram:42'); }); + it('hostExecStream children run with LOG_LEVEL=warn — host logger info noise stays off the wizard', async () => { + const root = mkdtempSync(join(tmpdir(), 'driver-loglevel-')); + const prev = process.env.LOG_LEVEL; + delete process.env.LOG_LEVEL; // simulate an operator who didn't set one + try { + const out = await hostExecStream(root)( + 'echo "=== NANOCLAW SETUP: ENV ==="; echo "STATUS: success"; echo "LVL: $LOG_LEVEL"; echo "=== END ==="', + ); + expect(out.fields.LVL).toBe('warn'); + } finally { + if (prev !== undefined) process.env.LOG_LEVEL = prev; + } + }); + function reuseScratch(): { root: string; skill: string } { const root = mkdtempSync(join(tmpdir(), 'reuse-')); const skill = mkdtempSync(join(tmpdir(), 'reuse-skill-')); diff --git a/setup/lib/skill-driver.ts b/setup/lib/skill-driver.ts index b881f1849..6b8baeb17 100644 --- a/setup/lib/skill-driver.ts +++ b/setup/lib/skill-driver.ts @@ -282,7 +282,20 @@ export function hostExecStream(projectRoot: string): (cmd: string) => Promise { const child = spawn('bash', ['-c', cmd], { cwd: projectRoot, - env: { ...process.env, PATH: `${join(projectRoot, 'bin')}:${process.env.PATH ?? ''}` }, + env: { + ...process.env, + PATH: `${join(projectRoot, 'bin')}:${process.env.PATH ?? ''}`, + // A step renders curated operator UI (a code card, a QR) — the host + // logger's info noise doesn't belong on the wizard screen, and it + // always emits ANSI so it can't be filtered by stream. Warnings and + // errors still pass. An operator-set LOG_LEVEL wins (debugging). + LOG_LEVEL: process.env.LOG_LEVEL ?? 'warn', + // The child's stdout is a pipe, so picocolors would strip its clack + // rendering to bare box chars that clash with the wizard theme. + // When the OPERATOR's terminal is a real TTY, the teed lines land + // there — force color so the child's card matches the parent. + ...(process.stdout.isTTY ? { FORCE_COLOR: '1' } : {}), + }, stdio: ['inherit', 'pipe', 'pipe'], }); const blocks: Array<{ fields: Record }> = [];