Skip to content

Unclaim overwrites GitHub's labels from the local cache, reverting anything changed since the last sync #921

Description

@joryirving

Unclaim overwrites GitHub's labels from the local cache, silently reverting anything changed since the last sync

Problem

releaseIssueClaim in src/lib/issue-claim.ts computes the new label set from
the cached row and then writes that whole set to GitHub:

// line 93
let updatedLabels = issue.labels.filter((label) => label !== agentLabel);
...
// line 156
await updateIssueLabels(repoFullName, issueNumber, updatedLabels);

updateIssueLabels replaces the label set rather than removing one label. The
caller, src/app/api/issues/unclaim/route.ts:48, is explicit that its source is
the cache: "Fetch the issue from the local database to get current labels".

So when the cached row is behind GitHub, unclaiming does not just drop the
agent label — it reverts every label change made on GitHub since the last sync.
Labels added there are deleted; labels removed there are restored.

Observed

Unclaiming an issue restored status/backlog and a blocked/* label that had
been cleared on GitHub minutes earlier. Both are circulation-affecting: the
issue went back to a non-claimable lane and stayed parked. Recovering it took a
manual gh edit, then refresh_issue, then set_issue_status.

The failure is silent. The write succeeds, the audit log records the labels it
intended to write, and nothing reports that GitHub's state was discarded.

Why it matters beyond one issue

Anything that reverts status/ or blocked/ takes work out of the queue, and
the symptom presents as "the loop is ignoring this issue" rather than as a
labelling bug — the cause is several steps removed from where it is noticed.

Suggested fix

Prefer the targeted primitive over set replacement. issue-claim.ts already
imports removeIssueLabel and does not use it on this path:

  • remove the agent label with removeIssueLabel,
  • apply any status transition through its own targeted call,
  • leave every other label untouched, so a stale cache cannot revert it.

If a full-set write is genuinely needed, re-fetch the issue's labels from GitHub
immediately before computing the new set, and treat the cache as a hint rather
than the source of truth. A stale-write guard — comparing the fetched set to the
cached one and refusing to drop labels the cache never knew about — would also
surface the condition instead of silently applying it.

stale-work.ts also calls releaseIssueClaim, so it inherits the same
behaviour and should be covered by whichever fix lands.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions