Skip to content

feat(cli): stream broker startup progress with --verbose#1218

Merged
willwashburn merged 2 commits into
mainfrom
feat/verbose-broker-startup-logs
Jun 30, 2026
Merged

feat(cli): stream broker startup progress with --verbose#1218
willwashburn merged 2 commits into
mainfrom
feat/verbose-broker-startup-logs

Conversation

@willwashburn

@willwashburn willwashburn commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

  • agent-relay up --verbose now logs CLI-side step markers as it starts the broker: API port resolution, orphan-process cleanup, broker process spawn, handshake retry attempts, status checks, fleet sidecar startup, node-delivery wait, and per-agent spawns.
  • Wires the broker child process's own stderr (its existing [agent-relay][startup +Nms] ... phase logs, gated by AGENT_RELAY_STARTUP_DEBUG, already on by default) to live console output when --verbose is set — previously those lines were only buffered for crash reports and invisible on a successful-but-slow or transient-503 startup.
  • Adds an onStep callback to HarnessDriverClient.spawn() / RuntimeSpawnOptions so consumers can observe binary resolution, process spawn, API bind, and handshake retry progress.

This came out of debugging a relay local up failure that surfaced only as Failed to start broker: Service Unavailable with no indication of which startup phase had stalled.

Test plan

  • npx vitest run packages/cli/src/cli/commands/core.test.ts packages/cli/src/cli/lib/broker-lifecycle.test.ts packages/cli/src/cli/lib/client-factory.test.ts packages/harness-driver/src — all passing, including a new up --verbose forwards the flag to createRelay and logs startup step markers test.
  • npx tsc --noEmit -p packages/cli/tsconfig.json — no new errors (pre-existing unrelated @relayfile/client errors persist on main).
  • npm --prefix packages/cli run lint — no new warnings/errors.

Review in cubic

…ose`

`up` previously surfaced only a terse error (e.g. "Service Unavailable")
when broker startup raced or stalled, with no visibility into which
step failed. `--verbose` now logs CLI-side step markers (port
resolution, broker spawn, handshake retries, fleet sidecar, node
delivery wait, agent spawns) and streams the broker's own
startup-phase logs and stderr live instead of only buffering them
for crash reports.
@willwashburn willwashburn requested a review from khaliqgant as a code owner June 30, 2026 20:21
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds an optional verbose mode to agent-relay up, threading a verbose flag and onStep/onStderr callbacks from CLI core dependencies through client-factory and harness-driver spawn code, plus broker lifecycle logging, with updated tests and a changelog entry.

Changes

Verbose Startup Logging

Layer / File(s) Summary
CLI core dependency contract and relay wiring
packages/cli/src/cli/commands/core.ts
CoreDependencies.createRelay and createDefaultRelay gain an optional verbose parameter; when enabled, onStep/onStderr callbacks are wired into relay runtime initialization.
Client-factory callback plumbing
packages/cli/src/cli/lib/client-factory.ts
CreateRuntimeClientOptions adds onStderr/onStep callbacks, destructured and forwarded into HarnessDriverClient.spawn.
Harness-driver spawn step logging
packages/harness-driver/src/client.ts, packages/harness-driver/src/spawn-config.ts
RuntimeSpawnOptions gains onStep; spawn() emits step messages for binary resolution, broker spawn, API URL detection, handshake retries/completion, and event stream connection.
Broker lifecycle verbose logging
packages/cli/src/cli/lib/broker-lifecycle.ts
A vlog() helper and verbose parameter are threaded through startBrokerWithPortFallback, waitForBrokerReadiness, detached startup, orphan cleanup, fleet sidecar loading, and agent spawning.
Tests and changelog
packages/cli/src/cli/commands/core.test.ts, CHANGELOG.md
Updates createRelay assertions for the new argument, adds an up --verbose test, and adds a changelog entry.

Sequence Diagram(s)

sequenceDiagram
  participant CoreCommand
  participant createRuntimeClient
  participant HarnessDriverClient
  participant BrokerProcess
  CoreCommand->>createRuntimeClient: createRuntimeClient(onStep, onStderr)
  createRuntimeClient->>HarnessDriverClient: spawn(onStep, onStderr)
  HarnessDriverClient->>BrokerProcess: spawn broker binary
  HarnessDriverClient-->>CoreCommand: onStep(broker binary resolved)
  HarnessDriverClient-->>CoreCommand: onStep(API listening)
  HarnessDriverClient-->>CoreCommand: onStep(handshake retry)
  HarnessDriverClient-->>CoreCommand: onStep(handshake complete)
  HarnessDriverClient-->>CoreCommand: onStep(event stream connected)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A rabbit hops with logs in tow,
Each startup step now starts to glow.
Port found, broker woke, handshake done—
No more silence, just steps in the sun.
🐇📡 Verbose hops, watch the broker run!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: verbose broker startup progress streaming for the CLI.
Description check ✅ Passed The description follows the template with Summary and Test Plan sections filled in, and Screenshots are reasonably omitted as not applicable.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/verbose-broker-startup-logs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces a --verbose flag to the agent-relay up command, enabling step-by-step startup progress logging and streaming of the broker's logs and stderr. It propagates the verbose option through the CLI commands, broker lifecycle management, and the harness driver client. The review feedback suggests improving the dependency injection pattern in createDefaultRelay by allowing an optional logError parameter instead of hardcoding console.error directly, which would facilitate custom logging and testing.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +145 to +150
async function createDefaultRelay(
cwd: string,
apiPort = 0,
brokerName?: string,
verbose = false
): Promise<CoreRelay> {

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.

medium

To maintain the dependency injection pattern of the codebase and allow custom logging (e.g., in tests or programmatic wrappers), consider adding an optional logError parameter to createDefaultRelay that defaults to console.error.

Suggested change
async function createDefaultRelay(
cwd: string,
apiPort = 0,
brokerName?: string,
verbose = false
): Promise<CoreRelay> {
async function createDefaultRelay(
cwd: string,
apiPort = 0,
brokerName?: string,
verbose = false,
logError: (message: string) => void = console.error
): Promise<CoreRelay> {

Comment on lines +165 to +170
...(verbose
? {
onStep: (message: string) => console.error(`[agent-relay][verbose] ${message}`),
onStderr: (line: string) => console.error(`[broker] ${line}`),
}
: {}),

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.

medium

Use the injected logError function instead of hardcoding console.error directly. This ensures that verbose logs can be captured or redirected by custom loggers.

Suggested change
...(verbose
? {
onStep: (message: string) => console.error(`[agent-relay][verbose] ${message}`),
onStderr: (line: string) => console.error(`[broker] ${line}`),
}
: {}),
...(verbose
? {
onStep: (message: string) => logError(`[agent-relay][verbose] ${message}`),
onStderr: (line: string) => logError(`[broker] ${line}`),
}
: {}),

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/harness-driver/src/client.ts (1)

293-393: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unguarded onStep calls can abort broker startup if a consumer-supplied callback throws.

HarnessDriverClient.spawn() is a public entry point and onStep is now part of its external contract, but every onStep?.(...) call here is invoked directly — an exception thrown from a caller's callback would propagate and fail the whole startup. This file already establishes the opposite pattern for callbacks in runBeforeSpawn, which explicitly catches handler exceptions so they "do not abort the chain."

🛡️ Proposed fix to guard callback invocation
   static async spawn(options?: RuntimeSpawnOptions): Promise<HarnessDriverClient> {
-    const onStep = options?.onStep;
+    const onStep = options?.onStep
+      ? (message: string) => {
+          try {
+            options.onStep!(message);
+          } catch {
+            // step callback failures must not abort broker startup
+          }
+        }
+      : undefined;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/harness-driver/src/client.ts` around lines 293 - 393, The startup
flow in HarnessDriverClient.spawn is vulnerable because each onStep callback is
invoked directly, so a consumer-thrown exception can abort broker startup.
Update the onStep?.(...) calls in spawn to use the same guarded callback pattern
already used for runBeforeSpawn, catching and suppressing callback errors so
they do not break the spawn sequence. Keep the fix localized around the existing
onStep usage and preserve the current startup logic for binary resolution,
broker process spawning, handshake polling, and event stream setup.
🧹 Nitpick comments (2)
CHANGELOG.md (1)

13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Trim implementation detail from the changelog bullet.

The new entry enumerates internal startup phases (port resolution, broker process spawn, handshake retries, fleet sidecar, node-delivery wait, agent spawns) rather than staying impact-first and concise. As per coding guidelines, "Changelog entries should be concise and impact-first, with one short bullet per user-visible change... Omit issue/PR links, internal notes, and implementation details."

📝 Suggested concise rewrite
-- `agent-relay up --verbose` now prints step-by-step startup progress (port resolution, broker process spawn, handshake retries, fleet sidecar, node-delivery wait, agent spawns) and streams the broker's own startup-phase logs and stderr live, instead of only surfacing a terse error if startup fails.
+- `agent-relay up --verbose` now prints step-by-step startup progress and streams the broker's startup logs live, instead of only surfacing a terse error if startup fails.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` at line 13, Trim the CHANGELOG bullet for the agent-relay up
--verbose update so it stays impact-first and concise; remove the internal
startup-phase enumeration and any implementation detail, and rewrite the entry
to focus only on the user-visible behavior change in a single short bullet.

Source: Coding guidelines

packages/cli/src/cli/commands/core.ts (1)

165-170: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Verbose hooks bypass the deps logging abstraction and split output across streams.

onStep/onStderr call console.error directly instead of deps.log/deps.error, while vlog() in broker-lifecycle.ts writes verbose messages via deps.log (stdout). This means verbose progress lines end up split across stdout (broker-lifecycle's vlog) and stderr (this file's onStep/onStderr) for what is conceptually the same --verbose feature, and these calls can't be intercepted via deps mocking in tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/cli/commands/core.ts` around lines 165 - 170, The verbose
callbacks in core.ts are bypassing the shared deps logging API and sending
output directly to stderr, which splits the verbose experience across streams
and makes testing harder. Update the object passed from the verbose branch so
onStep and onStderr route through the injected deps.log/deps.error methods
instead of console.error, and keep their formatting aligned with
broker-lifecycle.ts’s vlog() usage so all verbose output is handled consistently
through deps.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/harness-driver/src/client.ts`:
- Around line 302-308: The verbose step logging is exposing the workspace key in
cleartext. Update the logging in client.ts around buildBrokerSpawnConfig and the
onStep calls so any args string is redacted before being emitted, especially the
--workspace-key value; also fix the later log that prints session?.workspace_key
directly by masking or omitting the secret. Keep the existing step messages, but
route them through a small redaction helper so broker spawn diagnostics remain
useful without leaking credentials.

---

Outside diff comments:
In `@packages/harness-driver/src/client.ts`:
- Around line 293-393: The startup flow in HarnessDriverClient.spawn is
vulnerable because each onStep callback is invoked directly, so a
consumer-thrown exception can abort broker startup. Update the onStep?.(...)
calls in spawn to use the same guarded callback pattern already used for
runBeforeSpawn, catching and suppressing callback errors so they do not break
the spawn sequence. Keep the fix localized around the existing onStep usage and
preserve the current startup logic for binary resolution, broker process
spawning, handshake polling, and event stream setup.

---

Nitpick comments:
In `@CHANGELOG.md`:
- Line 13: Trim the CHANGELOG bullet for the agent-relay up --verbose update so
it stays impact-first and concise; remove the internal startup-phase enumeration
and any implementation detail, and rewrite the entry to focus only on the
user-visible behavior change in a single short bullet.

In `@packages/cli/src/cli/commands/core.ts`:
- Around line 165-170: The verbose callbacks in core.ts are bypassing the shared
deps logging API and sending output directly to stderr, which splits the verbose
experience across streams and makes testing harder. Update the object passed
from the verbose branch so onStep and onStderr route through the injected
deps.log/deps.error methods instead of console.error, and keep their formatting
aligned with broker-lifecycle.ts’s vlog() usage so all verbose output is handled
consistently through deps.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a9624234-57e3-487d-a920-5ad571922b95

📥 Commits

Reviewing files that changed from the base of the PR and between 49f0628 and a991f50.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • packages/cli/src/cli/commands/core.test.ts
  • packages/cli/src/cli/commands/core.ts
  • packages/cli/src/cli/lib/broker-lifecycle.ts
  • packages/cli/src/cli/lib/client-factory.ts
  • packages/harness-driver/src/client.ts
  • packages/harness-driver/src/spawn-config.ts

Comment on lines +302 to +308
onStep?.(`Resolved broker binary: ${binaryPath}`);
const apiKey = `br_${randomBytes(16).toString('hex')}`;
const { cwd, timeoutMs, args, env } = buildBrokerSpawnConfig(options, apiKey);
const stderrLines: string[] = [];
const stdoutLines: string[] = [];

onStep?.(`Spawning broker process: ${binaryPath} ${args.join(' ')}`);

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Verbose step log leaks the workspace key in cleartext.

buildBrokerSpawnConfig embeds the workspace key directly into args (...(workspaceKey ? ['--workspace-key', workspaceKey] : [])), and this line logs args.join(' ') verbatim via onStep. Since --verbose is added specifically so users can capture and share this output when relay local up fails, this credential is very likely to end up pasted into bug reports/chat logs. Same issue at line 389, which logs session?.workspace_key directly.

🔒 Proposed fix to redact the workspace key from verbose output
     onStep?.(`Resolved broker binary: ${binaryPath}`);
     const apiKey = `br_${randomBytes(16).toString('hex')}`;
     const { cwd, timeoutMs, args, env } = buildBrokerSpawnConfig(options, apiKey);
     const stderrLines: string[] = [];
     const stdoutLines: string[] = [];

-    onStep?.(`Spawning broker process: ${binaryPath} ${args.join(' ')}`);
+    const redactedArgs = args.map((arg, i) => (args[i - 1] === '--workspace-key' ? '[REDACTED]' : arg));
+    onStep?.(`Spawning broker process: ${binaryPath} ${redactedArgs.join(' ')}`);

And further down:

-    onStep?.(`Broker handshake complete (workspace: ${session?.workspace_key ?? 'unknown'})`);
+    onStep?.('Broker handshake complete.');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onStep?.(`Resolved broker binary: ${binaryPath}`);
const apiKey = `br_${randomBytes(16).toString('hex')}`;
const { cwd, timeoutMs, args, env } = buildBrokerSpawnConfig(options, apiKey);
const stderrLines: string[] = [];
const stdoutLines: string[] = [];
onStep?.(`Spawning broker process: ${binaryPath} ${args.join(' ')}`);
onStep?.(`Resolved broker binary: ${binaryPath}`);
const apiKey = `br_${randomBytes(16).toString('hex')}`;
const { cwd, timeoutMs, args, env } = buildBrokerSpawnConfig(options, apiKey);
const stderrLines: string[] = [];
const stdoutLines: string[] = [];
const redactedArgs = args.map((arg, i) => (args[i - 1] === '--workspace-key' ? '[REDACTED]' : arg));
onStep?.(`Spawning broker process: ${binaryPath} ${redactedArgs.join(' ')}`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/harness-driver/src/client.ts` around lines 302 - 308, The verbose
step logging is exposing the workspace key in cleartext. Update the logging in
client.ts around buildBrokerSpawnConfig and the onStep calls so any args string
is redacted before being emitted, especially the --workspace-key value; also fix
the later log that prints session?.workspace_key directly by masking or omitting
the secret. Keep the existing step messages, but route them through a small
redaction helper so broker spawn diagnostics remain useful without leaking
credentials.

@willwashburn willwashburn merged commit a0672b0 into main Jun 30, 2026
47 checks passed
@willwashburn willwashburn deleted the feat/verbose-broker-startup-logs branch June 30, 2026 20:34
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.

1 participant