Skip to content

[limen RESOLVE-organvm-i-theoria-.github-458] resolve .github#458 (BLOCKED) - #463

Open
4444J99 wants to merge 1 commit into
mainfrom
limen/resolve-organvm-i-theoria-.github-458-f825
Open

[limen RESOLVE-organvm-i-theoria-.github-458] resolve .github#458 (BLOCKED)#463
4444J99 wants to merge 1 commit into
mainfrom
limen/resolve-organvm-i-theoria-.github-458-f825

Conversation

@4444J99

@4444J99 4444J99 commented Jun 19, 2026

Copy link
Copy Markdown
Member

Autonomous limen dispatch of task RESOLVE-organvm-i-theoria-.github-458.

Resolve blocked PR #458 ('[limen CIFIX-organvm-i-theoria--github] Fix pre-existing CI '), state=BLOCKED. Branch=limen/cifix-organvm-i-theoria--github-64ce, base=main. In the worktree: git fetch origin limen/cifix-organvm-i-theoria--github-64ce main; git checkout -B limen/cifix-organvm-i-theoria--github-64ce origin/limen/cifix-organvm-i-theoria--github-64ce; git rebase origin/main then address the failing checks / unresolved review threads; run the build/tests; git push --force-with-lease origin limen/cifix-organvm-i-theoria--github-64ce. If the branch is unrecoverable, instead REBUILD the same feature cleanly off origin/main as a fresh PR. Goal: make it mergeable.

Produced in an isolated worktree off origin — review before merge.

Summary by Sourcery

Introduce a reusable Python-based planner for staggered walkthrough scheduling and integrate it into the GitHub Actions workflow to make scheduling, execution, and completion tracking more robust and flexible.

Enhancements:

  • Replace inline schedule generation and jq-based date selection with a dedicated Python script that plans, selects, and completes staggered walkthrough batches.
  • Make the staggered scheduling workflow more resilient by reusing active schedules when possible, supporting forced regeneration, and safely committing schedule changes only when needed.
  • Improve workflow execution robustness by using each target repository’s default branch when dispatching walkthrough workflows and by wiring stagger interval and concurrency through environment-configurable settings.

Tests:

  • Add unit tests covering schedule generation, reuse vs regeneration, due-batch selection, and completion marking for the new staggered walkthrough scheduling helper script.

limen task RESOLVE-organvm-i-theoria-.github-458
@sourcery-ai

sourcery-ai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors the staggered scheduling GitHub Actions workflow to delegate schedule planning and execution logic to a new reusable Python helper, adds tests, and passes richer metadata (date, stagger interval, default branches) through job outputs to make schedule reuse and completion more robust.

Sequence diagram for staggered scheduling workflow using Python helper

sequenceDiagram
    participant GitHubActions
    participant calculate_schedule as Job_calculate_schedule
    participant script as staggered_walkthrough_schedule_py
    participant execute_schedule as Job_execute_schedule
    participant GitHubAPI

    GitHubActions->>calculate_schedule: start job
    calculate_schedule->>script: staggered_walkthrough_schedule.py plan
    script-->>calculate_schedule: has_schedule, schedule_days, total_repositories
    calculate_schedule->>script: staggered_walkthrough_schedule.py due
    script-->>calculate_schedule: repos_today, schedule_date, stagger_minutes
    calculate_schedule-->>GitHubActions: outputs repos_today, schedule_date, stagger_minutes

    GitHubActions->>execute_schedule: start job (needs.calculate_schedule.outputs)
    execute_schedule->>GitHubAPI: GET /repos/{repo}
    GitHubAPI-->>execute_schedule: { default_branch }
    execute_schedule->>GitHubAPI: POST /repos/{repo}/actions/workflows/generate-walkthrough.yml/dispatches (ref=default_branch)
    execute_schedule-->>GitHubActions: staggered execution using STAGGER_MINUTES

    GitHubActions->>execute_schedule: always() Update schedule status step
    execute_schedule->>script: staggered_walkthrough_schedule.py complete
    script-->>execute_schedule: updated schedule.json
Loading

File-Level Changes

Change Details Files
Replace inline schedule generation in the GitHub Actions workflow with a reusable Python scheduler script and richer job outputs.
  • Expose schedule_date and stagger_minutes as outputs from the calculate-schedule job so they can be consumed by downstream jobs.
  • Replace the inline Python script that generated schedule.json with a call to src/automation/scripts/staggered_walkthrough_schedule.py plan, wiring in repos-per-day, force_reschedule, and today parameters.
  • Make schedule persistence more robust by creating the target directory with mkdir -p on the schedule file’s directory and only committing/pushing when staged changes exist.
  • Change the today-schedule step to call staggered_walkthrough_schedule.py due to compute due repositories and write them to repos_today.txt, then derive repos_today from that file instead of jq queries over the schedule JSON.
  • Ensure default outputs (repos_today=0, schedule_date empty, stagger_minutes=5) are written when no schedule file exists.
.github/workflows/staggered-scheduling.yml
Improve execution of staggered workflows to respect repository default branches and configurable concurrency/staggering parameters.
  • Configure the checkout step in the execute job to fetch only the current ref with a limited fetch depth and explicit token.
  • Parameterize STAGGER_MINUTES and MAX_CONCURRENT workflow execution settings from environment/needs outputs instead of hard-coded constants.
  • Before triggering generate-walkthrough workflows, query each repository’s metadata via the GitHub API to discover its default_branch and construct the workflow dispatch payload using jq.
  • Replace the previous dual-attempt main/master dispatch logic with a single dispatch targeting the discovered default branch and emit success/failure messages per repo.
.github/workflows/staggered-scheduling.yml
Make schedule completion updates date-aware and driven by the new scheduler helper instead of hard-coded ‘today’.
  • Pass the calculated schedule_date from the calculate-schedule job into the execute job via SCHEDULE_DATE env so status updates reflect the actual batch date executed.
  • Skip schedule status updates entirely when no schedule_date was selected.
  • Replace inline jq-based mutation of schedule.json with a call to staggered_walkthrough_schedule.py complete, and update commit messages to reference SCHEDULE_DATE instead of today.
.github/workflows/staggered-scheduling.yml
Introduce a reusable staggered walkthrough scheduler Python utility with CLI subcommands and add unit tests.
  • Add src/automation/scripts/staggered_walkthrough_schedule.py implementing helpers for parsing dates/booleans, reading/writing JSON, generating schedules, selecting due batches, and marking days complete.
  • Implement CLI subcommands plan, due, and complete that integrate with GitHub Actions via GITHUB_OUTPUT and filesystem artifacts.
  • Ensure schedule generation is deterministic by normalizing repositories, sorting by size and name, and encoding stagger_minutes on each day entry.
  • Add unit tests in tests/unit/test_staggered_walkthrough_schedule.py covering schedule generation layout, reuse vs regeneration semantics, due-batch selection behavior, and marking completion for specific dates.
src/automation/scripts/staggered_walkthrough_schedule.py
tests/unit/test_staggered_walkthrough_schedule.py

Possibly linked issues

  • #N/A: PR implements and improves the staggered walkthrough scheduling system used to generate and run the described schedule.

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

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hi @4444J99, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions

Copy link
Copy Markdown
Contributor

Version Control Standards Validation Failed

This pull request does not meet our version control standards.

Common Issues:

  1. Branch Name: Must follow format <lifecycle>/<type>/<component>[/<subcomponent>]

    • Examples:
      • develop/feature/user-authentication
      • production/hotfix/critical-security-fix
      • maintenance/v1.x/security-patches
  2. Commit Messages: Must follow Conventional Commits format

    • Format: <type>(<scope>): <subject>
    • Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
    • Examples:
      • feat(auth): add OAuth2 authentication
      • fix: resolve memory leak
      • docs: update installation guide

Documentation:

Please update your branch name and/or commit messages to follow the standards.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Reviewers Assigned

Reviewers have been automatically assigned based on the CODEOWNERS file.

What's Next:

  • Reviewers will be notified
  • Please respond to any feedback
  • Ensure all CI checks pass
  • Reviews typically completed within 48 hours

Need Help?


Automated reviewer assignment - PR #463

@github-actions

Copy link
Copy Markdown
Contributor

Action Pinning Required

This PR contains GitHub Actions that are not pinned to SHA commits.

Why this matters:
SHA pinning prevents supply chain attacks where a malicious actor could hijack a version tag.

How to fix:

  1. Run python src/automation/scripts/utils/update-action-pins.py
  2. Or manually pin actions using format: action@SHA # ratchet:action@version

Example:

# Before (vulnerable)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # ratchet:actions/checkout@v6.0.2

# After (secure)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # ratchet:actions/checkout@v6.0.2

@github-actions github-actions Bot added github-actions Related to GitHub Actions workflows configuration Configuration changes python labels Jun 19, 2026
@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

💬 Task Catcher Summary

💬 Unresolved discussions - Resolve review threads

📋 Task Overview

Category Count
PR Body Unchecked Tasks 0
PR Body Checked Tasks 0 ✅
Comment Tasks 0
Blocker Items 0 🚨
Suggestions 0 💡
Unresolved Review Threads 11

🎯 Next Steps

  • 💬 Resolve review discussion threads

Options:

  • ✅ Check off tasks as you complete them
  • 📋 Create issues for tasks to handle later: Add create-issues-for-tasks label
  • 🚫 Ignore tasks for merge: Add ignore-task-checks label

Last scanned: 2026-07-19 08:42 UTC
Triggered by: issue_comment

@github-actions

Copy link
Copy Markdown
Contributor

Version Control Standards Validation Failed

This pull request does not meet our version control standards.

Common Issues:

  1. Branch Name: Must follow format <lifecycle>/<type>/<component>[/<subcomponent>]

    • Examples:
      • develop/feature/user-authentication
      • production/hotfix/critical-security-fix
      • maintenance/v1.x/security-patches
  2. Commit Messages: Must follow Conventional Commits format

    • Format: <type>(<scope>): <subject>
    • Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
    • Examples:
      • feat(auth): add OAuth2 authentication
      • fix: resolve memory leak
      • docs: update installation guide

Documentation:

Please update your branch name and/or commit messages to follow the standards.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 I'm sorry @4444J99, but I was unable to process your request. Please see the logs for more details.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results

Test Suite Status
Month 1 Core Workflows ✅ Passed
Month 2 Features ✅ Passed
Month 3 Advanced ✅ Passed
Critical Path ✅ Passed

Overall: ✅ All tests passed!

View detailed test reports

@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 reviewed your changes and they look great!


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.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a new Python script (staggered_walkthrough_schedule.py) and corresponding unit tests to manage staggered walkthrough schedules for repositories. The reviewer's feedback focuses on improving robustness and platform compatibility. Key recommendations include explicitly specifying encoding='utf-8' when reading and writing files, adding defensive type checking for repository inputs, fixing a bug where a 0-minute stagger is incorrectly overridden by the default value, and leveraging argparse built-in type validation to eliminate redundant runtime type casting.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +38 to +41
def load_json(path: Path) -> Any:
"""Load JSON from a path."""
with path.open() as file:
return json.load(file)

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

Specify encoding="utf-8" explicitly when opening files to prevent platform-dependent encoding issues (e.g., on Windows runners where the default encoding might not be UTF-8).

Suggested change
def load_json(path: Path) -> Any:
"""Load JSON from a path."""
with path.open() as file:
return json.load(file)
def load_json(path: Path) -> Any:
"""Load JSON from a path."""
with path.open(encoding="utf-8") as file:
return json.load(file)

Comment on lines +44 to +49
def write_json(path: Path, data: Any) -> None:
"""Write indented JSON to a path."""
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w") as file:
json.dump(data, file, indent=2)
file.write("\n")

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

Specify encoding="utf-8" explicitly when writing JSON files to ensure consistent UTF-8 encoding across all environments.

Suggested change
def write_json(path: Path, data: Any) -> None:
"""Write indented JSON to a path."""
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w") as file:
json.dump(data, file, indent=2)
file.write("\n")
def write_json(path: Path, data: Any) -> None:
"""Write indented JSON to a path."""
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as file:
json.dump(data, file, indent=2)
file.write("\n")

Comment on lines +62 to +66
def normalize_repo(repo: dict[str, Any]) -> dict[str, Any]:
"""Normalize repository metadata used by the scheduler."""
full_name = str(repo.get("full_name") or repo.get("name") or "").strip()
if not full_name:
raise ValueError(f"Repository entry is missing full_name/name: {repo}")

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

Add defensive type checking to ensure repo is a dictionary before calling .get(). This prevents unhandled AttributeError exceptions if invalid repository metadata is passed.

Suggested change
def normalize_repo(repo: dict[str, Any]) -> dict[str, Any]:
"""Normalize repository metadata used by the scheduler."""
full_name = str(repo.get("full_name") or repo.get("name") or "").strip()
if not full_name:
raise ValueError(f"Repository entry is missing full_name/name: {repo}")
def normalize_repo(repo: dict[str, Any]) -> dict[str, Any]:
"""Normalize repository metadata used by the scheduler."""
if not isinstance(repo, dict):
raise ValueError(f"Repository entry must be a dictionary, got {type(repo).__name__}")
full_name = str(repo.get("full_name") or repo.get("name") or "").strip()
if not full_name:
raise ValueError(f"Repository entry is missing full_name/name: {repo}")

Comment on lines +182 to +185
selected_date = min(due_dates).isoformat()
entry = schedule["schedule"][selected_date]
repositories = [str(repo) for repo in entry.get("repositories", [])]
stagger_minutes = int(entry.get("stagger_minutes") or DEFAULT_STAGGER_MINUTES)

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

Using or on entry.get("stagger_minutes") will override a valid stagger time of 0 to the default 5 minutes because 0 is falsy in Python. Check for None explicitly to allow a 0 minute stagger.

Suggested change
selected_date = min(due_dates).isoformat()
entry = schedule["schedule"][selected_date]
repositories = [str(repo) for repo in entry.get("repositories", [])]
stagger_minutes = int(entry.get("stagger_minutes") or DEFAULT_STAGGER_MINUTES)
selected_date = min(due_dates).isoformat()
entry = schedule["schedule"][selected_date]
repositories = [str(repo) for repo in entry.get("repositories", [])]
stagger_val = entry.get("stagger_minutes")
stagger_minutes = int(stagger_val) if stagger_val is not None else DEFAULT_STAGGER_MINUTES

Comment on lines +207 to +214
def write_github_output(values: dict[str, Any]) -> None:
"""Append simple key/value outputs for GitHub Actions."""
output_path = os.getenv("GITHUB_OUTPUT")
if not output_path:
return
with open(output_path, "a") as file:
for key, value in values.items():
file.write(f"{key}={value}\n")

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

Specify encoding="utf-8" when appending to the GitHub Actions output file to prevent encoding issues with special characters.

Suggested change
def write_github_output(values: dict[str, Any]) -> None:
"""Append simple key/value outputs for GitHub Actions."""
output_path = os.getenv("GITHUB_OUTPUT")
if not output_path:
return
with open(output_path, "a") as file:
for key, value in values.items():
file.write(f"{key}={value}\n")
def write_github_output(values: dict[str, Any]) -> None:
"""Append simple key/value outputs for GitHub Actions."""
output_path = os.getenv("GITHUB_OUTPUT")
if not output_path:
return
with open(output_path, "a", encoding="utf-8") as file:
for key, value in values.items():
file.write(f"{key}={value}\n")

Comment on lines +229 to +240
today = parse_iso_date(args.today)
repos_per_day = int(args.repos_per_day)
force = parse_bool(args.force)

schedule, regenerated, reason = plan_schedule(
repositories,
existing_schedule,
repos_per_day,
today,
force=force,
stagger_minutes=int(args.stagger_minutes),
)

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

If argparse is configured to parse --repos-per-day and --stagger-minutes as integers directly, we can remove the redundant int() type casts here.

Suggested change
today = parse_iso_date(args.today)
repos_per_day = int(args.repos_per_day)
force = parse_bool(args.force)
schedule, regenerated, reason = plan_schedule(
repositories,
existing_schedule,
repos_per_day,
today,
force=force,
stagger_minutes=int(args.stagger_minutes),
)
today = parse_iso_date(args.today)
repos_per_day = args.repos_per_day
force = parse_bool(args.force)
schedule, regenerated, reason = plan_schedule(
repositories,
existing_schedule,
repos_per_day,
today,
force=force,
stagger_minutes=args.stagger_minutes,
)

Comment on lines +305 to +309
plan.add_argument("--output", required=True)
plan.add_argument("--repos-per-day", required=True)
plan.add_argument("--today")
plan.add_argument("--force", default="false")
plan.add_argument("--stagger-minutes", default=str(DEFAULT_STAGGER_MINUTES))

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

Leverage argparse built-in type validation by specifying type=int for --repos-per-day and --stagger-minutes. This provides automatic validation and user-friendly error messages on invalid inputs instead of throwing tracebacks at runtime.

Suggested change
plan.add_argument("--output", required=True)
plan.add_argument("--repos-per-day", required=True)
plan.add_argument("--today")
plan.add_argument("--force", default="false")
plan.add_argument("--stagger-minutes", default=str(DEFAULT_STAGGER_MINUTES))
plan.add_argument("--output", required=True)
plan.add_argument("--repos-per-day", type=int, required=True)
plan.add_argument("--today")
plan.add_argument("--force", default="false")
plan.add_argument("--stagger-minutes", type=int, default=DEFAULT_STAGGER_MINUTES)

@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown

Code Review — PR #463

This PR cleanly extracts the inline schedule logic into a dedicated Python module and adds a meaningful test suite. The overall direction is good. Below are the issues I found, ordered by severity.


Bugs / Regressions

1. env.REPOS_PER_DAY fallback will break if the variable is not defined at workflow level

# new
REPOSITORIES_PER_DAY: ${{ github.event.inputs.repositories_per_day || env.REPOS_PER_DAY }}
# old
REPOSITORIES_PER_DAY: ${{ github.event.inputs.repositories_per_day || '10' }}

If env.REPOS_PER_DAY is not defined (either as a workflow-level env: key or a repo variable expansion), this evaluates to an empty string. The Python script then calls int('') which raises ValueError and fails the step. The safe fix is to keep a final literal fallback:

REPOSITORIES_PER_DAY: ${{ github.event.inputs.repositories_per_day || env.REPOS_PER_DAY || '10' }}

2. Unquoted ${{ env.SCHEDULE_FILE }} in "Update schedule status"

git add ${{ env.SCHEDULE_FILE }}   # ← missing quotes

The surrounding git commit and git push lines (carried over from the original) also lack quotes. If the schedule file path ever contains spaces this will break. The "Save schedule" step correctly uses "${{ env.SCHEDULE_FILE }}" — apply the same treatment here.


Security Concerns

3. stagger_minutes piped from Python output into shell arithmetic

STAGGER_MINUTES: ${{ needs.calculate-schedule.outputs.stagger_minutes || '5' }}
…
sleep $((STAGGER_MINUTES * 60))

stagger_minutes is written to $GITHUB_OUTPUT by Python as int(entry.get(\"stagger_minutes\") or DEFAULT_STAGGER_MINUTES), so it will always be a decimal integer. The risk is low, but it is good practice to validate it in the shell before the arithmetic expansion:

# guard in execute_schedule.sh
[[ "$STAGGER_MINUTES" =~ ^[0-9]+$ ]] || STAGGER_MINUTES=5

Code Quality

4. command_due writes an empty file with no newline when there are no repos

repos_output.write_text("\n".join(repositories) + ("\n" if repositories else ""))

wc -l on an empty file returns 0, so the downstream REPOS_TODAY check is correct. However, the inconsistency (trailing newline only when non-empty) is easy to misread. A cleaner idiom:

repos_output.write_text("".join(repo + "\n" for repo in repositories))

This is a nit — not a functional bug.


5. has_schedule output is always "true" even for an empty repository list

command_plan unconditionally emits has_schedule=true regardless of whether total_repositories == 0. Downstream jobs gated on has_schedule will still run even when there is nothing to schedule. The old inline script had the same issue, but since the behaviour is now encoded in Python it is a good time to fix it:

"has_schedule": "true" if schedule.get("total_repositories", 0) > 0 else "false",

Test Coverage

The five unit tests cover the happy paths well. A few edge cases worth adding:

  • generate_schedule with an empty repository list (currently days_needed=0 and the schedule dict is empty — confirm this does not error).
  • select_due_batch when today is before all pending dates (should return None, [], 5).
  • plan_schedule with force=True when an active schedule already exists (should regenerate regardless).
  • mark_completed called with a scheduled_date that does not exist in the schedule (should return False).

Minor

  • The meta JSON version stays at "1.0.0" while the schedule format bumped to "1.1". Consider aligning to avoid confusion.
  • normalize_repo falls back default_branch to "main", but the improved workflow now fetches the actual default branch from the GitHub API. These two sources of truth could diverge if the schedule was generated before a repo renamed its default branch. Not a blocking issue, but worth a comment.

Overall: the refactor improves correctness and testability. Fix items 1 and 2 before merging — the REPOS_PER_DAY regression can silently break the scheduling job on workflow_dispatch runs that omit repositories_per_day.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@4444J99, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 53 minutes. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 95fba86a-43f5-4b86-a02c-c1f4d720b55a

📥 Commits

Reviewing files that changed from the base of the PR and between 53348cf and ba28927.

📒 Files selected for processing (4)
  • .github/workflows/staggered-scheduling.yml
  • .github/workflows/staggered-scheduling.yml.meta.json
  • src/automation/scripts/staggered_walkthrough_schedule.py
  • tests/unit/test_staggered_walkthrough_schedule.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch limen/resolve-organvm-i-theoria-.github-458-f825

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 and usage tips.

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

AI Code Review by LlamaPReview

🎯 TL;DR & Recommendation

Recommendation: Request Changes

This PR introduces a reusable Python-based staggered schedule planner, but contains critical regressions: it fails to detect when the repository list or configuration changes, causing new repositories to be missed and schedule drift to go unnoticed.

📄 Documentation Diagram

This diagram documents the refactored staggered walkthrough scheduling workflow.

sequenceDiagram
    participant W as Workflow
    participant RL as Repos List
    participant SS as Schedule Script
    participant GR as Git Repo

    W->>RL: fetch active repos
    RL-->>W: repositories.json
    W->>SS: plan schedule (force, repos_per_day)
    SS->>SS: decide reuse or regenerate
    note over SS: PR #35;463: New script reuse active schedule if pending batches exist
    SS-->>W: schedule.json
    W->>GR: save & commit schedule
    W->>SS: check today's due
    SS-->>W: due repos (repos_today.txt)
    W->>GR: trigger workflow per repo<br/>with stagger & concurrency
    W->>SS: mark date complete
    SS-->>W: completed schedule
Loading

🌟 Strengths

  • Solid test coverage for core logic, ensuring reliability.
  • Modular design with clear separation of concerns.
Priority File Category Impact Summary Anchors
P1 src/.../staggered_walkthrough_schedule.py Bug Misses detection of repository or config changes method:plan_schedule
P1 src/.../staggered_walkthrough_schedule.py Bug Ignores change in repos_per_day on reuse method:command_plan
P2 tests/.../test_staggered_walkthrough_schedule.py Testing No test for repo list change regeneration
P2 .github/workflows/staggered-scheduling.yml Documentation Variable names not updated in docs path:docs/guides/...
P2 src/.../staggered_walkthrough_schedule.py Testing Missing edge case coverage

🔍 Notable Themes

  • Config drift blindness: Both P1 findings stem from the same root cause: the schedule reuse code does not compare the current configuration (repos list, repos_per_day) against the stored schedule. This systemic risk could cause extended scheduling gaps. Consider adding a fingerprinting approach to detect drifts automatically.

📈 Risk Diagram

This diagram illustrates the stale schedule reuse risk and its potential impact.

sequenceDiagram
    participant U as User
    participant W as Workflow
    participant SS as Schedule Script
    participant R as Repository List

    U->>W: trigger (new repos / changed config)
    W->>R: fetch current repos (updated)
    R-->>W: repositories.json (changed)
    W->>SS: plan schedule (new repos, new repos_per_day)
    SS->>SS: check existing schedule (no config comparison)
    SS-->>W: return old schedule (stale)
    W->>W: execute old schedule (misses new repos)
    note over SS: R1(P1): Config drift not detected<br/>R2(P1): repos_per_day change ignored
Loading
⚠️ **Unanchored Suggestions (Manual Review Recommended)**

The following suggestions could not be precisely anchored to a specific line in the diff. This can happen if the code is outside the changed lines, has been significantly refactored, or if the suggestion is a general observation. Please review them carefully in the context of the full file.


📁 File: src/automation/scripts/staggered_walkthrough_schedule.py

The reuse logic in plan_schedule does not validate that the existing schedule’s repos_per_day value matches the current repos_per_day parameter. If a workflow user changes the REPOSITORIES_PER_DAY input (or the default env variable), the reused schedule will still use the old number of repositories per day, leading to an inconsistent schedule. This is a deterministic bug path when the config changes while pending batches exist.

Suggestion:

if existing_schedule and pending_schedule_dates(existing_schedule):
    stored_repos_per_day = existing_schedule.get("repos_per_day")
    if stored_repos_per_day is not None and stored_repos_per_day != repos_per_day:
        new_schedule = generate_schedule(...)
        return new_schedule, True, "repos_per_day changed"
    return existing_schedule, False, "active schedule has pending batches"

Related Code:

def plan_schedule(
    repositories: list[dict[str, Any]],
    existing_schedule: dict[str, Any] | None,
    repos_per_day: int,
    ...
) -> tuple[dict[str, Any], bool, str]:
    if existing_schedule and pending_schedule_dates(existing_schedule):
        return existing_schedule, False, "active schedule has pending batches"


💡 Have feedback? We'd love to hear it in our GitHub Discussions.
✨ This review was generated by LlamaPReview Advanced, which is free for all open-source projects. Learn more.

return sorted(pending_dates)


def plan_schedule(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 | Confidence: High

The plan_schedule function reuses an existing schedule when it has pending batches, without verifying that the set of repositories or repos_per_day configuration has changed. In the old workflow, the schedule was always regenerated from the current repository list. This change introduces a regression: if a new repository is added or an existing one is removed, the reused schedule will not reflect the updated list. Similarly, if a user changes the REPOSITORIES_PER_DAY input, the old schedule (with a different repos_per_day) will be incorrectly reused. This could cause new repositories to be missed from scheduling for an extended period (until all pending batches are completed), which is a real data-flow risk given that repository discovery runs in the same workflow before plan_schedule is called.

Code Suggestion:

def _schedule_fingerprint(repositories: list[dict], repos_per_day: int, stagger_minutes: int) -> str:
    import hashlib
    key = (json.dumps([r.get("full_name") for r in repositories], sort_keys=True),
           repos_per_day, stagger_minutes)
    return hashlib.sha256(repr(key).encode()).hexdigest()

def plan_schedule(...) -> tuple[dict[str, Any], bool, str]:
    if force:
        new_schedule, _, _ = generate_schedule(...)
        return new_schedule, True, "forced"

    if existing_schedule:
        stale = existing_schedule.get("_fingerprint") != _schedule_fingerprint(repositories, repos_per_day, stagger_minutes)
        if stale or not pending_schedule_dates(existing_schedule):
            new_schedule, _, _ = generate_schedule(...)
            new_schedule["_fingerprint"] = _schedule_fingerprint(repositories, repos_per_day, stagger_minutes)
            return new_schedule, True, "regenerated due to config change" if stale else "no pending batches"
        return existing_schedule, False, "active schedule has pending batches"

    new_schedule, _, _ = generate_schedule(...)
    new_schedule["_fingerprint"] = _schedule_fingerprint(repositories, repos_per_day, stagger_minutes)
    return new_schedule, True, "schedule file missing"

Evidence: method:plan_schedule

assert schedule["schedule"]["2026-06-02"]["repositories"] == ["organvm/large-app"]


@pytest.mark.unit

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 | Confidence: High

The test suite does not include any scenario where the repository list changes between schedule generations. This means the bug described in the first finding (missing detection of repo list changes) will not be caught by the current tests. Adding a test where the repositories argument contains different repos than the existing schedule would demonstrate the gap.

Code Suggestion:

def test_plan_schedule_regenerates_when_repositories_change(repositories):
    existing = generate_schedule(repositories, repos_per_day=2, start_date=date(2026, 6, 1))
    changed_repos = repositories + [{"name": "new-app", "full_name": "organvm/new-app", "size": 10}]
    planned, regenerated, _ = plan_schedule(changed_repos, existing, repos_per_day=2, today=date(2026, 6, 2))
    assert regenerated is True
    assert "organvm/new-app" in [repo for day in planned["schedule"].values() for repo in day["repositories"]]

Comment on lines +128 to +131
def pending_schedule_dates(schedule: dict[str, Any]) -> list[date]:
"""Return pending schedule dates in ascending order."""
entries = schedule.get("schedule") or {}
if not isinstance(entries, dict):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 | Confidence: High

The test file covers the main happy paths for generate_schedule, plan_schedule, select_due_batch, and mark_completed, but it does not test edge cases such as: empty repository list, select_due_batch when today exactly matches a scheduled date, select_due_batch when there are no pending dates, mark_completed with a non-existent date, and plan_schedule with force=True. These gaps reduce confidence in robustness.

echo "repo_count=$REPO_COUNT" >> $GITHUB_OUTPUT

- name: Generate staggered schedule
- name: Plan staggered schedule

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 | Confidence: Medium

The documentation guide docs/guides/AUTOMATION_GUIDE.md mentions configuration options including repos_per_day, but the new workflow uses REPOSITORIES_PER_DAY as the environment variable name and REPOS_PER_DAY as the fallback env. The guide is not updated to reflect the new variable name or the new --force input. This is a maintainability concern.

@4444J99

4444J99 commented Jul 19, 2026

Copy link
Copy Markdown
Member Author

Backlog engagement 2026-07-19 — disposition: superseded by current main.

Verified live state: CONFLICTING/DIRTY with 12 failing checks. Current main already contains src/automation/scripts/staggered_walkthrough_schedule.py and tests/unit/test_staggered_walkthrough_schedule.py, so this older resolver branch is superseded and should not be merged while red/conflicting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

configuration Configuration changes github-actions Related to GitHub Actions workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant