Skip to content

Add concurrent multi-workflow logs reports - #57976

Merged
pelikhan merged 4 commits into
mainfrom
copilot/update-logs-command-download-reports
Sep 2, 2026
Merged

Add concurrent multi-workflow logs reports#57976
pelikhan merged 4 commits into
mainfrom
copilot/update-logs-command-download-reports

Conversation

Copilot AI commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

gh aw logs previously handled one workflow and repository per invocation. This change supports resilient, concurrent reporting across workflows in multiple repositories.

  • Targeting

    • Accept multiple positional workflows.
    • Support owner/repo/workflow and full workflow file paths.
    • Preserve --repo behavior.
  • Downloads

    • Process workflow targets concurrently.
    • Bound aggregate artifact concurrency.
    • Coordinate rate-limit checks across workers with cancellation support.
  • Reporting and caching

    • Merge successful downloads into one report.
    • Isolate caches by repository and workflow.
    • Retain per-target continuation cursors for partial results.
    • Skip inaccessible or missing targets when other targets succeed.
gh aw logs \
  github/gh-aw/daily-report \
  other-org/operations/.github/workflows/weekly-report.yml

Copilot AI and others added 2 commits September 2, 2026 15:20
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI requested a review from pelikhan September 2, 2026 15:28
@pelikhan
pelikhan marked this pull request as ready for review September 2, 2026 16:11
Copilot AI balanced review requested due to automatic review settings September 2, 2026 16:11
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

⚠️ Security scanning failed for Ponytail Reviewer. Review the logs for details.

Lean already. Ship.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • ab.chatgpt.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

Generated by Ponytail Reviewer for #57976

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-09-02T16:14:47Z
review_event: REQUEST_CHANGES
top_themes:
  - local workflows misparsed as cross-repo targets
  - workflow target goroutines capture loop variable
files_reviewed:
  - pkg/cli/logs_command.go
  - pkg/cli/logs_multi.go
  - pkg/cli/logs_orchestrator.go
  - pkg/cli/logs_orchestrator_download.go
  - pkg/cli/logs_orchestrator_render.go
  - pkg/cli/logs_orchestrator_types.go
  - pkg/cli/logs_rate_limit.go
  - pkg/cli/logs_run_processor.go
  - pkg/cli/logs_command_test.go
  - pkg/cli/logs_multi_test.go
  - pkg/cli/logs_rate_limit_test.go
  - cmd/gh-aw/argument_syntax_test.go
comment_count: 2

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 53.8 AIC · ⌖ 7.48 AIC · ⊞ 21.8K ·
Comment /review to run again

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor
🏗️ ADR required — draft added for multi-workflow logs design

I generated a draft ADR at docs/adr/57976-add-concurrent-multi-workflow-logs-reports.md and queued it for this PR branch.

Why this gate triggered

  • The PR adds 599 lines in default business-logic directories, which exceeds the 100-line enforcement threshold.
  • The diff introduces a significant design decision: gh aw logs now supports concurrent multi-workflow, cross-repository reporting with shared rate-limit coordination and per-target cache isolation.

Evidence used

  • PR description: expands gh aw logs from one workflow/repository per invocation to resilient concurrent reporting across workflows and repositories.
  • Diff: new pkg/cli/logs_multi.go, command contract changes in pkg/cli/logs_command.go, shared rate-limit gate in pkg/cli/logs_rate_limit.go, and report schema/orchestration updates.

Next action

  • Review and refine the draft ADR, especially the decision wording and trade-offs.
  • Keep it as Draft until the maintainers confirm the architecture and then update status in a follow-up.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · pi · gpt54 · 20.4 AIC · ⌖ 11.1 AIC · ⊞ 9.8K ·
Comment /review to run again

@github-actions github-actions 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.

Request changes

The new multi-workflow path introduces at least two merge-blocking regressions: local workflow arguments can now be misparsed as cross-repo targets, and the worker goroutines capture the loop variable so results can be attributed to the wrong target.

Blocking themes
  1. Target parsing is no longer behavior-safe. The new owner/repo/workflow detection accepts too many relative paths and can silently redirect a local workflow argument into remote lookup.
  2. The concurrency fan-out is not safe as written. Each worker closes over the shared range variable, so multi-target downloads can duplicate the last target or scramble per-target errors and continuations.

Those are correctness issues in the core command path, so this should not merge before they are fixed.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 53.8 AIC · ⌖ 7.48 AIC · ⊞ 21.8K
Comment /review to run again

Comment thread pkg/cli/logs_command.go
return logsWorkflowTarget{workflowName: workflowName}, err
}

func splitCrossRepoWorkflowTarget(arg string) (string, string, bool) {

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.

This cross-repo splitter is too eager: any non-existent path with three slash-separated segments is treated as owner/repo/workflow, so a local workflow under a nested directory like docs/foo/bar.md stops being resolved locally and gets sent to a remote repo lookup instead.

💡 Why this blocks

splitCrossRepoWorkflowTarget() only excludes absolute paths, .github/..., and paths that already exist on disk. That means valid relative workflow paths in other directories are reinterpreted as cross-repo targets as soon as they contain two / separators. This is a behavior break in the CLI parser, not just a validation edge case, and it will route the command at the wrong repository instead of failing or using the local file the caller meant.

Tighten the parser so cross-repo mode only activates for unambiguous repo slugs, or fall back to local resolution before treating a token as owner/repo/workflow.

Comment thread pkg/cli/logs_multi.go
sem := make(chan struct{}, workerCount)
perTargetDownloads := max(1, getMaxConcurrentDownloads()/workerCount)
for _, target := range targets {
wg.Go(func() {

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.

This goroutine closes over the range variable target, so concurrent collectors can all observe the same final target and duplicate or misattribute downloads.

💡 Why this blocks

for _, target := range targets { wg.Go(func() { ... target ... }) } captures the loop variable by reference. In Go, that means every worker shares the same variable storage unless you rebind it inside the loop first. The failure mode here is nasty: output directories, repo overrides, errors, and continuations can all be attached to the wrong workflow target, which defeats the whole point of the multi-target orchestration.

Rebind inside the loop before spawning the goroutine, e.g. target := target, and then use that stable value in the closure.

Copilot AI 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.

🟡 Changes recommended

Host-aware throttling, collision-safe cache isolation, and partial-result propagation must be corrected for reliable concurrent reporting.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds concurrent, resilient log reporting across multiple workflows and repositories.

Changes:

  • Parses multiple local, cross-repository, and host-qualified workflow targets.
  • Collects targets concurrently with bounded artifact downloads and merged reporting.
  • Adds per-target continuations, cache directories, tests, and release notes.
File summaries
File Description
.changeset/minor-multi-workflow-logs.md Records the minor feature release.
cmd/gh-aw/argument_syntax_test.go Updates repeatable argument expectations.
pkg/cli/logs_command.go Parses and dispatches multiple targets.
pkg/cli/logs_command_test.go Tests target parsing and help text.
pkg/cli/logs_multi.go Implements concurrent collection and merging.
pkg/cli/logs_multi_test.go Tests concurrency and partial failures.
pkg/cli/logs_orchestrator.go Separates collection from rendering.
pkg/cli/logs_orchestrator_download.go Adds shared throttling and download limits.
pkg/cli/logs_orchestrator_render.go Adds per-target continuations to reports.
pkg/cli/logs_orchestrator_types.go Defines internal multi-target state.
pkg/cli/logs_rate_limit.go Adds a shared rate-limit gate.
pkg/cli/logs_rate_limit_test.go Tests cancellable gate waiting.
pkg/cli/logs_report.go Defines multi-target continuation output.
pkg/cli/logs_run_processor.go Supports per-target concurrency bounds.
Review details
  • Files reviewed: 15/15 changed files
  • Comments generated: 5
  • Review effort level: Balanced

Comment thread pkg/cli/logs_multi.go
if target.repoOverride == "" {
return filepath.Join(root, workflowDir)
}
repoDir := "repo-" + stringutil.SanitizeForFilename(target.repoOverride)
case <-ctx.Done():
return contextCause(ctx)
}
return checkAndWaitForRateLimit(ctx, verbose)
Comment thread pkg/cli/logs_multi.go Outdated
}

results := collectLogsTargets(ctx, opts, targets)
processedRuns, continuations, timeoutReached, allErrors := mergeLogsTargetResults(results, initialErrors)
return nil
}
if err := checkAndWaitForRateLimit(ctx, verbose); err != nil {
if err := checkAndWaitForRateLimitShared(ctx, verbose); err != nil {
Comment thread pkg/cli/logs_multi.go
targetOpts.OutputDir = logsTargetOutputDir(opts.OutputDir, target)
targetOpts.SummaryFile = ""
targetOpts.Train = false
targetOpts.SuppressRender = true

@github-actions github-actions 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.

Skills-Based Review 🧠

Applied /codebase-design, /tdd, and /diagnosing-bugs to the new multi-workflow log collection logic (logs_multi.go, logs_command.go).

📋 Key Themes & Highlights

Key Themes

  • Cross-platform path handling: displayName() mixes OS-native path joining with the forward-slash owner/repo/workflow vocabulary used elsewhere, risking inconsistent messages on Windows.
  • Concurrency budget fairness: the static per-target download concurrency split (getMaxConcurrentDownloads()/workerCount) doesn't rebalance when targets finish at different speeds; no test covers uneven completion.
  • Ambiguous target resolution: splitCrossRepoWorkflowTarget falls back to os.Stat to distinguish cross-repo targets from local paths — a filesystem-state-dependent heuristic that isn't covered by a collision-case test.

Positive Highlights

  • ✅ Clean separation of per-target orchestration (logs_multi.go) from single-target logic (logs_orchestrator.go), matching the existing LogsDownloadOptions interface style well.
  • ✅ Good resilience design: partial failures across targets are collected and reported without discarding successful results, and this is well tested in TestDownloadWorkflowLogsForTargetsConcurrentAndResilient.
  • ✅ Shared rate-limit gate (checkAndWaitForRateLimitShared) is a sensible way to stagger concurrent workers against a single API quota, with cancellation correctly tested.

None of the findings are blocking; they're refinement suggestions for polish and edge-case robustness.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 75.9 AIC · ⌖ 14.9 AIC · ⊞ 10.3K
Comment /matt to run again

Comments that could not be inline-anchored

pkg/cli/logs_multi.go:161

[/codebase-design] displayName() uses filepath.Join(t.repoOverride, t.workflowName), which normalizes separators to the OS path separator. On Windows this turns owner/repo + .github/workflows/x.yml into a backslash-joined string, inconsistent with the forward-slash owner/repo/workflow vocabulary used everywhere else in this PR (flags, docs, splitCrossRepoWorkflowTarget).

<details>
<summary>💡 Suggested fix</summary>

func (t logsWorkflowTarget) displayName() string {
	if</details>

<details><summary>pkg/cli/logs_multi.go:412</summary>

**[/tdd]** `perTargetDownloads := max(1, getMaxConcurrentDownloads()/workerCount)` divides the total download budget evenly per workflow worker, but if a fast target finishes early its unused concurrency slots aren&#39;t reclaimed by slower targetsthe pool sits under-utilized for the remainder of the run. No test exercises this uneven-completion scenario (only the equal-and-immediate-completion path is covered in `logs_multi_test.go`).

&lt;details&gt;
&lt;summary&gt;💡 Suggested test&lt;/summary&gt;

Add a test</details>

<details><summary>pkg/cli/logs_command.go:292</summary>

**[/diagnosing-bugs]** `os.Stat(arg)` is used to disambiguate a cross-repo target (`owner/repo/workflow`) from a local file path, but this makes CLI argument parsing depend on the invoking directory&#39;s filesystem state. A workflow name that happens to collide with an existing local file/dir (e.g. a repo checked out with a folder literally named `owner`) would silently be treated as local instead of cross-repo, with no test covering that ambiguity.

&lt;details&gt;
&lt;summary&gt;💡 Suggestion&lt;/summary&gt;

Con</details>

@github-actions github-actions 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.

Reviewed the new concurrent multi-workflow logs feature (pkg/cli/logs_multi.go + supporting orchestrator/rate-limit changes). Architecture is solid: bounded worker concurrency via semaphore, per-target output-dir isolation to avoid run-ID collisions, shared rate-limit gate with context-cancellation support, and good test coverage proving concurrency and partial-failure resilience.

One real correctness gap found: the multi-target path never aggregates/forwards countLimitReached, so the dateRangeCoverageWarning staleness check is effectively dead for combined multi-workflow reports (see inline comment on logs_multi.go). This could silently mislead users about data completeness within an explicit date range, so I'm requesting changes to fix it — it should be a small, low-risk fix mirroring the single-target path.

No other blocking issues found (build could not be run locally — sandboxed environment lacks network access to fetch the pinned Go 1.26.7 toolchain — so this review is based on careful manual reading of the diff).

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • proxy.golang.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "proxy.golang.org"

See Network Configuration for more information.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · copilot · sonnet50 · 143.7 AIC · ⌖ 14.8 AIC · ⊞ 8.3K

Comment thread pkg/cli/logs_multi.go
checkStaleness: true,
suppressRender: opts.SuppressRender,
continuations: continuations,
})

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.

mergeLogsTargetResults (lines 130-156) tracks timeoutReached across targets but never aggregates countLimitReached from targetResult.result.countLimitReached, and this renderLogsOutputOptions{} literal never sets countLimitReached (defaults to false).

Compare with the single-target path in logs_orchestrator.go (DownloadWorkflowLogs), which explicitly passes countLimitReached: result.countLimitReached into the same options struct. Here, checkStaleness: true is set but dateRangeCoverageWarning(processedRuns, opts.startDate, opts.endDate, opts.countLimitReached) (see logs_orchestrator_render.go:77 and logs_orchestrator.go:121) requires partial (i.e. countLimitReached) to be true to ever emit its coverage warning. For multi-target reports this means the warning is silently dead: if a per-target --count limit is hit within an explicit start/end date range, users get no signal that the combined report may be missing runs for that range, even though the same situation would be flagged for a single-target run.

Suggest: have mergeLogsTargetResults also OR together targetResult.result.countLimitReached (similar to timeoutReached), return it, and pass it here as countLimitReached: countLimitReached.

@copilot please address this.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the active blocking feedback before this PR moves forward.

  • Review threads still point to correctness issues in the multi-workflow path.
  • Blocking reviews to address:
    • PR Code Quality Reviewer: local workflow targets can be misparsed as cross-repo inputs and worker goroutines may attribute results to the wrong target.
    • Impeccable Skills Reviewer: the multi-target path should propagate countLimitReached so date-range coverage warnings remain accurate.
  • After pushing fixes, please refresh the branch and run the pr-finisher skill.

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 18.1 AIC · ⌖ 8.7 AIC · ⊞ 9.2K ·
Comment /souschef to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please address the active blocking feedback before this PR moves forward.

  • Review threads still point to correctness issues in the multi-workflow path....

Addressed the blocking multi-target feedback in commit c6a3f9d: local relative paths are no longer classified as cross-repository targets, concurrent target collection preserves each target association, and countLimitReached is ORed into the combined render options.

Copilot AI requested a review from gh-aw-bot September 2, 2026 17:03
@pelikhan
pelikhan merged commit 1d8ba87 into main Sep 2, 2026
32 checks passed
@pelikhan
pelikhan deleted the copilot/update-logs-command-download-reports branch September 2, 2026 17:27
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.88.2

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.

4 participants