Repository navigation
Recover a missing stable GitHub publication without replaying release side effects #602
Description
🤖 Generated by the Agentic Engineer
Problem
Two recorded tag-only releases remain unpublished. The inspector diagnoses them safely, but operators still have no maintained path to publish the existing tag without rerunning arbitrary release plugins or creating another version.
Design
Add a separate repository-local publisher and a default-off reusable recovery workflow. The workflow must run in the affected public repository on its default branch, sharing the normal release concurrency group. Build immutable catalogue code before credentials; first admit the incident with a one-repository read token, then obtain only Contents(write) and Actions(read) for that repository. Reuse the inspector’s original run/tag/source/config checks.
Under the writer credential, prove push capability, enumerate a complete bounded release inventory twice, refuse drafts/conflicting records and capture an existing stable latest release. Re-admit identity immediately before a single create-release request. Publish only the existing stable tag with its captured source, deterministic honest recovery receipt, no generated notes/assets and the JSON string make_latest=false. Independently re-read publication, source and latest anchor. A matching existing publication is a no-op. Ambiguous or failed writes fail without POST retries; a later explicit invocation reconciles through fresh reads.
The receipt names the original run/tag/source/producer and says original generated notes were not reconstructed. No channel-note mutation is included: native semantic-release stable/default-branch controls must establish that missing notes do not require repair in this bounded scope.
Implementation contract
Keep the publisher in the inspector's existing Go module, using a narrow typed GET-only admission/result interface. The inspection command remains read-only. Do not copy its identity checks or parse its printed diagnosis to reconstruct an admitted snapshot.
Keep permanent explicit operator confirmation separate from the temporary rollout flag. Production publication is manual-dispatch only; removing the rollout flag later must not remove publication consent. A matching publication found during read-only admission returns a verified no-op without acquiring a writer token; the same applies if it appears during writer re-admission.
Encode the latest-preservation field as the JSON string "make_latest":"false", not a JSON boolean; the endpoint uses a string enum and otherwise defaults to making a published release latest. A successful read-token release list does not establish draft absence: draft visibility requires push access, so that check belongs under the proven writer capability. Historical workflow-file differences can require additional workflow authorization; propagate a refusal without a broader-token fallback. Preserve exactly one POST and fail ambiguous transport results rather than retrying them.
Primary provider contracts: release creation and draft visibility, conditional request limits. These are design constraints, not evidence that the protected consumer or App has accepted publication.
Provider limits
GitHub has no documented conditional create-release or idempotency key. The queue serializes cooperating workflows only; concurrent external tag mutations are unsupported, and readback detects rather than prevents that race. Missing App push metadata or draft visibility fails closed. Historical workflow authorization may require more than Contents(write); a refusal must propagate without obtaining broader credentials or substituting another source. Protected consumer recovery must resolve these provider uncertainties before claiming either recorded incident recovered.
Acceptance criteria
- Default-off workflow and explicit enabled/offline CI calls execute without production credentials; every reusable caller is included in required aggregate coverage.
- Native HTTP controls exercise one successful POST and independent readback, matching publication no-op, wrong identity, draft/conflict on later pages, missing/false push proof, incomplete/changed inventory, changed latest/tag/source, failed reads/writes, malformed success, connection loss after accepted POST and explicit rerun reconciliation with zero additional POSTs.
- The original inspection command remains read-only. No assets, tag/notes/ref mutation, success/fail plugins, unrelated publication or broader-token fallback exists.
- The maintained workflow is documented, reviewed, merged and released. Real protected consumer publication remains Recover GitHub releases after a tag-only partial publication #587 acceptance; offline tests must not claim it.
- Track rollout and short-lived flag removal as separate open work before shipping the flag.
Refinement
Priority: High. Effort: High including native controls, hosted CI, review and consumer proof. Service class: Risk reduction. Directly reduces repeat operator risk for two observed incidents; no artificial deadline. Prerequisites: #600 admission extraction (delivered and verified) and #604 maintained channel-preservation coverage. Publisher implementation begins after both merge. User explicitly waived WIP limits for this run.
Part of #587
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Start date
Target date
Effort
Projects
- StatusShow more project fields🫴 Ready
Activity
devantler commentedon Oct 15, 2026
Primary-source design review confirms that release listings expose drafts only to principals with push access, and release creation has no documented conditional-write or idempotency parameter. The writer must establish its own inventory/capability proof; the read-only diagnosis cannot authorize publication. Existing-tag target behavior also does not promise an exception from historical workflow authorization. A refused write propagates without permission expansion.
Sources: release API, conditional requests, reusable workflow context, concurrency scope. Independent read-only design assessment completed; no provider publication has been attempted.
devantler commentedon Oct 15, 2026
Refinement complete; Ready with native dependency #600. Rollout/opt-in disposition is separately tracked in #603. Implementation will retain default-off behavior and will not equate loopback fixtures with completed provider recovery. Native planning-field writes remain QUERY-UNKNOWN under the required GraphQL binding classifier; no fallback Priority/Effort copies were created.
devantler commentedon Oct 15, 2026
Repeated the native stable-channel characterization with Node 22.23.3 and the production semantic-release 25.0.3 decision stack, omitting only the authenticated GitHub publisher. Both absent-note and matching
{"channels":[null]}cases select no new version at the stranded tag and 1.2.5 after one subsequent fix. All four runs preserve local refs, remote refs and the working tree. This supports leaving notes unchanged within the stable/default-branch scope; it does not prove prerelease/multibranch behavior or real GitHub publication. The maintained offline suite must retain this coverage.Native HTTP test design is prepared for one POST plus independent readback, explicit confirmation, matching-publication no-op, missing/false writer capability, later-page drafts, pagination refusal, changed inventories/latest/tag/source, malformed success and lost connections after server-side publication. An explicit rerun must reconcile without another POST. Product implementation still waits for #600 to merge.
chore: agent-claim #602 nonce=7146987f936092bb37e04824a6261745