Skip to content

feat: Add CLIReporterPlugin for real-time progress reporting#252

Closed
thalesraymond wants to merge 1 commit intomainfrom
feature/add-cli-reporter-16322371157142790787
Closed

feat: Add CLIReporterPlugin for real-time progress reporting#252
thalesraymond wants to merge 1 commit intomainfrom
feature/add-cli-reporter-16322371157142790787

Conversation

@thalesraymond
Copy link
Copy Markdown
Owner

Implemented the add-cli-reporter proposal to introduce real-time CLI observability of workflow progress. The new CLIReporterPlugin hooks into lifecycle events (taskStart, taskEnd, etc.) and displays task states and a final summary. Documentation was updated, unit tests added, and the OpenSpec change proposal archived.


PR created automatically by Jules for task 16322371157142790787 started by @thalesraymond

- Implement CLIReporterPlugin for real-time workflow observability
- Hook into EventBus lifecycle events to print task states
- Print total execution time, successful, failed, and skipped metrics on workflow end
- Export plugin from index.ts
- Add documentation to README.md
- Add comprehensive unit tests achieving 100% coverage
- Archive OpenSpec change proposal

Co-authored-by: thalesraymond <32554150+thalesraymond@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 3, 2026

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

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 the CLIReporterPlugin, which provides real-time CLI observability for workflow progress, including documentation, specifications, and unit tests. The review feedback identifies a concurrency issue where maintaining state as instance properties makes the plugin unsafe for simultaneous workflow executions. It is recommended to derive summary statistics from the results Map in the workflowEnd event to ensure reliability. Additionally, the reviewer noted overlapping logic between the taskEnd and taskSkipped listeners that could result in duplicate logging and incorrect task counts.

Comment on lines +11 to +14
private startTime = 0;
private successful = 0;
private failed = 0;
private skipped = 0;
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.

high

The plugin maintains execution state (counters and start time) as instance properties. This makes the plugin unsafe for concurrent workflow executions on the same runner instance (or if the plugin instance is shared across multiple runners). If two workflows run concurrently, their counters will be interleaved and the startTime will be overwritten, leading to corrupted summaries.

To improve robustness, consider calculating the final summary statistics directly from the results Map provided in the workflowEnd event payload, which ensures the summary reflects the actual final state of that specific execution.

References
  1. When implementing an initialize or reset method in a stateful class, ensure all state-holding members are cleared to prevent stale data across multiple initializations.

Comment on lines +37 to +45
} else if (result.status === "cancelled") {
// According to the spec we track skipped, but cancelled is another status.
// Will just increment failed for now or leave it. We'll track it as skipped.
this.skipped++;
console.log(`[CANCELLED] Task ${step.name}`);
} else {
this.skipped++;
console.log(`[SKIPPED] Task ${step.name}`);
}
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

The logic in taskEnd for handling cancelled and other statuses overlaps with the taskSkipped listener. Specifically, both the else block here and the taskSkipped listener (lines 48-51) increment the skipped counter and log a [SKIPPED] message. If the runner emits both events for a single task (e.g., a task that is skipped but still triggers a terminal event), it will result in duplicate logs and double-counting in the summary.

Additionally, the else block acts as a catch-all that logs [SKIPPED] for any unknown status, which might mask future status types (like timeout).

Comment on lines +53 to +63
context.events.on("workflowEnd", () => {
const endTime = performance.now();
const totalTime = endTime - this.startTime;

console.log("--- Workflow Execution Summary ---");
console.log(`Total Time: ${Math.round(totalTime)}ms`);
console.log(`Successful: ${this.successful}`);
console.log(`Failed: ${this.failed}`);
console.log(`Skipped: ${this.skipped}`);
console.log("----------------------------------");
});
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

Instead of relying on manually incremented counters which are prone to concurrency issues, leverage the results Map passed in the workflowEnd event payload to generate the summary. This ensures the counts are derived from the source of truth for the workflow's completion state. While this involves creating an array from the map values, the performance impact is negligible for a reporter compared to the gain in reliability.

    context.events.on("workflowEnd", ({ results }) => {
      const endTime = performance.now();
      const totalTime = endTime - this.startTime;

      const counts = Array.from(results.values()).reduce(
        (acc, res) => {
          if (res.status === "success") acc.successful++;
          else if (res.status === "failure") acc.failed++;
          else acc.skipped++;
          return acc;
        },
        { successful: 0, failed: 0, skipped: 0 }
      );

      console.log("--- Workflow Execution Summary ---");
      console.log("Total Time: " + Math.round(totalTime) + "ms");
      console.log("Successful: " + counts.successful);
      console.log("Failed: " + counts.failed);
      console.log("Skipped: " + counts.skipped);
      console.log("----------------------------------");
    });
References
  1. Before merging loops or removing duplicate checks for code simplification, consider the performance impact. Retaining separate logic can be justified if it avoids expensive operations (like JSON.stringify or heavy string manipulation) and provides significant, benchmarked performance gains.

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