Skip to content

Improve logs cache size diagnostics - #59336

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

pelikhan merged 4 commits into
mainfrom
copilot/update-logs-command

Conversation

Copilot AI commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

gh aw logs --max-storage only reported aggregate cache-folder sizes, making oversized entries difficult to identify.

  • Cache diagnostics

    • Report the 20 largest files by relative path and size.
    • Include total file count and indicate when results are truncated.
    • Keep output deterministic by sorting by size, then path.
  • Coverage

    • Cover ordering, truncation, relative paths, and missing cache directories.
  • Release

    • Add a patch changeset for the enhanced diagnostics.

Run: https://github.com/github/gh-aw/actions/runs/34185177055

Warning

Firewall blocked 1 domain

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

  • github.com

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

network:
  allowed:
    - defaults
    - "github.com"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 45.4 AIC · ⌖ 8.71 AIC · ⊞ 9K · ◷
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI requested a review from pelikhan September 8, 2026 02:02
@pelikhan
pelikhan marked this pull request as ready for review September 8, 2026 02:03
Copilot AI balanced review requested due to automatic review settings September 8, 2026 02:03
@github-actions

github-actions Bot commented Sep 8, 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 8, 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.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

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 #59336

@github-actions

github-actions Bot commented Sep 8, 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 8, 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 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.

Ponytail pass focused only on removable complexity.

  • One trim opportunity found in the new top-file accounting path; existing behavior can stay while simplifying implementation churn during traversal.

net: -8 lines possible.

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 #59336 · codex · gpt53codex · 3.91 AIC · ⌖ 4.2 AIC · ⊞ 13.9K
Comment /ponytail to run again

}
return walkErr
}
if info == nil || !info.Mode().IsRegular() {

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.

pkg/cli/logs_storage_limit.go:L173: shrink: full-sort on every visited file inside Walk. Keep a fixed-size top-K insert (or append+single sort at end) and sort once for output.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the latest commit: computeLogsCacheStats now appends file candidates during the walk and sorts once after the walk completes, instead of re-sorting on every visited file.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-09-08T02:06:59Z
review_event: COMMENT
top_themes: []
files_reviewed:
  - .changeset/patch-logs-cache-file-diagnostics.md
  - pkg/cli/logs_storage_limit.go
  - pkg/cli/logs_storage_limit_test.go
comment_count: 0

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 · 16.1 AIC · ⌖ 7.37 AIC · ⊞ 23.5K · ◷
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 cache-file diagnostics introduce a startup-time performance regression in the exact path that runs before downloads begin.

Blocking theme

The implementation sorts the candidate slice on every file visited while walking the logs cache. That turns a simple inspection step into work that grows roughly quadratically with cache size, so the larger the cache gets, the slower gh aw logs --max-storage becomes before it can do anything useful. This should be reworked to collect first and sort once, or to keep a bounded top-N structure.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 16.1 AIC · ⌖ 7.37 AIC · ⊞ 23.5K
Comment /review to run again

Comment thread pkg/cli/logs_storage_limit.go Outdated
return folders, nil
}

func largestLogsFiles(path string, limit int) ([]logsFileSize, int, error) {

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 adds an avoidable startup-time regression: largestLogsFiles sorts the growing slice on every visited file, so cache inspection degrades toward quadratic work as the logs cache grows.

💡 Why this should block

initialize() now calls this before any download starts, which means every gh aw logs --max-storage run pays the extra cost even when pruning never happens. On a cache with many files, repeatedly sorting inside filepath.Walk is much more expensive than collecting candidates once and sorting after the walk, or maintaining a bounded heap of the top N files.

A safer shape is:

err := filepath.Walk(path, func(filePath string, info os.FileInfo, walkErr error) error {
    // collect file metadata only
})
slices.SortFunc(files, compareLogsFileSizeDesc)
if len(files) > limit {
    files = files[:limit]
}

That keeps the diagnostics deterministic without turning a lightweight startup measurement into an O(n2 log n) pass.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: computeLogsCacheStats collects file candidates during the walk and sorts once after the walk, avoiding the O(n² log n) behavior.

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

The implementation adds an avoidable full cache traversal, and path tie-breaking lacks coverage.

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

Pull request overview

Adds detailed logs-cache diagnostics to help identify oversized files.

Changes:

  • Reports the 20 largest cache files with deterministic ordering and truncation details.
  • Adds focused tests and a patch changeset.
File summaries
File Description
pkg/cli/logs_storage_limit.go Collects and reports largest cache files.
pkg/cli/logs_storage_limit_test.go Tests limits, paths, and missing directories.
.changeset/patch-logs-cache-file-diagnostics.md Documents the patch release.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/cli/logs_storage_limit.go Outdated
return fmt.Errorf("failed to measure logs cache folders: %w", err)
}
l.reportStartingUsage(size, folders)
files, fileCount, err := largestLogsFiles(l.outputDir, maxReportedLogsCacheFiles)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: introduced computeLogsCacheStats, a single-pass walk that computes total size, per-folder sizes, and the bounded top-file list together, replacing the separate logsDirectorySize/logsFolderSizes/largestLogsFiles traversals.

Comment thread pkg/cli/logs_storage_limit.go Outdated
Comment on lines +183 to +184
if a.size == b.size {
return cmp.Compare(a.path, b.path)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added TestLargestLogsFilesTieBreaksByPath with equally sized files in reverse-lexical creation order to lock in the ascending path tie-breaker.

@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 cache diagnostics enhancement in logs_storage_limit.go. Logic is correct and well-tested (sorting, truncation, missing-dir cases covered). One non-blocking performance note left inline: the top-N tracking re-sorts the full slice on every walked file, which is O(n2 log n) for large caches.

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

Comments that could not be inline-anchored

pkg/cli/logs_storage_limit.go:68

Performance: this re-sorts the entire files slice on every single file encountered during the walk (slices.SortFunc inside the filepath.Walk callback), giving roughly O(n2 log n) behavior for a directory with n files. For a logs cache with many thousands of files this can be noticeably slow. Since only the top limit entries are ever kept, consider only re-sorting/truncating when len(files) &gt; limit (e.g. compare against the current minimum before appending), or maintain a bounded min-h…

@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 /diagnosing-bugs and /tdd to the new largestLogsFiles/reportStartingUsage logic. Two non-blocking suggestions posted inline.

📋 Key Themes & Highlights

Key Themes

  • Performance: largestLogsFiles sorts the whole accumulator on every file visited during the walk — O(n2 log n) for large caches, which is exactly the scenario --max-storage diagnostics target.
  • Test coverage: new tests cover largestLogsFiles sorting/limiting/missing-directory well, but the truncation-message branching in reportStartingUsage isn't directly exercised.

Positive Highlights

  • ✅ Deterministic ordering (size desc, then path) is well-tested and matches the PR's stated goal.
  • ✅ Missing-directory handling (os.IsNotExist) mirrors the existing logsFolderSizes/logsDirectorySize pattern for consistency.
  • ✅ Changeset included per release process.

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

Comment thread pkg/cli/logs_storage_limit.go Outdated
}
fileCount++
files = append(files, logsFileSize{path: relativePath, size: info.Size()})
slices.SortFunc(files, func(a, b logsFileSize) int {

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.

[/diagnosing-bugs] largestLogsFiles re-sorts the entire accumulator (slices.SortFunc) on every file visited during the walk, giving O(n2 log n) behavior. For a logs cache large enough to need --max-storage diagnostics (the exact scenario this PR targets), this could be thousands of files and noticeably slow down every logs invocation.

💡 Suggested approach

Since only the top limit (20) entries are kept, a cheaper pattern is to only re-sort/trim when the running list actually exceeds limit, or better, maintain a bounded min-heap keyed on size and do a single sort at the end:

var files []logsFileSize
...
files = append(files, logsFileSize{path: relativePath, size: info.Size()})
if len(files) > limit*4 { // keep it from growing unbounded before trimming
    slices.SortFunc(files, cmpFn)
    files = files[:limit]
}
...
// after Walk completes:
slices.SortFunc(files, cmpFn)
if len(files) > limit {
    files = files[:limit]
}

This keeps correctness while avoiding a full sort per file for large caches.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: computeLogsCacheStats collects candidates during the walk and sorts once at the end.

)))
logsOrchestratorLog.Printf("Logs cache folder size: folder=%q size=%d", folder.name, folder.size)
}
if fileCount > len(files) {

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.

[/tdd] The new truncation-message branch in reportStartingUsage ("showing %d largest of %d" vs "Logs cache files: %d") isn't directly exercised by a test — the added tests only cover largestLogsFiles, not the reporting output/formatting logic that decides which message to print.

💡 Suggested test

Add a test that calls reportStartingUsage (or an integration-style test through initialize) with fileCount > len(files) and fileCount == len(files), capturing stderr to assert the correct message variant is produced. This locks in the "indicate when truncated" behavior called out in the PR description.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added TestReportStartingUsageIndicatesTruncation and TestReportStartingUsageNoTruncation, which call reportStartingUsage directly and assert on the captured stderr output for both message variants.

@pelikhan

pelikhan commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

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

Copy link
Copy Markdown
Collaborator

@copilot please address the outstanding review feedback on cache traversal/performance, refresh the branch if needed, then run the pr-finisher skill before requesting another review.

Warning

Firewall blocked 1 domain

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

  • github.com

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

network:
  allowed:
    - defaults
    - "github.com"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 45.4 AIC · ⌖ 8.71 AIC · ⊞ 9K · ◷
Comment /souschef to run again

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

Copilot AI commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please address the outstanding review feedback on cache traversal/performance, refresh the branch if needed, then run the pr-finisher skill before requesting another review.

[!WARNING]...

Addressed: initialize() was left calling the removed logsFolderSizes/largestLogsFiles helpers after a prior merge, breaking the build. Replaced with a single-pass computeLogsCacheStats that walks the cache once for total size, folder sizes, and the bounded top-file list (fixing the traversal/performance feedback), added a path tie-breaker test with equal-sized files, and added tests for the truncation message in reportStartingUsage. make fmt, make lint, and make test-unit all pass locally.

Copilot AI requested a review from gh-aw-bot September 8, 2026 04:15
@pelikhan
pelikhan merged commit 16b070b into main Sep 8, 2026
32 checks passed
@pelikhan
pelikhan deleted the copilot/update-logs-command branch September 8, 2026 04:24
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.88.7

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