Skip to content

Fix email report forecast visibility split - #18

Merged
kgrizz-git merged 5 commits into
mainfrom
fix/email-forecast-visibility-split
Aug 11, 2026
Merged

Fix email report forecast visibility split#18
kgrizz-git merged 5 commits into
mainfrom
fix/email-forecast-visibility-split

Conversation

@kgrizz-git

@kgrizz-git kgrizz-git commented Aug 10, 2026

Copy link
Copy Markdown
Owner

The email report forecast was counting total Actions minutes (private + public) against the 2,000-minute free-tier limit. Only private repos consume quota—public repos are free. This fix ensures the email report now correctly uses private-only minutes, matching the legacy terminal report behavior.

Changes

  • Added attach_actions_visibility_split() call to build_report_data() to compute private/public split on account-level Actions minutes
  • Expanded needs_repos to include include_actions so repos are always fetched when needed for the split
  • Extended get_repo_consumers() to expose raw per-repo rows (_raw_rows key) for reuse by the visibility split
  • Split logic intelligently reuses existing consumer rows when available (zero extra API calls); falls back to fetch_repo_actions_table() when consumers aren't enabled

Impact

  • Email forecast scope note now shows "(private repos — quota-counted)" label
  • Key Insight 1 now appears when private minutes ≥ 100%
  • Warning threshold evaluates against private-only minutes
  • Text actions section displays the private/public breakdown

Tests

Added 4 new test cases:

  • Consumers enabled: verifies rows are reused, no duplicate API calls
  • Actions only: verifies fetch_repo_actions_table() fallback is used
  • Actions disabled: verifies per-repo fetch is skipped
  • Consumers error: verifies fallback to fetch_repo_actions_table()

Summary by CodeRabbit

  • Bug Fixes
    • Email reports now calculate free-tier usage using private Actions minutes only.
    • Actions forecasts, insights, warnings, and text/HTML summaries now show accurate visibility-based usage.
    • Reports use available usage data when possible and fetch additional repository data when needed.
  • Documentation
    • Added changelog and implementation documentation for the email forecast visibility update.
  • Chores
    • Added guidance to protect sensitive credential files.
    • Documented a future source-coverage improvement target.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f6e75c42-e94d-47bd-bd2a-bcf1720e45ad

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The report now separates private and public Actions minutes. It reuses consumer rows when available and otherwise fetches repository Actions data. Repository loading occurs when Actions reporting is enabled. Tests cover fallback, filtering, and disabled Actions behavior.

Changes

Actions visibility reporting

Layer / File(s) Summary
Report data flow
src/github_usage/report_data.py
Actions-enabled reports now load repositories and attach private/public Actions totals. The flow reuses consumer rows or fetches repository Actions data.
Consumer row contract and validation
src/github_usage/report_optional.py, tests/test_report_data.py
get_repo_consumers exposes _raw_rows. Tests cover reuse, fallback fetching, disabled Actions, and only_public filtering.
Implementation records and repository guidance
docs/superpowers/plans/archived/..., CHANGELOG.md, TO_DO.md, AGENTS.md
The completed plan and changelog describe the visibility split. The code health task and credential-file rule are recorded.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReportData
  participant Consumers
  participant ActionsTable
  participant VisibilitySplit
  ReportData->>Consumers: collect consumer rows
  alt Consumer rows available
    Consumers-->>ReportData: return raw repository rows
  else Consumer retrieval fails or is disabled
    ReportData->>ActionsTable: fetch repository Actions rows
    ActionsTable-->>ReportData: return Actions data
  end
  ReportData->>VisibilitySplit: attach private/public minute totals
Loading

Possibly related PRs

Suggested reviewers: cursoragent

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: correcting the email report forecast visibility split.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/email-forecast-visibility-split

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.

@kgrizz-git

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
tests/test_report_data.py (1)

513-531: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test filtering with mixed repository input.

The fixture at Line 524 contains only a public repository. The mocked fallback also returns only a public row. This test passes if the Actions path stops filtering private repositories before the fallback fetch. Use _TWO_REPOS and assert that the fallback receives no private repository when only_public=True.

🤖 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 `@tests/test_report_data.py` around lines 513 - 531, Update the test around
build_report_data to use the mixed-repository _TWO_REPOS fixture instead of only
_PUBLIC_REPO, while keeping only_public=True. Assert that the mocked
fetch_repo_actions_table fallback receives only the public repository and
excludes the private one, ensuring filtering occurs before the fallback fetch.
🤖 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
`@docs/superpowers/plans/archived/2026-08-10-fix-email-forecast-visibility-split.md`:
- Line 47: Update build_report_data() so the errors returned by the fallback
fetch_repo_actions_table() call are merged into report["errors"] before
attach_actions_visibility_split() runs. Replace the discarded _errors binding
with propagation of those errors, while preserving the existing rows and
visibility-split behavior.

In `@src/github_usage/report_data.py`:
- Around line 407-410: Update the caller around fetch_repo_actions_table so its
returned _errors are recorded through the report error contract, and ensure
attach_actions_visibility_split marks the result incomplete or skips the
private-minute forecast when failures exist. Add a regression test covering a
failed repository Actions billing fetch and the resulting partial-fallback
behavior.
- Around line 339-341: Update estimate_api_request_count() to include one
fallback Actions request per repository when include_actions is enabled,
matching the fetch_repo_actions_table(api, repos) path used by
build_report_data() when repo_consumers data is unavailable. Add a low-quota
test covering Actions-only reporting and verify the quota check accounts for
these requests.

In `@src/github_usage/report_optional.py`:
- Around line 56-58: Normalize the per-repository rows before attaching the
Actions visibility split so storage is attributed correctly. In
src/github_usage/report_optional.py lines 56-58, update the _raw_rows data
passed from get_repo_consumers() to retain or derive storage_gb_hours from
storage_avg_mb. In src/github_usage/report_data.py lines 399-405, ensure the
attachment call consumes rows containing storage_gb_hours rather than
storage_avg_mb alone, preserving existing behavior for other row fields.

---

Nitpick comments:
In `@tests/test_report_data.py`:
- Around line 513-531: Update the test around build_report_data to use the
mixed-repository _TWO_REPOS fixture instead of only _PUBLIC_REPO, while keeping
only_public=True. Assert that the mocked fetch_repo_actions_table fallback
receives only the public repository and excludes the private one, ensuring
filtering occurs before the fallback fetch.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d9491270-c178-44d9-bf87-3a31ec982836

📥 Commits

Reviewing files that changed from the base of the PR and between d8a7eb3 and cf35ce7.

📒 Files selected for processing (7)
  • AGENTS.md
  • CHANGELOG.md
  • TO_DO.md
  • docs/superpowers/plans/archived/2026-08-10-fix-email-forecast-visibility-split.md
  • src/github_usage/report_data.py
  • src/github_usage/report_optional.py
  • tests/test_report_data.py


1. **`include_consumers=True`** — `get_repo_consumers()` already calls `get_actions_per_repo()` for every repo (`report_optional.py:35`) and builds rows with `repo`, `minutes`, `storage_avg_mb`, and `visibility` — the same shape `attach_actions_visibility_split()` needs. Extend `get_repo_consumers()` to return its raw rows under a `_raw_rows` key (or a dedicated public key), then pass them directly to `attach_actions_visibility_split()`. This avoids duplicating every per-repo API call.

2. **`include_consumers=False`** — `get_repo_consumers()` is not called, so no rows are available. Call `fetch_repo_actions_table(api, repos)` from `report_actions.py:172`, which returns `(rows, errors)` — the same function the legacy path uses.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Propagate fallback fetch errors before finalizing the visibility split.

fetch_repo_actions_table() returns (rows, errors), but build_report_data() currently binds the second value to _errors and drops it. A failed repository request then produces a partial visibility split without an entry in report["errors"]. The email can undercount private minutes and the forecast.

Merge the fallback errors before calling attach_actions_visibility_split().

Proposed fix
-            rows, _errors = fetch_repo_actions_table(api, repos)
+            rows, fetch_errors = fetch_repo_actions_table(api, repos)
+            errors.update(fetch_errors)
🤖 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
`@docs/superpowers/plans/archived/2026-08-10-fix-email-forecast-visibility-split.md`
at line 47, Update build_report_data() so the errors returned by the fallback
fetch_repo_actions_table() call are merged into report["errors"] before
attach_actions_visibility_split() runs. Replace the discarded _errors binding
with propagation of those errors, while preserving the existing rows and
visibility-split behavior.

Comment on lines +339 to +341
needs_repos = (
include_actions or include_consumers or include_artifact_storage or include_release_assets
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the estimator definition and all call sites.
ast-grep outline src/github_usage/report_data.py --items all --type function
rg -n -A100 -B5 '^def estimate_api_request_count\(' src/github_usage/report_data.py
rg -n -C5 'estimate_api_request_count\(' src tests

# Inspect Actions-only coverage and quota-related tests.
rg -n -C5 'include_actions|core_remaining|estimated_incremental_requests|quota' tests/test_report_data.py

Repository: kgrizz-git/github-usage

Length of output: 1261


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- report_data.py relevant sections ---'
cat -n src/github_usage/report_data.py | sed -n '230,430p'

printf '%s\n' '--- estimator definitions and call sites ---'
rg -n -C8 'estimate_api_request_count|estimated_incremental_requests|core_remaining' . \
  -g '*.py' -g '*.toml' -g '*.yaml' -g '*.yml'

printf '%s\n' '--- report-data test files ---'
git ls-files 'tests/*' | sort

Repository: kgrizz-git/github-usage

Length of output: 30109


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Actions fallback implementation ---'
rg -n -A100 -B10 'def fetch_repo_actions_table|def get_actions_usage' src/github_usage/report_actions.py src/github_usage/report_data.py

printf '%s\n' '--- Actions request tests and report-data fixtures ---'
rg -n -C12 'fetch_repo_actions_table|include_actions|build_report_data|rate_limit|quota' \
  tests/test_report_actions.py tests/test_report_data.py tests/_fakes.py

printf '%s\n' '--- optional estimator implementation and constants ---'
cat -n src/github_usage/report_optional.py | sed -n '1,180p'

Repository: kgrizz-git/github-usage

Length of output: 45996


Include Actions fallback requests in the quota estimate.

When include_actions is enabled without usable repo_consumers rows, build_report_data() calls fetch_repo_actions_table(api, repos) after the quota check. estimate_api_request_count() does not count these per-repository requests. Add the Actions request count to the estimate and add an Actions-only low-quota test.

🤖 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 `@src/github_usage/report_data.py` around lines 339 - 341, Update
estimate_api_request_count() to include one fallback Actions request per
repository when include_actions is enabled, matching the
fetch_repo_actions_table(api, repos) path used by build_report_data() when
repo_consumers data is unavailable. Add a low-quota test covering Actions-only
reporting and verify the quota check accounts for these requests.

Comment thread src/github_usage/report_data.py Outdated
Comment on lines +407 to +410
rows, _errors = fetch_repo_actions_table(api, repos)
attach_actions_visibility_split(
report, rows, only_public=only_public, only_private=only_private
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve failed repository Actions fetches.

fetch_repo_actions_table() omits a repository row when its billing request fails and returns that failure in its second result. Line 407 discards those failures. The visibility split can then understate private minutes while the report shows no incomplete-data error. Record these failures through the report error contract and mark the split incomplete, or skip the private-minute forecast. Add a partial-fallback regression test.

🤖 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 `@src/github_usage/report_data.py` around lines 407 - 410, Update the caller
around fetch_repo_actions_table so its returned _errors are recorded through the
report error contract, and ensure attach_actions_visibility_split marks the
result incomplete or skips the private-minute forecast when failures exist. Add
a regression test covering a failed repository Actions billing fetch and the
resulting partial-fallback behavior.

Comment on lines +56 to +58
# Used by report_data.build_report_data() to pass per-repo rows to
# attach_actions_visibility_split() without duplicate API calls.
"_raw_rows": rows,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the split helper defaults and row-field access.
ast-grep outline src/github_usage/usage_split.py --items all --type function
rg -n -A120 -B5 '^def split_rows_by_visibility\(' src/github_usage/usage_split.py
rg -n -A80 -B5 '^def finalize_actions_split\(' src/github_usage/usage_split.py

# Compare fallback and consumer row construction.
rg -n -A45 -B5 '^def fetch_repo_actions_table\(' src/github_usage/report_actions.py
rg -n -A45 -B5 '^def get_repo_consumers\(' src/github_usage/report_optional.py

Repository: kgrizz-git/github-usage

Length of output: 14870


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the accumulator and the complete attachment path.
sed -n '100,205p' src/github_usage/usage_split.py
sed -n '282,345p' src/github_usage/usage_split.py
sed -n '370,425p' src/github_usage/report_data.py

# Find all consumers of the reused rows and all storage_avg_mb/storage_gb_hours
# transformations to determine whether the schema mismatch affects output.
rg -n -A20 -B10 'storage_avg_mb|storage_gb_hours|_raw_rows|attach_actions_visibility_split' src tests 2>/dev/null || true

Repository: kgrizz-git/github-usage

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '100,205p' src/github_usage/usage_split.py
sed -n '282,345p' src/github_usage/usage_split.py
sed -n '370,425p' src/github_usage/report_data.py
rg -n -A20 -B10 'storage_avg_mb|storage_gb_hours|_raw_rows|attach_actions_visibility_split' src tests 2>/dev/null || true

Repository: kgrizz-git/github-usage

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- usage_split accumulator ---'
sed -n '145,200p' src/github_usage/usage_split.py

printf '%s\n' '--- attach_actions_visibility_split ---'
sed -n '282,345p' src/github_usage/usage_split.py

printf '%s\n' '--- report_data attachment call ---'
sed -n '390,415p' src/github_usage/report_data.py

Repository: kgrizz-git/github-usage

Length of output: 4160


Normalize consumer rows before attaching the Actions split.

attach_actions_visibility_split() reads storage_gb_hours by default. _raw_rows contains only storage_avg_mb, so reused rows contribute zero visibility storage and place the full account storage in unattributed_storage_gb_hours. Retain storage_gb_hours in get_repo_consumers() rows, or normalize the rows before the attachment call.

📍 Affects 2 files
  • src/github_usage/report_optional.py#L56-L58 (this comment)
  • src/github_usage/report_data.py#L399-L405
🤖 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 `@src/github_usage/report_optional.py` around lines 56 - 58, Normalize the
per-repository rows before attaching the Actions visibility split so storage is
attributed correctly. In src/github_usage/report_optional.py lines 56-58, update
the _raw_rows data passed from get_repo_consumers() to retain or derive
storage_gb_hours from storage_avg_mb. In src/github_usage/report_data.py lines
399-405, ensure the attachment call consumes rows containing storage_gb_hours
rather than storage_avg_mb alone, preserving existing behavior for other row
fields.

@sonarqubecloud

Copy link
Copy Markdown

@kgrizz-git
kgrizz-git merged commit 1dc4865 into main Aug 11, 2026
13 checks passed
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