Repository navigation
Recover GitHub releases after a tag-only partial publication #587
Description
🤖 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
- Use the maintained credential-free native release fixture and its exact shipped semantic-release 25.0.3 command against a disposable local Git remote.
- An ordinary
fix:after v1.2.3 computes v1.2.4. - 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.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Effort
Projects
- StatusShow more project fields🫴 Ready
Activity
devantler commentedon Oct 14, 2026
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 ofrefs/notes/semantic-release-v1.223.3with 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'smainshows 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:
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.3should 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.
chore: agent-claim #587 nonce=727859a376b98c04ad0d78e00ac4e14e
devantler commentedon Oct 15, 2026
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:
v1.2.4at the captured fix commitfix:commit1.2.5{"channels":[null]}1.2.5All 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.
devantler commentedon Oct 15, 2026
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.
devantler commentedon Oct 15, 2026
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.