Skip to content

orb(actuation): the label-vs-close correlation guard is batch-scoped — a rejected or breaker-downgraded close still brands the PR with its enforcement label #9158

Description

@JSONbored

Problem

The #label-close-split-brain guard — which exists to stop ORB branding a PR with an enforcement label
when the paired close did not happen — only inspects the same planned array
(src/services/agent-action-executor.ts:252-257):

for (let i = 0; i < outcomes.length; i++)
  if (planned[i]?.actionClass === "close" && planned[i]?.closeKind === closeKind) return outcomes[i]?.outcome;
return undefined;                       // → guard does not fire

Two paths never carry the pair, so the guard silently no-ops.

1. The approval queue stages one action per row

agent-approval-queue.ts:344 builds plan = [pendingActionToPlanned({ actionClass: pending.actionClass, … })]
— a single action. createPendingAgentActionIfAbsent conflicts on
(repoFullName, pullNumber, actionClass) (src/db/repositories.ts:6355), so the close and its
blacklist / contributor_cap / review_nag / copycat label are two independently accept/rejectable
rows
. A maintainer rejecting the close and accepting the label labels an open PR "blacklisted
contributor".

2. After a breaker downgrade the close is gone but the label remains

downgradeCloseToHold's isDowngradableClose requires actionClass === "close"
(src/settings/agent-actions.ts:625-651), so #9115 now drops a copycat/review_nag close while leaving
its closeKind-tagged label in the plan. The executor then finds no close at all → undefined → label
applied.

Trigger

closehold:<repo> engaged plus a block-mode copycat match (agent-actions.ts:939-943); or a maintainer
rejecting a staged close whose companion label row is still pending.

Impact

A PR is publicly branded with a plagiarism / ban / cap enforcement label while remaining open and
unclosed — precisely the outcome the guard's own doc comment says must never happen. Contributor-visible
reputational harm from a decision the system deliberately withheld, and nothing removes the label
afterwards (see #9009manual-review is not the only sticky label).

Requirements

  • Make the correlation durable rather than batch-local: stage the coupled pair as one pending row
    whose params carry both mutations, so accept/reject is atomic over the pair.
  • Or have downgradeCloseToHold and the approval accept path drop any label whose closeKind has no
    surviving/accepted close.
  • Add an invariant test: no enforcement label is ever applied without its paired close having succeeded,
    across all execution entry points.

Test Coverage Requirements

99%+ patch coverage, branch-counted; both paths (approval-queue split, post-breaker downgrade) covered
explicitly.

Links & Resources

maintainer-only — actuation atomicity.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions