Skip to content

feat(cli): readable go-code startup output - #1414

Merged
dennisonbertram merged 2 commits into
mainfrom
fix/1413-readable-startup-output
Sep 8, 2026
Merged

feat(cli): readable go-code startup output#1414
dennisonbertram merged 2 commits into
mainfrom
fix/1413-readable-startup-output

Conversation

@dennisonbertram

Copy link
Copy Markdown
Owner

Closes #1413

The problem

Starting go-code printed 13 lines in which the one that mattered was indistinguishable from the twelve that did not:

[go-code] no server at http://127.0.0.1:8080, starting harnessd on port 8080
[go-code] waiting for server to become healthy (pid 9900)...
2026/09/08 06:45:44 loaded model catalog with 15 providers
2026/09/08 06:45:44 provider catalog: 15 files, added 4 providers and 59 models
2026/09/08 06:45:44 note: rates not expressible in USD per million tokens, ...
2026/09/08 06:45:44 pricing resolver wired from model catalog (fallback)
2026/09/08 06:45:44 loaded 4 skill(s)
2026/09/08 06:45:44 embedded cron scheduler started (db: .harness/cron.db)
2026/09/08 06:45:44 delayed callbacks enabled
2026/09/08 06:45:44 callback run persistence enabled: .harness/runs.db
2026/09/08 06:45:44 hot-reload watcher started (interval: 5s, dirs: ...)
2026/09/08 06:45:44 fatal: refusing to start: :8080 listens beyond this machine ...
[go-code] ERROR: harnessd (pid 9900) exited before becoming healthy on port 8080. ...

Three voices — the wrapper, the daemon's boot log, a fatal error — sharing one visual channel. The actionable fatal: line sits in the middle with no more emphasis than delayed callbacks enabled. On success the same boot dump is pure noise in front of someone who asked for a TUI, and because the daemon inherited stdout, a log line emitted after handoff could render into the interface.

What changed

Severity carries color. Cyan prefix for info, yellow for warnings, red for errors. The WARN:/ERROR: words stay in the text, so severity survives a monochrome terminal, a pipe, a captured log, and colorblind readers. Only the 8 standard ANSI colors are used, so terminals apply their own theme rather than a hardcoded shade that can vanish on a light background.

The daemon writes to a log file (${TMPDIR:-/tmp}/harnessd.<pid>.log, umask 077) instead of inheriting the terminal. A healthy start reports readiness and the log path; no daemon line can reach the TUI after handoff.

A failed start prints the log back. Capturing output without surfacing it would trade noise for silence, so die shows the last 20 lines under harnessd said: with fatal:/panic:/refusing to start in bold red and the rest dimmed.

Result

Success, 13 lines down to 4:

[go-code] starting harnessd on port 19502
[go-code] waiting for server to become healthy (pid 39157)...
[go-code] server ready at http://127.0.0.1:19502     <- "server ready" green
[go-code] log: /var/folders/.../T/harnessd.39144.log

Failure, cause no longer buried:

[go-code] ERROR: harnessd (pid 38439) exited before becoming healthy on port 19501. ...

  harnessd said:
    loaded model catalog with 15 providers                    <- dimmed
    hot-reload watcher started (interval: 5s)                 <- dimmed
    fatal: refusing to start: :8080 listens beyond this ...   <- bold red

  full log: /var/folders/.../T/harnessd.38424.log

Verification

TestGoCodeScriptSurfacesHarnessdLogOnStartupFailure was genuinely red first. Redirection landed before surfacing, and the test caught exactly the trap that creates — the wrapper still claimed "See the harnessd log above" while the log was now in a file:

--- FAIL: TestGoCodeScriptSurfacesHarnessdLogOnStartupFailure
    startup failure did not surface harnessd's own reason for dying;
    the daemon log was captured but never shown, which hides the cause

TestGoCodeScriptEmitsNoAnsiWhenNotATty (no tty, NO_COLOR=1, TERM=dumb) is a regression guard, not red-first — it cannot fail before the feature exists, and it is labelled that way in the source rather than presented as TDD.

All five wrapper tests green under -race. Bench smoke 13/13 PASS, proving the sibling scripts and the daemon's own logging are untouched.

Rendered output was inspected in a real pty for all four states — success, failure, piped, NO_COLOR. That inspection is what caught a bug the test suite could not: color detection was originally lazy inside the style helper, but style runs in command substitution, where $( ) redirects stdout to a pipe. [[ -t 1 ]] was therefore always false and stdout was never colored, while stderr colored correctly because $( ) leaves fd 2 alone. Detection now happens once at startup. The gotcha is recorded in the engineering log.

go-code runs | cat yields zero escape sequences.

Out of scope

Colorizing harnessd's own Go log output. That stream feeds systemd journals, CI logs, macapp, and the benchmark scripts, and should not be restyled to serve one wrapper's presentation.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WJGxhoFhA8JjkwZFcLGdS5

dennisonbertram and others added 2 commits September 8, 2026 06:52
Starting go-code printed 13 lines in which the one that mattered was
indistinguishable from the twelve that did not. The wrapper's messages,
harnessd's boot log, and a fatal error all shared one visual channel, so
the actionable line sat buried in the middle with no more emphasis than
"delayed callbacks enabled".

Severity now carries color: cyan for the wrapper's prefix, yellow for
warnings, red for errors. The WARN:/ERROR: words stay in the text, so
severity survives a monochrome terminal, a pipe, a captured log, and
colorblind readers. Only the 8 standard ANSI colors are used, so
terminals apply their own theme.

A wrapper-started harnessd now writes to a log file instead of inheriting
the terminal. A healthy start reports readiness and the log path in a few
lines rather than a boot dump, and no daemon line can scribble into the
TUI after handoff. Because capturing output would otherwise trade noise
for silence, a failed start prints the log back with the fatal line
emphasized and the rest dimmed.

Color is detected once at startup rather than inside the style helper:
the helper runs in command substitution, where stdout is a pipe, so a
lazy `-t 1` check would leave stdout permanently uncolored.

Closes #1413

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJGxhoFhA8JjkwZFcLGdS5
…avior

Records the -t 1 inside command substitution gotcha in the engineering
log: the check has to run once at startup, because $( ) redirects stdout
to a pipe, so a lazy check leaves stdout permanently uncolored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJGxhoFhA8JjkwZFcLGdS5
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-08T11:00:34.079777Z e00b135 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@dennisonbertram
dennisonbertram merged commit 65edbaa into main Sep 8, 2026
2 checks passed
@dennisonbertram
dennisonbertram deleted the fix/1413-readable-startup-output branch September 8, 2026 10:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e00b1359e1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -1,5 +1,42 @@
# Engineering Log

## 2026-09-08 — Issue #1413 readable go-code startup output

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Record the issue success criteria in the intent log

This adds the implementation record for issue #1413, but the reviewed tree's docs/logs/long-term-thinking-log.md contains no #1413 entry. Consequently future agents have no required command intent, user intent, or success definition against which to evaluate follow-up work; add the issue's criteria there as required.

AGENTS.md reference: AGENTS.md:L19-L23

Useful? React with 👍 / 👎.

@@ -1,5 +1,42 @@
# Engineering Log

## 2026-09-08 — Issue #1413 readable go-code startup output

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Update the logs index for the new entry

Adding issue #1413 as the newest engineering-log entry materially changes that file, but docs/logs/INDEX.md is unchanged and still describes engineering-log.md as current for issue #1264. Update the folder index so repository navigation reflects the newly documented work.

AGENTS.md reference: AGENTS.md:L55-L56

Useful? React with 👍 / 👎.

Comment thread scripts/go-code.sh
Comment on lines +256 to +257
HARNESSD_LOG="${tmpdir%/}/harnessd.${$}.log"
( umask 077; : > "$HARNESSD_LOG" )

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 Badge Allocate the daemon log with a unique filename

When a daemon started with go-code --server outlives its short-lived wrapper and that wrapper PID is later reused for another invocation on a different port, this $$-derived path collides with the live daemon's log. The subsequent : > "$HARNESSD_LOG" truncates the inode still held by the first daemon, after which both daemons can write overlapping diagnostics; predictable creation in shared /tmp also permits pre-created-file or symlink hazards on platforms without protected-temp semantics. Allocate the path atomically with a genuinely unique file instead.

Useful? React with 👍 / 👎.

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.

feat(cli): make go-code startup output readable — severity color, captured daemon log, emphasized failure cause

1 participant