Skip to content

Let a silent team's findings out of the escalation queue (#190) - #200

Merged
icebergai-review-bot[bot] merged 1 commit into
mainfrom
claude/codebase-review-cleanup-ovlli4
Aug 19, 2026
Merged

Let a silent team's findings out of the escalation queue (#190)#200
icebergai-review-bot[bot] merged 1 commit into
mainfrom
claude/codebase-review-cleanup-ovlli4

Conversation

@richardmhope

Copy link
Copy Markdown
Contributor

Fixes #190.

Overdue escalation could stall behind findings that had nobody to escalate to.

The defect

_escalation_targets legitimately returns no targets in three cases: an owning team with no channel ("silent by choice", and allowed), a team pointing at a disabled channel, and an unowned finding that no enabled channel's filter selects. None of them writes an outbox row — so the SQL exclusion, which asks "has this been escalated for this deadline?", kept answering no and kept selecting them.

The beat is bounded (200) and ordered by deadline, oldest first. Once that many silent findings accumulated, they occupied the whole page on every beat and nothing behind them was ever escalated. That is precisely the stall the exclusion was written to prevent, arriving by a different route: a bounded page that never advances.

Reproduced against merged main — three silent findings, one targeted finding with a later deadline, five beats at limit=2:

AssertionError: a finding behind the silent ones was never escalated

Zero escalations, indefinitely.

The fix

"Has this finding anywhere to go?" is now answered by the query that selects the page, rather than in Python after it — the same reason the existing exclusion is in SQL: it has to happen before the limit.

  • _has_escalation_target is the SQL twin of _escalation_targets, mirroring its three cases.
  • _wanted_by is channel_wants as a predicate, built from the same parsed EventFilter — one filter vocabulary, two evaluation sites, rather than two vocabularies drifting apart. Severities are enumerated from the existing rank rather than compared in SQL, since the column stores the label and the ordering is the filter's policy.
  • A test holds the two readings against each other across the whole severity range and on a source filter, so they cannot quietly diverge — the same discipline tests/test_suppression_consistency.py applies to the API/engine suppression pair.

The design I did not take

The issue proposed recording a terminal "skipped/silent" delivery row, which preserves more evidence, and I'd have preferred it. It isn't available: notification_delivery.channel_id is non-nullable, and two of the three cases have no channel at all. Making it nullable would weaken what the row means — one announcement, to one channel, about one finding — and break the partial unique index that stops an escalation re-inserting on every beat, because NULLs don't collide in a unique index. That is a lot of structural damage for a bookkeeping row, so the exclusion goes in the query instead. The console's overdue queue remains the record for silent findings, exactly as the docs already said.

Validation

  • Regression test reproducing the starvation, confirmed to fail against the pre-fix code.
  • Two drift-guard tests pairing channel_wants with _wanted_by. (These fail pre-fix only because the helper is new — they guard the future, they aren't evidence about this bug.)
  • Full suite 1950 passed, 2 skipped; ruff / ruff format / mypy / make docs-check clean.

Incidental

Owner groups are now loaded once to build the predicate, which also retires the db.get(OwnerGroup, ...) issued per finding in the loop below — one of the N+1s noted in #197.

Operator note

No action required, and no migration. The next beat picks up whatever had been starved.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sohE85sRDt6t2w3936rGJ


Generated by Claude Code

Escalation could stall behind findings that had nobody to escalate to.

`_escalation_targets` returns no targets in three cases: an owning team with no
channel ("silent by choice", and allowed), a team pointing at a disabled
channel, and an unowned finding no enabled channel's filter selects. None of
them writes an outbox row — so the SQL exclusion, which asks "has this been
escalated for this deadline?", kept answering no and kept selecting them.

The beat is bounded and ordered by deadline, oldest first. Once `limit` silent
findings accumulated they occupied the whole page on every beat, and nothing
behind them was ever escalated. That is the stall the exclusion was written to
prevent, arriving by a different route: a bounded page that never advances.

So "has this finding anywhere to go?" is now answered by the query that selects
the page rather than in Python after it. `_has_escalation_target` is the SQL
twin of `_escalation_targets` and mirrors its three cases; `_wanted_by` is
`channel_wants` as a predicate, built from the same parsed `EventFilter`, so
there is one filter vocabulary and two evaluation sites rather than two
vocabularies. A test holds the two readings against each other on the whole
severity range and on a source filter, so they cannot quietly diverge.

An outbox row recording the silence would have been the more evidence-preserving
fix and is what the issue proposed, but `notification_delivery.channel_id` is
non-nullable and two of the three cases have no channel at all. Making it
nullable would weaken the row's meaning — one announcement, to one channel — and
break the partial unique index that stops an escalation re-inserting every beat,
since NULLs do not collide. Not worth it for a bookkeeping row.

Owner groups are now loaded once for the predicate, which also retires the
`db.get(OwnerGroup, ...)` per finding in the loop below it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sohE85sRDt6t2w3936rGJ

@icebergai-review-bot icebergai-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict

APPROVE

Completed bounded review across 1 immutable scope(s). No independently actionable regressions found in the supplied change.

Scope health

Convergence: healthy. Review mode: initial.
Recommended action: CONTINUE_INCREMENTAL.

  • No escalation signals.

Prior findings

Finding Status
No prior finding state

New findings

No new findings.

Fix-induced regressions

  • None evidenced.

Uncertainty

  • No material uncertainty recorded.

Validation

  • Reviewed the supplied immutable diff and its added regression coverage.
  • Exact-head CI is reported as passed.

Residual risks

  • None identified.

@icebergai-review-bot
icebergai-review-bot Bot merged commit 6206da1 into main Aug 19, 2026
6 checks passed
@icebergai-review-bot
icebergai-review-bot Bot deleted the claude/codebase-review-cleanup-ovlli4 branch August 19, 2026 23:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Escalation starves: an overdue finding with zero escalation targets is re-selected every beat and blocks the page

2 participants