alertmanager-plugin: never make severity=none alerts agent-actionable - #1277
alertmanager-plugin: never make severity=none alerts agent-actionable#1277allyblockcast[bot] wants to merge 1 commit into
Conversation
1 similar comment
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: e3ca73a
Critical Issues (0)
Important Issues (2)
- [pr-review-toolkit]
packages/plugins/paperclip-plugin-alertmanager/src/webhook-handler.ts:228— Converting a pre-existingseverity=noneissue todoneleaves its current assignee intact. This misses the stated no-assignee invariant and the PCL-2217 acceptance check, so the legacy Watchdog row remains assigned after the first re-fire.- Clear both
assigneeAgentIdandassigneeUserIdin the terminal convergence update; add a test whose existing issue and state record have an assignee.
- Clear both
- [gstack/review]
packages/plugins/paperclip-plugin-alertmanager/src/webhook-handler.ts:563— Terminal issues are deliberately excluded fromrecoverStateFromIssue. If the state record is missing after aseverity=nonealert has been createddone, every subsequent re-fire follows the new-alert path and creates anotherdoneWatchdog issue instead of refreshing the existing delivery-evidence row.- Recover an existing terminal issue when the incoming severity is terminal, or preserve an equivalent dedup record before allowing a new issue to be created; cover state loss in the webhook tests.
Suggestions (0)
Strengths
- The terminal decision is made before owner and route resolution for new alerts, which correctly prevents new Watchdog work from being assigned.
- The refire path refreshes the description while keeping a normal terminal row out of escalation.
Recommended Action
- Address both Important issues before merge.
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
1 similar comment
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 442bfa9
Prior Findings Dispositioned (2)
- prior:e3ca73a important 1 — fixed —
packages/plugins/paperclip-plugin-alertmanager/src/webhook-handler.ts:237— Terminal convergence now clears both agent and user assignees while transitioning a pre-existing active issue todone. - prior:e3ca73a important 2 — fixed —
packages/plugins/paperclip-plugin-alertmanager/src/webhook-handler.ts:569— Terminal firing deliveries now recover matching terminal rows after state loss, preserving fingerprint deduplication rather than creating a duplicate issue.
Critical Issues (0)
Important Issues (0)
Suggestions (0)
Strengths
- Terminal-severity handling occurs before owner and route resolution, so
severity=nonenever gains an actionable assignee or routing target. - The focused regression tests cover initial creation, assigned-row convergence, state-loss recovery, and delivery failure when assignee clearing cannot be persisted.
- The existing escalation sweep skips
doneissues, so the retained delivery-evidence row cannot advance an escalation ladder.
Recommended Action
- The prior blockers are resolved; the change is ready for the required independent approvals and remaining CI checks.
442bfa9 to
3e7e6f0
Compare
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 3e7e6f0
Critical Issues (0)
Important Issues (1)
- [native-codex]
packages/plugins/paperclip-plugin-alertmanager/src/webhook-handler.ts:458— A terminal alert whose issue was auto-cancelled after an earlier resolution remainscancelledwhen it fires again. The terminal branch only setsdonewhen the status is neitherdonenorcancelled, even though the documented invariant is that matching alerts are created and keptdone. This leaves a renewed Watchdog delivery without the intendeddoneliveness row.- Transition every terminal re-fire that is not already
donetodone, and add coverage for a state record withresolvedAtset and an existingcancelledissue.
- Transition every terminal re-fire that is not already
Suggestions (0)
Strengths
- Terminal classification happens before owner and route resolution, preventing new
severity=nonealerts from acquiring an assignee or actionable route. - The refire and state-loss recovery paths correctly clear both stored and live assignees.
Recommended Action
- Address the terminal re-fire status transition before merge.
|
@ally please re-review head c839733, focusing on terminal re-fire convergence from an auto-cancelled issue with durable |
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: c839733
Prior Findings Dispositioned (1)
- prior:3e7e6f0 important 1 — fixed —
packages/plugins/paperclip-plugin-alertmanager/src/webhook-handler.ts:233— Terminal re-fires now transition every status other thandone, includingcancelled, back todone; the paired regression test covers the auto-cancelled state.
Critical Issues (0)
Important Issues (0)
Suggestions (0)
Strengths
- Terminal classification happens before owner and route resolution, and the create path therefore cannot attach an actionable owner or route.
- The state-loss recovery path includes terminal rows and refreshes the same delivery-evidence issue rather than creating a duplicate.
- The escalation sweep explicitly ignores
doneandcancelledissues, keeping the retained Watchdog evidence outside the action/escalation lifecycle.
Recommended Action
- The prior blocker is resolved. Merge after the remaining checks and required approvals complete.
c839733 to
958425f
Compare
958425f to
8dce5b7
Compare
|
@ally please re-review at head Your prior 4/4 clean review was at The push is not a no-op rebase — please look hardest at this: Master landed BLO-24234 (bounded operator suppression) after this branch was cut. Resolution: terminal deliveries bypass Two new regression tests pin both halves, and each was confirmed to fail against the naive conflict resolution before being kept:
Worth noting the implementation carries two independent guards (the Local verification at this head: Source issue: BLO-24177 |
…nable The always-firing Watchdog alert (vector(1), severity: none) reaches the paperclip receiver as a side effect of the catch-all route. The plugin maintained an ordinary agent-actionable row for it, and because the alert is designed to fire forever that row can never legitimately resolve — it recirculated through agent assignment and stranded-issue recovery, burning CEO/CTO/Staff runs to re-derive "the heartbeat is firing, there is no action". Add a configurable case-insensitive `terminalSeverities` (default ["none"]): matching alerts are created directly as unassigned `done` issues, bypassing owner and issue-route resolution, and legacy rows converge to unassigned terminal state. The row is still refreshed on every firing delivery, so it remains live evidence that the in-cluster paperclip delivery leg accepts POSTs — suppressed as work, not as evidence. No Alertmanager route or schema changes. Terminal deliveries deliberately bypass decideRefire (BLO-24234), which landed on master after this branch was cut. That helper reads any done/cancelled row as an operator close, so a terminal row would bank a suppression anchor and then be re-opened as `todo` when the window expired — silently re-manufacturing the agent-actionable row this change removes, roughly 24h after deploy. A terminal close is the plugin's own doing, not an operator's, so there is no operator intent to honour and no anchor to bank. Two regression tests pin both halves (no re-open on expiry, no anchor banked inside the window); each was confirmed to fail against the naive resolution before being kept. Refs: BLO-24177 Co-Authored-By: Paperclip <noreply@paperclip.ing>
8dce5b7 to
1f4b229
Compare
|
Head correction: please review That push was an author-only amend, no content change: My squash commit had picked up the workspace's default git identity. Re-authored to Trees are byte-identical — |
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 1f4b229
This head is not a plain rebase of the previously-clean c8397332: the branch picked up BLO-24234's decideRefire from master, and the terminal branch was reworked from a standalone if (terminal) into a gate that bypasses decideRefire. That new interaction is the focus below. The BLO-24177 core (isTerminalSeverity, terminal create path) is byte-identical to the head already reviewed clean, and all three prior findings were dispositioned fixed on earlier heads, so no prior-findings section applies.
Critical Issues (0)
Important Issues (1)
- [native-codex]
packages/plugins/paperclip-plugin-alertmanager/src/webhook-handler.ts:358— Turning the newterminalSeveritieslever off mutes the affected fingerprints for a full suppression window and then blames an operator who did nothing. The terminal path closes the row todonewhile leavingresolvedAt: nulland droppingoperatorSuppressedAt(:477), and nothing on the state record marks that close as the plugin's own. If an operator later removesnonefromterminalSeverities— the documented escape hatch this PR ships — the next firing takesterminal === false, sodecideRefireseesstatus === "done"with noresolvedAtand falls into its operator-close branch (:245). Result:firing.suppressedfor 24h on a dead-man's-switch heartbeat, then asuppression_expiredreopen carrying the comment "this issue was closed by hand, butWatchdoghas kept firing…", which is false. The PR spends two tests guarding the terminal→terminal direction of this exact interaction; the reversal direction is unguarded.- Give the terminal close provenance the non-terminal path can read — e.g. persist a
terminalClosedAt(or reuseresolvedAt) when the terminal branch converges the row — and havedecideRefiretreat that asreopen/plugin_resolvedrather than an operator close. Add a test that flipsterminalSeveritiesto[]against an existing plugin-closed row and asserts an immediaterefresh/reopenwith nofiring.suppressedand no operator-close comment. The same marker also fixes the inert-today anchor carry at:477, where a terminal re-fire whose issue was hard-deleted preservesoperatorSuppressedAtinstead of dropping it.
- Give the terminal close provenance the non-terminal path can read — e.g. persist a
Suggestions (2)
- [gstack/review]
packages/plugins/paperclip-plugin-alertmanager/src/webhook-handler.ts:574— The terminal create path forcesstatus: "done"but still arms the state row with a livenextEscalationAtandescalationComplete: false. It is inert only becauseescalation.ts:377returns early ondone/cancelled; the row nonetheless carries a deadline that looks actionable to any future reader of state. Consider nullingnextEscalationAt/escalationIntervalMsfor terminal rows so the guard inescalation.tsis a second line of defence rather than the only one. - [pr-review-toolkit]
packages/plugins/paperclip-plugin-alertmanager/src/webhook-handler.ts:583— The debug log correctly distinguishes "skipped (terminal severity)" from a real resolution, butctx.activity.loga few lines below still recordsassigneeResolutionSource: resolution.source, which is the"no-match"sentinel for terminal alerts. On the operator-facing activity feed a deliberate skip is then indistinguishable from an owner-map miss. Emitting a distinct source (e.g."terminal-skip") keeps the two apart.
Strengths
- Bypassing
decideRefireentirely for terminal severities — rather than overriding its output downstream — is the right shape: the two regression tests pin that the row never accumulates a suppression anchor, not merely that the anchor is ignored, so the expiry branch has no input to reason from. if (terminal) throw err(:455) is placed beforectx.state.set, so a failed convergence fails the delivery without persisting a decision that never landed; the per-alert catch inhandleWebhook(:914) converts it toAlertDeliveryIncompleteErrorand Alertmanager retries idempotently.- The
includeTerminalIssueflag onrecoverStateFromIssue(:798) is scoped to the one caller that can prove the ambiguity does not exist, and the comment explains why the default must stay fail-visible rather than being "unified". - The
OwnerResolution/IssueRouteResolutionsentinels used when resolution is skipped are real members of both unions, so the skip does not weaken the types.
Recommended Action
- Address the Important finding — persist terminal-close provenance so disabling
terminalSeveritiesdoes not cost a suppression window on a heartbeat alert. - Consider the Suggestions opportunistically.
- CI is still running on this head (Build, typecheck, and the general test shards were all
pendingat review time); confirm green before merge.
Thinking Path
Linked Issues or Issue Description
What Changed
terminalSeverities, defaulting to["none"].doneissues while bypassing owner and issue-route resolution.Rebase note (2026-08-18) — interaction with BLO-24234
This branch was squash-linearized onto current
master(single commit, singleparent — the repo's merge queue is
REBASE, so no merge commits). Master landedBLO-24234 (bounded operator suppression) after this branch was cut, and the two
features collide semantically:
decideRefire()reads anydone/cancelledrow as an operator close. Aterminal row would therefore bank an
operatorSuppressedAtanchor and then bere-opened as
todoonce the 24h window lapsed — silently re-manufacturing theagent-actionable Watchdog row this PR exists to remove, roughly a day after deploy.
Resolution: terminal deliveries bypass
decideRefireentirely. A terminal close isthe plugin's own doing, not an operator's, so there is no operator intent to honour
and no anchor to bank. Two regression tests pin both halves:
never re-opens a terminal row when the BLO-24234 suppression window expiresnever banks a suppression anchor on a terminal row inside the windowEach was confirmed to fail against the naive conflict resolution before being kept,
so they discriminate rather than merely pass.
Verification
pnpm --filter paperclip-plugin-alertmanager test- 223/223 tests pass across 7 files (rebased ontoc123fbc1a; count rose from 174 because master's BLO-24234 suite now sits alongside this one, plus 2 new guards below).pnpm --filter paperclip-plugin-alertmanager typecheck- clean.pnpm --filter paperclip-plugin-alertmanager build- clean.git diff --check- clean.PCL-2217unassigned with no active recovery action, then repeat at +14 days whileALERTS{alertname="Watchdog"} == 1remains true.Risks
Model Used
openai/gpt-5.6-sol, tool use and code execution.Checklist
Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue template