Skip to content

fix(agent-actions): approval-queue accept bypasses the merge-precision circuit breaker entirely #2127

Description

@JSONbored

Parent: #1936

Problem

The live webhook merge path applies two accuracy circuit-breakers immediately before executing an action: applyPrecisionBreakers(planned, await isHoldOnly(env, repoFullName), await isCloseHoldOnly(env, repoFullName)) (src/queue/processors.ts:1725-1729). If the holdonly flag is engaged — by the auto-tuner after merge precision drops, or manually by an operator — downgradeMergeToHold strips the merge action and substitutes a needs-human-review label. isHoldOnly (src/review/outcomes-wire.ts:62-79) reads the system_flags table fresh on every call specifically so an operator can halt auto-merge instantly.

The approval-queue accept path (src/services/agent-approval-queue.ts:58-71, decidePendingAgentAction) never calls applyPrecisionBreakers/isHoldOnly/isCloseHoldOnly — it rebuilds the action from the persisted pending row via pendingActionToPlanned and passes it straight to executeAgentMaintenanceActions, whose own gate stack (kill-switch → autonomy → dry-run → approval-staging → freshness → write-permission) has no precision-breaker step either.

Failure scenario: a repo's merge precision drops (or an operator manually flips holdonly:global/holdonly:<repo>) at time T. A merge was staged for auto_with_approval before T, with a head SHA that never changes. A maintainer, unaware the breaker just engaged fleet-wide, accepts the pre-existing pending action at T+1. Because accept never checks the breaker, the merge executes anyway — exactly the scenario the breaker exists to stop slips through the one path that bypasses it. Since a merge can only reach the approval queue by construction while the breaker was off at staging time, every staged pending merge is, by definition, a candidate for this gap once the breaker engages later.

Requirements

  • Accepting a staged merge/close action must be subject to the same precision circuit-breaker as the live webhook path, evaluated at accept time (not staging time).
  • An operator engaging holdonly must halt all auto-merges, including ones already staged and awaiting a maintainer's accept — not just future ones.

Deliverables

  • In decidePendingAgentAction, before calling executeAgentMaintenanceActions, re-apply applyPrecisionBreakers using the current isHoldOnly/isCloseHoldOnly state, and pass the (possibly downgraded-to-hold) plan to the executor instead of the raw persisted action.
  • If the breaker strips the action, record a distinct decision outcome (e.g. breaker_engaged) rather than silently executing or silently no-op'ing, so the maintainer's UI/audit trail explains why their accept didn't merge — mirroring how a head-SHA mismatch is already surfaced as "superseded."
  • Add a regression test in test/unit/agent-approval-queue.test.ts: stage a merge while holdonly is off, flip holdonly:global on, accept, and assert mergePullRequest is not called and the outcome reflects the breaker engagement.

Acceptance criteria

  • A staged merge accepted after the precision breaker engages does not call the GitHub merge API; it downgrades to hold/label with a distinguishable audit outcome.
  • A staged merge accepted while the breaker is off still executes normally.

Expected outcome

An operator-engaged (or auto-tuner-engaged) merge-precision halt takes effect fleet-wide immediately, with no window where a pre-existing staged action can slip through it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.gittensor:priorityMaintainer-selected Gittensor priority — scores a 1.5x multiplier.

    Projects

    Status
    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions