Skip to content

Fix/148, 181 resolution and leaderboard sets to one commit - #248

Open
oskariluoma wants to merge 2 commits into
forecastingresearch:mainfrom
oskariluoma:fix/148-resolution-sets-one-commit
Open

Fix/148, 181 resolution and leaderboard sets to one commit#248
oskariluoma wants to merge 2 commits into
forecastingresearch:mainfrom
oskariluoma:fix/148-resolution-sets-one-commit

Conversation

@oskariluoma

@oskariluoma oskariluoma commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

src/orchestration/_io.py — upload_resolution_set now only uploads to the bucket (no git push). Added push_all_resolution_sets() that gathers all resolution sets from the bucket and pushes them in one commit.
src/orchestration/func_push_resolution_sets/ — new Cloud Run job (func-push-resolution-sets) that runs push_all_resolution_sets() as a single task.
src/nightly_update_workflow/manager/main.py — runs the push job after resolve finishes, before leaderboards.
src/nightly_update_workflow/worker/main.py — registers the new job.
Makefile — adds the push-resolution-sets target and wires it into resolve.
src/helpers/git.py — clone_and_push_files skips empty commits and returns whether it pushed (so no false "pushed" log when nothing changed).

Summary by CodeRabbit

  • New Features

    • Added a new workflow step to push resolution sets after forecasts are resolved, before downstream updates run.
    • Introduced a job that collects all available resolution sets and publishes them in a single commit, helping avoid push conflicts.
    • Added deployment support for the new job so it can run in the cloud like existing workflow tasks.
  • Bug Fixes

    • Skips creating empty commits when there are no file changes, while still allowing remote mirrors to catch up.
    • Improves error reporting for push failures.

@oskariluoma oskariluoma reopened this Jun 30, 2026
@oskariluoma
oskariluoma requested review from houtanb and removed request for houtanb June 30, 2026 10:14
@oskariluoma
oskariluoma marked this pull request as ready for review June 30, 2026 12:56
@oskariluoma
oskariluoma requested a review from houtanb June 30, 2026 12:57
@houtanb
houtanb requested a review from nikbpetrov July 6, 2026 14:28
Comment thread src/helpers/git.py Outdated
Comment thread src/orchestration/_io.py Outdated
Comment thread src/orchestration/func_push_resolution_sets/requirements.txt Outdated
@nikbpetrov

Copy link
Copy Markdown
Collaborator

Apart from these minor notes, LGTM

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a dedicated push-resolution-sets pipeline step. clone_and_push_files now returns a boolean indicating whether a commit was made, always pushing origin/mirrors. A new push_all_resolution_sets() function collects and pushes all GCS resolution sets in one commit via a new Cloud Run job, wired into the nightly workflow and Makefile.

Changes

Push Resolution Sets Feature

Layer / File(s) Summary
Git push helper returns commit status
src/helpers/git.py
clone_and_push_files detects existing changes, conditionally commits, always pushes to origin and mirrors with explicit error raising, and returns a bool; clone_commit_and_push docstring updated.
push_all_resolution_sets orchestration function
src/orchestration/_io.py
upload_resolution_set docstring clarified as GCS-only; new push_all_resolution_sets() lists, downloads, and pushes all resolution set files to git in a single commit.
New func_push_resolution_sets Cloud Run job
src/orchestration/func_push_resolution_sets/main.py, .../Makefile, .../requirements.txt
New Cloud Run job entrypoint invoking push_all_resolution_sets, deployment Makefile with all/deploy/clean targets, and pinned dependencies.
Nightly workflow and Makefile wiring
src/nightly_update_workflow/worker/main.py, src/nightly_update_workflow/manager/main.py, Makefile
Worker adds push_resolution_sets job group and dispatch entry; manager blocks on resolve_forecasts then runs and blocks on push_resolution_sets; root Makefile adds push-resolution-sets target wired into resolve.

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

Sequence Diagram(s)

sequenceDiagram
  participant Manager as Nightly Manager
  participant Worker
  participant CloudRunJob as func-push-resolution-sets
  participant IO as orchestration._io
  participant Git as git helper

  Manager->>Worker: call_worker(resolve_forecasts)
  Worker-->>Manager: block_and_check_job_result
  Manager->>Worker: call_worker(push_resolution_sets)
  Worker->>CloudRunJob: run func-push-resolution-sets
  CloudRunJob->>IO: push_all_resolution_sets()
  IO->>IO: list & download GCS resolution sets
  IO->>Git: clone_and_push_files(files, message)
  Git->>Git: detect changes, commit if needed
  Git->>Git: push to origin and mirrors
  Git-->>IO: return has_changes (bool)
  IO-->>CloudRunJob: log pushed count
  CloudRunJob-->>Worker: job complete
  Worker-->>Manager: block_and_check_job_result
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is related to the main change: batching resolution sets into a single commit, even though the phrasing is terse.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

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

🧹 Nitpick comments (1)
src/orchestration/_io.py (1)

264-308: 🚀 Performance & Scalability | 🔵 Trivial

Full resync of all historical resolution sets on every run.

push_all_resolution_sets() lists, downloads, and restages every resolution-set file under the prefix on each invocation, not just newly uploaded ones. This is presumably intentional (self-healing if a prior push/mirror failed), but as the corpus grows this means unbounded, ever-increasing GCS I/O and git index work per nightly run for what is typically a single new file.

Worth keeping an eye on job duration/cost over time; if it becomes a bottleneck, consider only fetching files newer than the last successful push (e.g. tracked via a marker/manifest) while still falling back to a full resync periodically for recovery.

🤖 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/orchestration/_io.py` around lines 264 - 308,
`push_all_resolution_sets()` currently re-downloads and re-commits every
resolution set on each run, which will keep increasing GCS and git work as the
dataset grows. Update this flow to avoid a full resync on every invocation by
tracking the last successful push (for example with a marker or manifest) and
only fetching/restaging newer files, while preserving a periodic or fallback
full resync for recovery. Use the existing `push_all_resolution_sets`,
`gcp.storage.list_with_prefix`, and `git.clone_and_push_files` flow as the place
to implement the incremental behavior.
🤖 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.

Nitpick comments:
In `@src/orchestration/_io.py`:
- Around line 264-308: `push_all_resolution_sets()` currently re-downloads and
re-commits every resolution set on each run, which will keep increasing GCS and
git work as the dataset grows. Update this flow to avoid a full resync on every
invocation by tracking the last successful push (for example with a marker or
manifest) and only fetching/restaging newer files, while preserving a periodic
or fallback full resync for recovery. Use the existing
`push_all_resolution_sets`, `gcp.storage.list_with_prefix`, and
`git.clone_and_push_files` flow as the place to implement the incremental
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e273aa85-329e-48ac-b0bb-5d16bd97c11d

📥 Commits

Reviewing files that changed from the base of the PR and between 86a399d and cc51475.

📒 Files selected for processing (8)
  • Makefile
  • src/helpers/git.py
  • src/nightly_update_workflow/manager/main.py
  • src/nightly_update_workflow/worker/main.py
  • src/orchestration/_io.py
  • src/orchestration/func_push_resolution_sets/Makefile
  • src/orchestration/func_push_resolution_sets/main.py
  • src/orchestration/func_push_resolution_sets/requirements.txt

@oskariluoma
oskariluoma force-pushed the fix/148-resolution-sets-one-commit branch from cc51475 to 1a524c8 Compare August 25, 2026 08:36
Comment thread src/helpers/git.py Outdated
Comment thread src/nightly_update_workflow/manager/main.py Outdated
Comment thread src/orchestration/func_push_resolution_sets/requirements.txt Outdated
@oskariluoma

Copy link
Copy Markdown
Collaborator Author

I suggest merging the PR that solves #181 before this one. I'll have the PR up soon. It will bundle up the leaderboard pushes into a single commit. But at the moment the three leaderboard jobs run in parallel and their pushes can reject each other. The raise_if_error() added here would fail the nightly run.

@houtanb

houtanb commented Aug 28, 2026

Copy link
Copy Markdown
Member

Hmm, I don't see why. If that one goes after this one, you'd just add a few more files to push in the commit right? One overall commit containing resolution files, leaderboards, parity dates, and question difficulty per night? Instead of func_push_resolution_sets you'd rename it to be more general func-push-datasets-to-git. I think that should come after this or be folded into this if you want. Happy to discuss on a call or Slack, in case I'm missing something in what you see

@oskariluoma
oskariluoma force-pushed the fix/148-resolution-sets-one-commit branch 2 times, most recently from 6cec2fe to 2ae1894 Compare August 31, 2026 13:01
@oskariluoma oskariluoma changed the title Fix/148 resolution sets one commit Fix/148, 181 resolution and leaderboard sets to one commit Aug 31, 2026
Comment thread src/helpers/git.py Outdated
Comment thread src/helpers/git.py Outdated
@nikbpetrov

Copy link
Copy Markdown
Collaborator

Fine PR, solves the problem it sets out but I catch myself asking if these really are problems we should be solving at all, @houtanb .

The whole GCP-tied architecture remains strange to me, tests are still lacking and I am nervous about every line. There's a ton of assumptions built on top of each other, and all I want to do is just point the code to some folder (on GCS or local) and have all the code just work from whatever state the files are in - each stage must encode exactly what it expects, and test must be the encoding of these expectations.

This is obviously not the case here as every stage is very finnicky, local runs/test etc are borderline unrunnable etc. We've talked about this before but I feel like we're doing a lot of patching these days. Not immediately actionable any of this I guess as such a refactor would be significant amount of work*. This codebase currently is worthwhile because it's been battle-tested a decent bit, which is impressive.

*although, tbh, as long as we get the tests right, agents can probably execute the majority of it and shadow parity runs/code review over the course of a few weeks can verify correctness/architecture --- even if minor bugs slip through after all that, at least will be debugging/patching something more robust.

@houtanb

houtanb commented Aug 31, 2026

Copy link
Copy Markdown
Member

@nikbpetrov indeed that's why you're working on the refactor and a test suite!

It takes time because we're doing this on a public-facing system so we're going slowly to be confident in changes, but the end goal, depending on tradeoffs, is a more reliable system that will allow us to move more quickly. All ideas are welcome.

The resolve and leaderboard jobs run in parallel and each pushed its own files to the dataset
repo, racing on the remote. They now only write to the bucket; a new `func-push-datasets-to-git`
job collects everything and pushes it in a single commit at the end of the nightly run.

Parity dates and question fixed effects were never in the dataset repo at all. They now land in
`datasets/parity_dates/` and `datasets/question_fixed_effects/`. The resolution sets and the
leaderboard html & csv keep their existing paths.

The leaderboard wrote a new dated question-fixed-effects file every night and the website served
the whole pile. It now writes one file per leaderboard, overwritten nightly, and the history is
tracked in the dataset repo instead, so the website page is replaced by a link to the repo from
the datasets page.

`clone_and_push_files` now skips empty commits, surfaces a push to origin that the remote
rejected (GitPython does not raise on this) and tolerates a failing mirror push.

Closes forecastingresearch#181
Closes forecastingresearch#148
… the SSH key is unset

A failed mirror push was only logged. It now also sends a Slack message.

`clone()` returns None when `API_GITHUB_SSH_ID_RSA` is not set, so the tuple unpack in
`clone_and_push_files` raised `TypeError` instead of the intended soft skip. It now returns early, before cloning.

`helpers/git.py` now imports `helpers/slack.py`, so `slack_sdk` is added to the requirements of the three deployments that use `helpers.git` and were missing it.
@houtanb
houtanb force-pushed the fix/148-resolution-sets-one-commit branch from 25ab688 to 25689ac Compare September 1, 2026 08:14
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.

3 participants