Skip to content

Recover GitHub releases after a tag-only partial publication #587

Open
2 of 4 issues completed
Open

Description

@devantler

🤖 Generated by the Agentic Engineer

Expected Behavior

An interrupted GitHub-only release can be completed through a reviewed, explicitly enabled recovery path that preserves its existing tag and exact source. A later normal run must not be mistaken for proof that the missing publication recovered.

Actual Behavior

The shared release command pushes the tag and channel note before invoking publication. If the notes push fails after the tag arrives, the next invocation treats that tag as the last release and can return success without publishing it. Both the maintained catalogue at 8ed2071ebe18864b8b86efe0f6b1df8d73d2a6ab and Platform's legacy caller use semantic-release 25.0.3 without a partial-publication recovery path.

Platform's incident is tracked separately in devantler-tech/platform#4839. Failed Release run 38418390528 at 607fd864e98d7883274270d0737934963039407c left tag v1.222.1 present and its GitHub release absent. Later runs 38422957712 and 38425819758 succeeded while recognizing that tag and publishing no new version. That original release-by-tag read returned 404; its then-current published list ended at v1.222.0. A second partial publication later left v1.223.3 missing as well. The merged read-only inspector at 24a32fa has now diagnosed both original incidents as missing-github-release. Those bounded reads do not inventory drafts or channel metadata and authorize no future write. Actual publication recovery remains undelivered in #602/#603. The original remote notes rejection's cause remains unknown.

Steps to Replicate

  1. Use the maintained credential-free native release fixture and its exact shipped semantic-release 25.0.3 command against a disposable local Git remote.
  2. An ordinary fix: after v1.2.3 computes v1.2.4.
  3. Add v1.2.4 to that same fix commit, representing the tag-only partial effect, then run the same command again. It succeeds with no new release. Files and refs remain unchanged.

All twelve release-decision controls passed in this diagnostic. Its GitHub publisher is omitted exactly as the existing offline fixture does, so it proves the tag-based decision and no-op behavior, not real GitHub publication or the original rejection's cause.

Acceptance Criteria

  • Define the supported recovery scope explicitly: GitHub-only publication, one existing stable version tag, captured source and original failed run. Reject arbitrary publisher configuration, malformed identities, mismatches and ambiguous or incomplete observations.
  • Deliver a default-off reviewed recovery path in this maintained catalogue first. Preserve normal consumer behavior, protection, immutable helper provenance and repository-scoped credentials; serialize recovery with normal release writers.
  • Admit the original tag/source/run relationship and known partial effects before obtaining write authority. Re-read before mutation; a matching completed publication is a verified no-op, while conflicting publication refuses all writes.
  • Preserve the existing tag and source. Restore only the explicitly missing publication and any necessary authenticated channel metadata; do not delete, replace or force-push release refs, rerun unrelated publishers, or move another version's aliases.
  • Propagate failed and ambiguous writes, avoid blind replay, and verify the completed GitHub release and exact source independently from the job conclusion. Keep the original failure record and disclose evidence limits.
  • Exercise actual source code with normal, partial, complete, mismatched and failed-read/write controls. Hosted tests stay read-only and credential-free; real publication proof belongs to the protected consumer recovery.
  • Mirror only the urgent compatible legacy requirement after maintained delivery, then recover Platform v1.222.1 through that reviewed path. Track consumer rollout and removal of the release flag rather than leaving it permanently experimental.

High priority: this is the missing shared prerequisite for an already-started release incident. High effort includes safe recovery implementation, CI, semantic review, maintained and legacy delivery, and real consumer readback. Capture in Backlog until ownership, refinement and delivery capacity are freshly established; this issue is not evidence that recovery was implemented or run.

Part of #287. Related incident: devantler-tech/platform#4839.

Primary source: semantic-release 25.0.3 release sequence, tag-based last release selection, and upstream partial-publish issue.

Activity

  1. added theissue type on Oct 13, 2026
  2. updated Effort High and Priority High on Oct 13, 2026
  3. devantler commented on Oct 14, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Refined → Ready (2026-10-14)

    Why now: the failure this issue recovers from has happened again. Platform Release run 38573634755 (13:39Z today) created tag v1.223.3, then GitHub refused the push of refs/notes/semantic-release-v1.223.3 with the same generic (failed) result. No GitHub release exists for that tag. The releases just before it (v1.223.0–v1.223.2, all today) published normally, so the failure comes and goes. It does not mean notes can never be written. Details are on devantler-tech/platform#4839.

    Cost of waiting: each occurrence leaves a gap that stays. As the body explains, the next normal run treats the stranded tag as the last release and publishes nothing for it. Platform now has two missing releases (v1.222.1, v1.223.3), and Platform's main shows a red Release run until the next release succeeds. Without this path, recovering a missing release means a person acting outside the reviewed pipeline.

    Planning values:

    • Priority High (kept): this is the shared missing piece for an open incident that has now happened twice.
    • Effort High (kept): a recovery path in the shared release workflows, CI, review, then a mirror for Platform's older release setup and a real recovery with readback.
    • Service class Risk reduction: no fixed deadline and no live outage. The deployed revisions are unaffected; only the release records are missing. This shrinks a recurring integrity gap.

    Prerequisites checked: no native blockers (blockedBy = 0). Parent #287. The acceptance criteria above define the scope, the controls and the order (shared workflows first, then Platform's older setup, then recovering v1.222.1). v1.223.3 should be recovered through the same path once it is delivered.

    Open question, does not block: why GitHub refuses the notes push is still unknown. The repository rulesets target branches, tags and repository settings only, so none of them matches refs/notes/*. When the recovery reads back a failed run, it should record the original rejection so a pattern can show up.

    Ready does not mean started. Pull it by importance once In Progress has room.

  4. added a commit that references this issue on Oct 14, 2026
  5. moved this from 📥 Backlog to 🫴 Ready in 🌊 Project Boardon Oct 14, 2026
  6. devantler commented on Oct 15, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    The read-only admission slice is in #599, closing child #598. Publication recovery, legacy delivery and protected consumer verification remain here.

    I checked whether stable/default-branch recovery needs to repair a missing semantic-release channel note. A credential-free, disposable local Git diagnostic ran the shipped semantic-release 25.0.3 release command with the maintained JSON configuration, omitting only the GitHub publisher as the existing native fixture does. Both variants passed:

    Existing v1.2.4 at the captured fix commit Same commit One subsequent fix: commit
    No channel note No new version Computes 1.2.5
    Note containing {"channels":[null]} No new version Computes 1.2.5

    All four dry runs preserved local and remote refs and consumer files. This supports the narrower design for the supported stable/default-branch configuration: restoring a missing GitHub release record may need no notes write. It does not prove publication, authorize mutation, explain the original notes rejection, or cover prerelease and multibranch configurations.

    That result agrees with the pinned upstream implementation: missing notes default to the ordinary channel, and stable last-release selection still recognizes the existing version tag. Carry this as an evidence-backed constraint when refining the write path: avoid adding ref-writing authority unless a supported case demonstrates that it is necessary. The future recovery tests still need to prove the actual GitHub publication/no-op/conflict behavior and normal-release serialization.

  7. devantler commented on Oct 15, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    The first delivery child #598 is complete: #599 is merged and released in v7.11.0, at 226e6e7e5f3609868eb96a6a26f6dc293c88ba30. Its acceptance receipt records exact-head CI/review and actual default-branch workflow execution against both original incidents, using the scoped read-only path. Disabled mode also completed without credential steps.

    Both diagnoses still say missing-github-release, with no publication success or future write authorization. This parent remains open. The writer must still establish the additional publication admission, draft/conflict handling, normal-release serialization, failed/ambiguous-write reconciliation and independent completed-release readback before obtaining mutation authority. Maintained delivery precedes the urgent legacy mirror and protected Platform recovery. Preserve the original failures and existing version tags.

    The earlier four-case offline channel-note evidence remains a design constraint: stable/default-branch recovery may require no notes write. It does not explain the original rejection or satisfy real publication acceptance. No Platform source, release record, tag, channel note or alias was changed by this delivery.

  8. devantler commented on Oct 15, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    This round is finishing the maintained path in independently reviewable increments: #600 / PR #601 preserves the current inspector while making its GET-only admission reusable; #602 owns the default-off publisher and native failure controls; #603 owns protected rollout and opt-in disposition. Both historical publications remain missing and this parent remains open.

    The CLI GraphQL identity has now recovered: a live viewer/rateLimit read succeeds, the complete Project 5 binding classifier passes, and native issue-field readbacks confirm High priority with Low/High/Medium effort on #600/#602/#603 respectively. This clears the temporary planning metadata unknown; it does not prove the separate workflow-installation quota failure in #596 is repaired.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    Priority

    High

    Effort

    High

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions