Skip to content

fix(evolution): account for every generated file - #7

Merged
jhinpan merged 3 commits into
mainfrom
fix/evolution-budget-accounting
Jul 22, 2026
Merged

fix(evolution): account for every generated file#7
jhinpan merged 3 commits into
mainfrom
fix/evolution-budget-accounting

Conversation

@jhinpan

@jhinpan jhinpan commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • force porcelain status to expand untracked directories into individual files
  • remeasure the refresh after writing its generated summary
  • enforce file/line budgets against the final reviewable change set
  • add a regression test for nested generated candidate ledgers

Verification

  • python3 tests/test_evolution.py
  • python3 scripts/validate.py
  • git diff --check

Found while deploying the live cron automation: bootstrap PR #6 contained 13 files while its pre-fix summary reported 2 because Git collapsed candidates/runs/ into one untracked directory entry.

Summary by Sourcery

Ensure evolution refresh accounting includes all generated files and enforces budgets against the final change set.

Bug Fixes:

  • Count untracked files individually in git status so nested generated files are included in change accounting.
  • Recalculate and enforce file/line budgets after writing the generated summary so limits apply to the final reviewable changes.

Tests:

  • Add a regression test verifying untracked directories are expanded into individual files for budgeting.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@sourcery-ai

sourcery-ai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Reviewer's Guide

Updates the evolution refresh workflow to count every generated file (including those in previously-collapsed untracked directories and the generated summary itself) when enforcing file/line budgets, and adds a regression test to lock in the new git status behavior.

Sequence diagram for updated refresh budget enforcement workflow

sequenceDiagram
    title run_refresh sequence with remeasurement and budget enforcement
    actor Cron
    participant run_refresh
    participant _write_summary
    participant _git_changes
    participant _run
    participant enforce_change_budget

    Cron->>run_refresh: run_refresh(args, root)
    alt [not args.dry_run]
        run_refresh->>_write_summary: _write_summary(root, run_date, summary)

        run_refresh->>_git_changes: _git_changes(root)
        _git_changes->>_run: _run(["git","status","--porcelain=v1","--untracked-files=all"], root)
        _run-->>_git_changes: output
        _git_changes-->>run_refresh: changed_files, changed_lines
        run_refresh->>_write_summary: _write_summary(root, run_date, summary)

        run_refresh->>_git_changes: _git_changes(root)
        _git_changes->>_run: _run(["git","status","--porcelain=v1","--untracked-files=all"], root)
        _run-->>_git_changes: output
        _git_changes-->>run_refresh: changed_files, changed_lines
        run_refresh->>_write_summary: _write_summary(root, run_date, summary)

        run_refresh->>enforce_change_budget: enforce_change_budget(changed_files, changed_lines, args.max_files, args.max_lines)
    end
Loading

File-Level Changes

Change Details Files
Ensure git status expands untracked directories so every generated file is individually accounted for in budget calculations.
  • Change _git_changes to invoke git status --porcelain=v1 --untracked-files=all so untracked directories are expanded into individual file entries.
  • Keep the existing parsing logic but rely on the expanded porcelain output to return per-file paths and line counts.
scripts/evolve/refresh.py
Recompute and persist refresh summary after generating it, then enforce budgets against the final reviewable change set including the summary file itself.
  • After initial summary write, rerun _git_changes to pick up the generated summary file and update the in-memory summary with the new changed_files/changed_lines.
  • Write the summary again with the updated counts, then recompute once more to reflect the final change set before enforcement.
  • Invoke enforce_change_budget with the final changed_files and changed_lines using CLI-provided max_files and max_lines.
  • Ensure validation and tests still run only after budget enforcement passes.
scripts/evolve/refresh.py
Add regression test to verify untracked directory contents are expanded and counted correctly for budgeting.
  • Introduce a new test that initializes a temporary git repo, creates nested untracked files under candidates/runs/today/, and calls _git_changes.
  • Assert that _git_changes returns individual file paths for each nested file rather than a collapsed directory entry and that changed_lines matches the number of created lines.
  • Import subprocess in the test module to support initializing the temporary git repository.
tests/test_evolution.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai 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.

Hey - I've found 2 issues, and left some high level feedback:

  • In run_refresh, the sequence of _write_summary and _git_changes calls is repeated three times; consider refactoring to a single helper or clearly documenting why multiple writes/measurements are required, and whether this can be reduced to the minimal number of passes needed to account for the summary file.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `run_refresh`, the sequence of `_write_summary` and `_git_changes` calls is repeated three times; consider refactoring to a single helper or clearly documenting why multiple writes/measurements are required, and whether this can be reduced to the minimal number of passes needed to account for the summary file.

## Individual Comments

### Comment 1
<location path="scripts/evolve/refresh.py" line_range="209-208" />
<code_context>
     }
     if not args.dry_run:
         _write_summary(root, run_date, summary)
+        # Include the generated summary itself and expand every untracked file
+        # before enforcing/reported budgets. A second measurement is needed
+        # because adding the summary path grows the summary by one list entry.
+        changed_files, changed_lines = _git_changes(root)
+        summary["changed_files"] = changed_files
+        summary["changed_lines"] = changed_lines
+        _write_summary(root, run_date, summary)
+        changed_files, changed_lines = _git_changes(root)
+        summary["changed_files"] = changed_files
</code_context>
<issue_to_address>
**issue (bug_risk):** The summary is written three times and `_git_changes` is called twice; this sequencing is hard to reason about and likely not doing exactly what the comment describes.

Right now the sequence is:
- write summary (v1)
- measure1
- write summary with measure1 (v2)
- measure2
- write summary with measure2 (v3)
- enforce budget based on measure2

That means the budget is enforced on the repo state as of v2, not the final on-disk summary (v3), and v3 itself changes the state after the budget check. This off‑by‑one in what’s being measured adds complexity and extra I/O.

Could we simplify this so that the budget is enforced on the final written summary (e.g., two writes and one/two measurements with a clear invariant about what’s included), or document why three writes/two measurements are strictly required?
</issue_to_address>

### Comment 2
<location path="tests/test_evolution.py" line_range="391" />
<code_context>
 def test_refresh_budget_rejects_unreviewable_change_sets():
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test that exercises `run_refresh`’s new behavior of enforcing budgets against the final change set including the generated summary file.

Production now re-runs `_git_changes` and calls `enforce_change_budget` after writing the generated summary so budgets apply to the final change set (including the summary file and fully expanded untracked files). The current tests only cover `_git_changes` directly and the existing `test_refresh_budget_rejects_unreviewable_change_sets()` behavior.

To fully validate this bugfix, add an integration-style test around `run_refresh` that:
- uses a temporary git repo,
- creates a case where the pre-summary measurement is under budget but the post-summary measurement exceeds it (e.g., via `max_files` so the summary file pushes it over), and
- verifies that `enforce_change_budget` is called with the final `changed_files`/`changed_lines` (e.g., via mocking) and that an over-budget scenario is rejected.

This will help prevent regressions where `run_refresh` might again enforce budgets on a pre-summary snapshot.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread scripts/evolve/refresh.py Outdated
Comment thread tests/test_evolution.py
@jhinpan
jhinpan merged commit 9b69029 into main Jul 22, 2026
3 checks passed
@jhinpan
jhinpan deleted the fix/evolution-budget-accounting branch July 22, 2026 23:33
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