Skip to content

fix(OMN-15127): nightly-promote scheduled runs default to REAL path, fail-loud on block - #4846

Merged
jonahgabriel merged 2 commits into
devfrom
jonah/omn-15127-nightly-promote-dry-run-fix
Jul 25, 2026
Merged

jonahgabriel merged 2 commits into
devfrom
jonah/omn-15127-nightly-promote-dry-run-fix

Conversation

@jonahgabriel

Copy link
Copy Markdown
Contributor

OMN-15127: nightly-promote.yml is a structural no-op — fix DRY_RUN + repo-scope + fail-loud

Child of epic OMN-15067 (dev→main promotion systemically broken fleet-wide). Fixes scope item 1 named in OMN-15067.

Root cause (quoted lines, before this PR)

env:
  DRY_RUN: ${{ github.event.inputs.dry_run || 'true' }}
  REPOS_INPUT: ${{ github.event.inputs.repos || 'omniweb' }}

github.event.inputs is empty on schedule triggers (it only exists on workflow_dispatch). So on every scheduled run, github.event.inputs.dry_run evaluates to empty string, the || fires, and DRY_RUN resolves to the literal 'true' — unconditionally, by construction, regardless of any repo variable, branch protection, or operator intent. Separately, REPOS_INPUT defaulted to omniweb only, so even a real (non-dry-run, workflow_dispatch-triggered) run would only ever promote one repo of the 12-repo fleet.

Live evidence gathered this pass

  • gh run list --workflow=nightly-promote.yml --limit 25 (2026-07-01 → 2026-07-25): 25 scheduled runs, 23 success, 1 cancelled (07-07), 1 failure (07-25, most recent) — i.e. the "21+ green" claim in OMN-15067/OMN-15127 is confirmed and, if anything, undercounted.
  • gh search prs "Evidence-Ticket: OMN-11732" across the OmniNode-ai org returns exactly 2 PRs, both the original workflow-authoring PRs from 2026-05-23 (feat(OMN-11732): add nightly promotion workflow #1405, feat(OMN-11732): nightly promotion workflow MVP #1435 on onex_change_control itself) — zero promotion PRs matching the workflow's own generated title pattern (promotion($repo): promote dev to main for ...) or trailer exist anywhere. 61+ days of scheduled "success" runs, zero output.

Fix

  1. DRY_RUN is now reachable only via an explicit workflow_dispatch input (github.event_name == 'workflow_dispatch' && (github.event.inputs.dry_run || 'true')). All other trigger types (i.e. schedule) fall through to vars.NIGHTLY_PROMOTE_FORCE_DRY_RUN || 'false' — scheduled runs now default to the real promotion path. An operator retains an explicit, auditable kill switch (the repo variable) without needing another workflow-file edit.
  2. REPOS_INPUT default now covers the Tier-2 fleet from the OMN-15067 census (omnimarket,omnibase_infra,omnidash,omninode_infra,omniintelligence,omnimemory,omniweb,omnibase_spi,omnibase_compat) instead of omniweb-only — satisfying AC Add Claude Code GitHub Workflow #2. Tier-1 repos (omnibase_core, omniclaude, onex_change_control) are excluded from the default per OMN-15067 scope item 4: the "Capture cross-repo verification placeholder" step in this same workflow is still a hardcoded not_run_in_mvp stub, not a real cross-repo check, so Tier-1 fleet-wide-CI-provider repos stay on manual promotion until that lands (tracked separately under OMN-15067 scope item 2). Override via workflow_dispatch repos input or the NIGHTLY_PROMOTE_REPOS repo variable.
  3. Fail-closed and loud: in the "Plan or create promotion PRs" step, the real (non-dry-run) path previously did echo "Promotion gates are blocked..."; exit 0 when promotable != true — a genuinely green run that silently created nothing. That branch now emits ::error:: and exit 1, so a real run that cannot promote goes red, not green-no-op. This is the entire point of the defect class this ticket is closing: a control that reports success while doing nothing.

Acceptance criteria mapping (OMN-15127)

  • Scheduled runs no longer resolve DRY_RUN='true' by construction — diff above; live run-log readback of the resolved DRY_RUN value on the first post-merge scheduled run is the outstanding proof, see below.
  • Default repo list covers the intended fleet (Tier-2), not omniweb-only — diff above; live REPOS_INPUT readback also pending the first post-merge scheduled run.
  • A SCHEDULED run opens at least one real promotion PR — this is the live, post-merge proof surface (§4a X2). Cannot be produced by this PR (no scheduled trigger fires pre-merge); the first real 0 6 * * * run after merge is the proof. If Tier-2 repos are already at dev==main parity that night, the run must show a legitimate no changes to promote result on the real path (not a blocked-gate no-op) — the fail-loud change in point 3 above guarantees a blocked gate cannot masquerade as that outcome.
  • Tier-1 safety respected — Tier-1 repos excluded from the automated default (branch not: "replace stub with real check", since that is separately scoped as OMN-15067 item 2 and out of scope for this fix).

Verification performed

  • python3 -c "import yaml; yaml.safe_load(...)" — YAML parses.
  • actionlint .github/workflows/nightly-promote.yml — clean (no findings).
  • uv run pre-commit run --files .github/workflows/nightly-promote.yml (on .200) — all applicable hooks Passed.
  • Pushed from .200 (stickybeatz-studio) per repo convention.

Follow-up (not this PR)

  • First real scheduled run after merge (0 6 * * *) is the live proof this ticket's AC Add Claude Code GitHub Workflow #3 requires — a real promotion PR opened, or an honest failed/red run citing the blocking gate. Report that run id + outcome back to OMN-15127 once it fires.
  • OMN-15067 scope item 2 (replace the not_run_in_mvp cross-repo stub with a real check) remains open and is what continues to gate Tier-1 repos out of the automated default here.

Evidence-Ticket: OMN-15127

…fail-loud on block

github.event.inputs is empty on schedule triggers, so DRY_RUN unconditionally resolved to true on every scheduled run (21+ green runs, zero promotion PRs ever). DRY_RUN now honors an explicit workflow_dispatch input only; scheduled runs default to the real promotion path via a repo-variable override (NIGHTLY_PROMOTE_FORCE_DRY_RUN) for an explicit escape hatch. REPOS_INPUT default no longer omniweb-only; covers the Tier-2 fleet from the OMN-15067 census, excluding Tier-1 (omnibase_core/omniclaude/onex_change_control) per OMN-15067 scope item 4 since the cross-repo verification step is still a not_run_in_mvp stub. The real-path blocked-gate branch now exits 1 instead of 0 so a run that cannot promote goes red instead of reporting a silent no-op success.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 4 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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

How do review limits work?

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

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, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 05caaa27-2f5a-459c-bf20-8938e5b654ff

📥 Commits

Reviewing files that changed from the base of the PR and between bc7f79b and 9fc1bd4.

📒 Files selected for processing (5)
  • .github/workflows/nightly-promote.yml
  • contracts/OMN-15127.yaml
  • drift/dod_receipts/OMN-15127/dod-onex-change-control-pr-4846-actionlint/command.yaml
  • drift/dod_receipts/OMN-15127/dod-onex-change-control-pr-4846-workflow-diff/command.yaml
  • drift/dod_receipts/OMN-15127/occ-self-bind-pr-4846/command.yaml
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jonah/omn-15127-nightly-promote-dry-run-fix

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

@jonahgabriel
jonahgabriel force-pushed the jonah/omn-15127-nightly-promote-dry-run-fix branch from 7375221 to 9fc1bd4 Compare July 25, 2026 18:41
@jonahgabriel
jonahgabriel merged commit f60f32f into dev Jul 25, 2026
55 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-15127-nightly-promote-dry-run-fix branch July 25, 2026 18:46
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