Skip to content

fix(release): escalate a publish that fails on consecutive commits instead of warning into the void - #9965

Merged
JSONbored merged 1 commit into
mainfrom
fix/publish-outage-escalation
Jul 30, 2026
Merged

fix(release): escalate a publish that fails on consecutive commits instead of warning into the void#9965
JSONbored merged 1 commit into
mainfrom
fix/publish-outage-escalation

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Closes #9951

The gap

The retry helper’s own comment already drew the right distinction:

"Retrying after a short wait resolves it once propagation catches up — this is NOT the same failure class as a genuine test regression, which would fail identically on every retry."

Nothing acted on it. Both classes got the same three retries, and on exhaustion the same ::warning:: … left for manual follow-up — a signal nobody reads.

The consequence was #9946: the MCP and miner publishes failed on every main commit for as far back as the run history went (a one-line missing build step), retrying three times per push and never once succeeding. The only evidence was a red check that reads as release noise, so neither package shipped for dozens of commits.

The fix

A publish failing on consecutive commits is not a flake being retried, it is a standing outage. The escalation counts failures at the head of the workflow’s own run history — index("success") is the number of leading non-successes — and past three, files a tracking issue naming the streak.

It escalates once per outage: an open issue for the same workflow suppresses the next filing, because an alert that repeats per attempt is the same unread noise in a different place. It re-files only after a success resets the streak.

Wired into all three give-up sites (publish-mcp, publish-miner, publish-ui-kit); issues: write added for it.

Verified, not assumed

The detection query was run against the live API for all four publish workflows (all currently 0 — the #9947/#9950 fixes landed and both previously-failing publishes now show success at the head), and against synthetic histories for every arm:

history leading failures
all failure (the #9946 shape) 4
3 failures then success 3
2 failures then success 2 (below threshold, stays quiet)
healthy head 0
empty history 0 (not a false alarm)

actionlint clean, including a shellcheck directive for the jq $c binding that genuinely must stay single-quoted.

…stead of warning into the void

The retry helper's own comment already drew the right distinction:

  "Retrying after a short wait resolves it once propagation catches up -- this
   is NOT the same failure class as a genuine test regression, which would fail
   identically on every retry."

Nothing acted on it. Both classes got the same three retries, and on exhaustion
the same `::warning:: ... left for manual follow-up`, which is a signal nobody
reads. The MCP and miner publishes then failed on EVERY main commit for as far
back as the run history went (#9946, a one-line missing build step), retrying
three times per push and never once succeeding, while the only evidence was a
red check that reads as release noise.

A publish failing on CONSECUTIVE commits is not a flake being retried, it is a
standing outage. This counts the failures at the head of the workflow's own run
history (`index("success")` = leading non-successes) and, past three, files a
tracking issue naming the streak.

Escalates ONCE per outage: an open issue for the same workflow suppresses the
next filing, because an alert that repeats per attempt is the same unread noise
in a different place. It re-files only after a success has reset the streak.

The jq was verified against the live API and against synthetic histories for
every arm: the all-failure outage shape (4), a streak ending in success (3),
a below-threshold streak (2), a healthy head (0), and empty history (0, not a
false alarm).
@loopover-orb

loopover-orb Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Warning

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-30 22:23:53 UTC

1 file · 1 AI reviewer · no blockers · CI green · clean

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review: This PR changes guardrail-protected path(s): .github/workflows/mcp-release-please.yml (matched .github/workflows/**).

Review summary
This adds `escalate_persistent_publish_failure()` to `.github/workflows/mcp-release-please.yml`'s `reconcile-stale-releases` job, which counts leading non-success runs in a publish workflow's own Actions run history and files a dedup'd tracking issue once that streak hits 3, replacing a silent `::warning::` on retry exhaustion for `publish-mcp.yml`, `publish-miner.yml`, and `publish-ui-kit.yml`. The premise — that the existing retry helper conflates transient propagation lag with a genuine standing outage — is accurate and the #9946 incident is a real motivating case, but the implementation's data source (the workflow's raw run history) doesn't actually measure 'consecutive commits' the way the PR claims, since `dispatch_and_wait_with_retry` itself manufactures 3 of those history entries per single failing push for exactly the two packages (mcp, miner) the PR is built around.

Nits — 8 non-blocking
  • The `issues: write` permission added at .github/workflows/mcp-release-please.yml (release-please job) is commented as being for A publish workflow that fails identically forever should surface once, loudly, not retry quietly #9951's escalation issue-filing, but `gh issue create`/`gh issue list` actually run in the separate `reconcile-stale-releases` job authenticated via `GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }}` (a PAT) — a workflow `permissions:` block doesn't govern PAT-authenticated calls, so this line does nothing for the code it's commented against; confirm RELEASE_PLEASE_TOKEN's PAT scope itself includes Issues:write.
  • `gh issue create --label maintainer-only` assumes that label already exists in the repo; if it doesn't, creation fails and the `||` fallback silently degrades back to a `::warning::` — the exact unread-signal failure mode this PR exists to fix — so it's worth confirming the label is present.
  • The title-substring `gh issue list --search "publish outage ${workflow} in:title"` dedup check has no label/author filter, so it could match an unrelated open issue that happens to contain the same words in its title and suppress a real new filing.
  • `escalate_persistent_publish_failure()` (mcp-release-please.yml, reconcile-stale-releases job) counts leading non-success entries in `publish-mcp.yml`/`publish-miner.yml`'s own run history as a proxy for '3 consecutive commits failing', but `dispatch_and_wait_with_retry` (called just before it) already dispatches up to 3 real workflow runs per single failing push before giving up — so a lone push whose deterministic failure exhausts all 3 retries produces exactly 3 leading failures and escalates on that FIRST bad commit, not after 3 separate commits as the PR's own synthetic-history table and description claim; worse, a single push that exhausts all 3 retries purely from prolonged propagation lag (the explicitly-described flake class the code says should NOT escalate) will still file a 'standing outage' issue, because the retry mechanism and the escalation detector share the same run-history signal with no way to distinguish retries-within-a-push from failures-across-pushes. (demoted: CI state is decided deterministically, not by review)
  • Derive the escalation count from actual `HEAD_COMMIT`/push identity (e.g. dedupe consecutive runs from the same triggering commit, or query only runs where `event == 'push'` vs `workflow_dispatch` retries) rather than raw run-history conclusions, so retries within one push can't masquerade as multiple failed commits.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
  • Diff looks like trivial or whitespace-only churn — Reduce whitespace-only or formatting-only churn and keep the diff focused on substantive changes.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ⚠️ Gate result — Not blocking (Advisory; not blocking this PR.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #9951
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 9 registered-repo PR(s), 8 merged, 287 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 9 PR(s), 287 issue(s).
Improvement ℹ️ Insufficient signal risk: low · value: insufficient-signal
Linked issue satisfaction

Partially addressed
The PR implements consecutive-failure tracking and a once-per-outage escalation (filing/reusing a GitHub issue) for all three give-up sites, directly addressing the 'track consecutive failures' and 'escalate once' asks. However, it still runs the full three retries before escalating rather than stopping retries once a deterministic failure is detected, and it does not add the requested durable 'la

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, TypeScript, Ruby, Go, MDX, Shell, Solidity, JavaScript
  • Official Gittensor activity: 9 PR(s), 287 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Treat this as maintainer-lane context rather than normal contributor-lane activity.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

Decision record
  • action: hold · clause: guardrail_hold
  • config: 0d3ac9bb1fa97935c39af4a931df9799b3e499ee2e7a1781a15d3f7f2c1b5369 · pack: oss-anti-slop · ci: passed
  • record: 006c409b9bac428448a78de86e1749b0893b2c059173f74e947d917b194bcacb (schema v6, head 839f174)

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@JSONbored JSONbored self-assigned this Jul 30, 2026
@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.87%. Comparing base (b2d02dd) to head (839f174).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #9965   +/-   ##
=======================================
  Coverage   91.87%   91.87%           
=======================================
  Files         928      928           
  Lines      113697   113697           
  Branches    27421    27421           
=======================================
  Hits       104460   104460           
  Misses       7936     7936           
  Partials     1301     1301           
Flag Coverage Δ
backend 95.66% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 30, 2026
@JSONbored
JSONbored merged commit 0b01b63 into main Jul 30, 2026
9 checks passed
@JSONbored
JSONbored deleted the fix/publish-outage-escalation branch July 30, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A publish workflow that fails identically forever should surface once, loudly, not retry quietly

1 participant