Skip to content

fix(productivity-review): exclude dependency-gate cancellations from streak, skip blocked issues (BLO-22436) - #1158

Merged
allyblockcast merged 3 commits into
masterfrom
platformsre/blo-22436-dependency-blocked-streak
Aug 14, 2026
Merged

fix(productivity-review): exclude dependency-gate cancellations from streak, skip blocked issues (BLO-22436)#1158
allyblockcast merged 3 commits into
masterfrom
platformsre/blo-22436-dependency-blocked-streak

Conversation

@allyblockcast

@allyblockcast allyblockcast Bot commented Aug 8, 2026

Copy link
Copy Markdown

Thinking Path

  • Paperclip is the open source app people use to manage AI agents for work
  • The productivity-review service watches each agent's runs on an issue and raises a review when the pattern looks unhealthy — no_comment_streak fires when N consecutive terminal runs produced no issue comment
  • fix(productivity-review): exclude never-executed runs from no_comment_streak (BLO-21769) #1041 (BLO-21769) excluded zero-token infra failures (livenessState: "failed" + no tokens) from that streak. It did not cover a second, disjoint population: cancelQueuedRunForBlockedDependencies (heartbeat.ts) cancels a queued run before dispatch when the issue has an unresolved blockedBy edge. That cancellation never gets classified failed liveness (it never ran), so it slipped past the fix(productivity-review): exclude never-executed runs from no_comment_streak (BLO-21769) #1041 filter entirely
  • This is not a rare edge case, it is self-reinforcing: the standard remediation for a flagged platform fault is to model it as a blockedBy edge. Doing that guarantees the streak keeps growing on every subsequent cycle the issue stays blocked. BLO-20815 triggered two full CTO productivity reviews (BLO-21723, BLO-22262) on exactly this loop — both verdicts: not an assignee failure, zero-token dependency-gate cancellations
  • This pull request splits the never-executed population by cause (isInfraFailureRun vs isDependencyBlockedRun), excludes dependency-gate cancellations from noCommentStreak without folding them into runtime_failure_streak (they are a graph-state fact, not an infra fault), and adds a hard gate so a currently-blocked issue is skipped for productivity review entirely, regardless of trigger
  • The benefit is that an issue with an unresolved blocker can no longer generate a productivity review at all — closing the loop where the fix for one review becomes the cause of the next

Linked Issues or Issue Description

Related PRs found by search (no duplicates):

What Changed

  • server/src/services/productivity-review.ts
    • Split isNeverExecutedRun into isInfraFailureRun (unchanged BLO-21769 zero-token+failed-liveness check) and isDependencyBlockedRun (errorCode === "issue_dependencies_blocked"). isNeverExecutedRun is now their union, used to exclude both from noCommentStreak's walk.
    • runtimeFailureStreak now walks isInfraFailureRun only — dependency-gate cancellations no longer surface as runtime_failure_streak (an infra-fault trigger with a "route to platform/SRE" remedy menu that doesn't fit a graph-state fact).
    • collectEvidence now fetches issuesSvc.listDependencyReadiness for the source issue up front and returns null (skip) whenever unresolvedBlockerCount > 0, before any trigger logic runs — an issue can't be reviewed while its runs are being cancelled by the dependency gate, regardless of which trigger would otherwise fire.
    • Added nonExecutingRunCount / nonExecutingDominantErrorCode to the evidence and to buildReviewMarkdown's Evidence section (- Non-executing runs in sample window (excluded from streaks above): N (dominant errorCode: ...)), so a review generated for another reason (e.g. a genuine no-comment streak with some non-executing runs mixed into the sample window) reports dispatch health explicitly instead of the reviewing manager having to re-derive it from raw run telemetry.
  • server/src/__tests__/productivity-review-service.test.tsinsertRuns helper gained errorCode and spacingMs params; added an addBlocker helper (inserts a blocks issueRelations edge); three new tests (below).

Verification

$ pnpm exec vitest run src/__tests__/productivity-review-service.test.ts
 Test Files  1 passed (1)
      Tests  100 passed (100)
   Duration  133.40s

Three new tests, all integration tests against the real reconcileProductivityReviews path on embedded postgres:

  1. excludes issue_dependencies_blocked cancellations from both streaks and produces no review — 10 dependency-gate-cancelled runs → result.created === 0, result.skipped === 1, no review issue. Matches the AC's "re-running the detector against BLO-20815's history produces no review issue."
  2. skips an issue with an unresolved blocker regardless of streak — a genuine 10-run silent-but-executed streak (would trip no_comment_streak on its own) plus a live blocks edge → still result.created === 0. The hard gate wins over any trigger.
  3. reports non-executing dependency-blocked runs separately, without inflating either streak, once a review fires for another reason — 3 recent dependency-gate cancellations (blocker since resolved, so the gate above doesn't apply) + 10 older genuinely-silent executed runs → review fires on no_comment_streak: 10, runtime_failure_streak: 0, and a new evidence line: Non-executing runs in sample window (excluded from streaks above): 3 (dominant errorCode: issue_dependencies_blocked).

All pre-existing BLO-21769 tests (positive control included) still pass unmodified — isInfraFailureRun is behaviorally identical to the old isNeverExecutedRun for the infra-failure population; only the dependency-gate population is new.

tsc --noEmit -p server clean (0 errors).

Risks

Model Used

  • Claude Sonnet 5 (claude-sonnet-5[1m], 1M context window), via the PlatformSREEngineer Paperclip agent (claude_k8s adapter), with tool use and code execution.

Checklist

  • I have included a thinking path that traces from project context to this change
  • I have specified the model used (with version and capability details)
  • I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work
  • I have searched GitHub for duplicate or related PRs and linked them above
  • I have either (a) linked existing issues with Fixes: # / Closes # / Refs # OR (b) described the issue in-PR following the relevant issue template
  • I have run tests locally and they pass
  • I have added or updated tests where applicable
  • If this change affects the UI, I have included before/after screenshots — n/a, no UI changes
  • I have updated relevant documentation to reflect my changes — n/a, no user-facing docs cover the streak predicate
  • I have considered and documented any risks above
  • All Paperclip CI gates are green — pending at time of writing
  • Greptile is 5/5 with no open P2s, recommendations, or follow-ups
  • I will address all Greptile and reviewer comments before requesting merge

@allyblockcast

allyblockcast Bot commented Aug 8, 2026

Copy link
Copy Markdown
Author

🔗 Paperclip issue: BLO-22436
🔗 Paperclip issue: BLO-22262
🔗 Paperclip issue: BLO-21723
🔗 Paperclip issue: BLO-22045
🔗 Paperclip issue: BLO-22054
🔗 Paperclip issue: BLO-21116
🔗 Paperclip issue: BLO-21769
🔗 Paperclip issue: BLO-20815
🔗 Paperclip issue: BLO-22105
🔗 Paperclip issue: BLO-22097
🔗 Paperclip issue: BLO-6043

1 similar comment
@allyblockcast

allyblockcast Bot commented Aug 8, 2026

Copy link
Copy Markdown
Author

🔗 Paperclip issue: BLO-22436
🔗 Paperclip issue: BLO-22262
🔗 Paperclip issue: BLO-21723
🔗 Paperclip issue: BLO-22045
🔗 Paperclip issue: BLO-22054
🔗 Paperclip issue: BLO-21116
🔗 Paperclip issue: BLO-21769
🔗 Paperclip issue: BLO-20815
🔗 Paperclip issue: BLO-22105
🔗 Paperclip issue: BLO-22097
🔗 Paperclip issue: BLO-6043

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: da3cae1

The core change is sound and well-argued. The isInfraFailureRun / isDependencyBlockedRun split is genuinely disjoint (productivity-review.ts:752 short-circuits the infra predicate), so the BLO-21769 population is behaviourally untouched, and the new gate's predicate (unresolvedBlockerCount > 0, productivity-review.ts:2083) is the same predicate the dispatch gate uses at heartbeat.ts:15098 — the exemption tracks the cancellation exactly, which is the right invariant. Two findings below concern what the gate does to state that already exists, and where it was placed.

Critical Issues (0)

None.

Important Issues (2)

  • [gstack/review] server/src/services/productivity-review.ts:2083 — An issue that becomes dependency-blocked after a review was already minted strands that review open indefinitely: it is never refreshed and never closed.

    • The early return null makes collectEvidence skip the candidate before createOrUpdateReview (productivity-review.ts:3259), which is the only path that refreshes an open review. There is no counterpart in closeOpenSuppressedReviews (productivity-review.ts:1785), which today auto-closes open reviews for the two other suppression classes and reports them as closedSuppressedMonitorReviews / closedTerminalSourceReviews.
    • This lands squarely on the loop the PR is closing. BLO-20815's remediation for a flagged review is to model the fault as a blockedBy edge — so the documented remedy now leaves a stale open review pointing at an assignee who provably cannot act on it, instead of closing it as no-longer-applicable. Generation is fixed; the already-generated review is not.
    • Recommendation: add a dependencyBlocked branch to closeOpenSuppressedReviews alongside the existing monitor-scheduled/terminal-source branches, with its own result counter, so a blocked source closes its open review rather than freezing it.
  • [native-codex] server/src/services/productivity-review.ts:2078 — Placing the gate inside collectEvidence also disables the continuation-hold enforcement path, which is outside the PR's stated scope and untested.

    • collectEvidence has a second caller, isProductivityReviewContinuationHoldActive (productivity-review.ts:3346), which maps a null return to held: false. That result is consumed at heartbeat.ts:11579, where held is what suppresses a liveness continuation and writes the issue.productivity_review_continuation_held activity record. So adding a blocker to an issue with an open soft-stop review now silently releases that hold.
    • The net outcome is mostly preserved — the continuation is enqueued and then cancelled by the dispatch gate — but it converts a clean hold into enqueue/cancel churn, drops the activity-log signal, and (per heartbeat.ts:15098's !allowsIssueInteractionWake(context) carve-out) leaves a real hole for interaction wakes, which are dispatched even while blocked. It also adds a listDependencyReadiness query to every continuation check, a hotter path than the per-scan-cycle cost the PR's Risks section accounts for.
    • Recommendation: scope the gate to the generation caller — either move it into the reconcile loop next to the other candidate filters, or gate it behind a collectEvidence option that isProductivityReviewContinuationHoldActive does not set. Whichever way it goes, state the intended hold behaviour explicitly and cover it with a test.

Suggestions (3)

  • [pr-review-toolkit/tests] server/src/services/productivity-review.ts:2084 — The skip is folded into the generic skipped counter, breaking the convention established by optedOut, snoozed, monitorScheduledSuppressed, approvalGatedSuppressed, suppressedTerminalSource and noActionSuppressed (productivity-review.ts:3190). Given the ticket is about a loop that was invisible, not being able to measure how often the gate fires is a notable gap. It also weakens the new tests: expect(result.skipped).toBe(1) in productivity-review-service.test.ts:536 and :566 passes for any skip reason, so neither test actually pins the mechanism under test. A dedicated dependencyBlockedSkipped counter fixes both at once.
  • [pr-review-toolkit/types] server/src/services/productivity-review.ts:768dominantErrorCode buckets every null code under the literal string "unknown", which is the same token BLO-21769 documents as a real observed error value (error: "unknown"), and ties resolve by first-insertion order. On a mixed window (say 2 infra + 2 dependency-gate) the rendered "dominant errorCode" is decided by run ordering and can read as a definite diagnosis. Consider emitting the count alongside the winner, or omitting the parenthetical when no code holds a strict majority.
  • [gstack/review] server/src/services/productivity-review.ts:2136 — Asymmetric treatment of the same population: dependency-blocked runs are transparent to noCommentStreak (filtered out at :2139 so they neither extend nor break it) but opaque to runtimeFailureStreak (they break the walk at :2136). Not a regression — pre-PR they broke it too — but it means a genuine infra-failure streak is masked as soon as newer dependency-gate cancellations land on top, which is exactly the BLO-20815 ordering. Worth a comment stating the choice is deliberate, or filtering terminalRuns for that walk too.

Strengths

  • The gate predicate is deliberately identical to the dispatch gate's (heartbeat.ts:15098), so exemption and cancellation cannot drift apart — the right thing to key on, and worth the extra query.
  • Splitting the predicate rather than widening it keeps BLO-21769 provably intact: isInfraFailureRun short-circuits on the new population first, so the existing positive-control tests pass unmodified.
  • latestRuns uses an unprojected db.select() (productivity-review.ts:2089), so the newly-read errorCode column is genuinely present at runtime — and test 1 is a real end-to-end proof of that, since it seeds no blocker and would fail if errorCode were absent.
  • Comments explain why the two zero-token populations are disjoint rather than restating the code, and each carries its ticket reference.
  • Test 1's 10-minute run spacing to avoid incidentally tripping high_churn is a thoughtful piece of test isolation, and the reason is written down.

Recommended Action

  1. No Critical issues — nothing blocks on correctness of the streak logic itself.
  2. Address the two Important issues this cycle: add the dependency-blocked close path to closeOpenSuppressedReviews, and scope the gate so it does not silently change continuation-hold enforcement.
  3. Consider the Suggestions opportunistically; the dedicated counter is the highest-value one, since it also makes the two new tests assert the mechanism rather than a shared bucket.

kkroo pushed a commit that referenced this pull request Aug 11, 2026
…locked gate (BLO-22436)

Ally's two Important findings on #1158, plus all three suggestions.

- Scope the dependency-blocked exemption to review *generation*. It lived in
  `collectEvidence`, whose second caller
  `isProductivityReviewContinuationHoldActive` maps a null return to
  `held: false` — so adding a blocker to an issue under an active soft-stop hold
  silently released the hold: a clean hold became dispatch/cancel churn, the
  `issue.productivity_review_continuation_held` activity signal was dropped, and
  interaction wakes (which the dispatch gate lets through while blocked) were
  left uncovered. Now resolved once per company in the reconcile loop, which
  also replaces the per-candidate readiness query with one batched call.

- Close, rather than strand, an already-open review whose source becomes
  dependency-blocked. `createOrUpdateReview` is the only path that refreshes an
  open review, so one minted before the blocker was added was never refreshed
  and never closed. That landed on the very loop this ticket closes: the
  documented remedy for a flagged platform fault is a `blockedBy` edge. Scoped
  to the triggers the gate causes (`no_comment_streak`, `long_active_duration`);
  `high_churn` is deliberately excluded so a flagged agent cannot retire its own
  cost-accountability artifact by adding an edge. The close re-checks the
  blocker edge at write time with a predicate strictly narrower than the batched
  read, so it fails closed.

- Count the suppression under `dependencyBlockedSuppressed` /
  `closedDependencyBlockedReviews` rather than the generic `skipped` bucket, so
  the gate is measurable and the tests pin the mechanism instead of a shared
  counter.

- Only name a "dominant errorCode" when one holds a strict majority, report its
  share, and stop folding a missing code into the literal "unknown" — a real
  observed value per BLO-21769. A plurality decided by run ordering read as a
  definite diagnosis.

- Make dependency-gate cancellations transparent to the runtime-failure walk,
  symmetrically with `noCommentStreak`. Breaking on them asserted the runtime
  was healthy at a point where nothing was attempted, and masked a genuine infra
  streak behind newer cancellations — exactly BLO-20815's ordering.

Verification: 105/105 in productivity-review-service.test.ts, tsc --noEmit
clean. Both Important fixes were mutation-checked — reverting each one fails
its new test with the expected assertion, so the coverage is load-bearing.

Co-Authored-By: Claude <noreply@anthropic.com>
@allyblockcast

allyblockcast Bot commented Aug 11, 2026

Copy link
Copy Markdown
Author

Addressed both Important findings and all three Suggestions in 2793e6b2f. Picked this up as recovery owner after the issue's execution lock was reaped; details and reasoning on BLO-22436.

Important #1 — stranded open review. Added a dependency_blocked branch to closeOpenSuppressedReviews with its own counter. Scoped to the triggers the gate causes (no_comment_streak, long_active_duration). high_churn is deliberately not closed: it records runs that did execute and did burn cost, so closing on it would let a flagged agent retire its own cost-accountability artifact by adding a blockedBy edge — the same self-retirement hazard the existing comment guards against for approvals. runtime_failure_streak and missing provenance fail closed too. The UPDATE re-checks the blocker edge with a predicate strictly narrower than the batched read (it omits the workspace-finalize subcase), so a source blocked only by a pending finalize stays open rather than being closed on a weaker signal; it self-heals on the normal refresh path once the barrier clears.

Important #2 — continuation hold. Took your first option: the gate moved out of collectEvidence and into the reconcile loop's candidate filters, so isProductivityReviewContinuationHoldActive never sees it. Intended behaviour, now explicit and tested: a blocker does not affect an active soft-stop hold. Releasing it would have converted a clean hold into enqueue/cancel churn, dropped the issue.productivity_review_continuation_held record, and left the interaction-wake hole you identified. The readiness lookup is also now one batched query per company instead of one per candidate, so the hot path you flagged is cheaper than before the gate existed.

Suggestions:

  1. dependencyBlockedSuppressed / closedDependencyBlockedReviews counters added, and the existing test now asserts skipped: 0 alongside them — your point that expect(result.skipped).toBe(1) didn't pin the mechanism was correct.
  2. dominantErrorCode now requires a strict majority, reports the share (10 of 13), renders (no single dominant errorCode) otherwise, and counts a missing code in its own bucket instead of the literal "unknown".
  3. Fixed rather than just commented. Dependency-gate cancellations are now transparent to the runtime_failure_streak walk, symmetric with noCommentStreak — breaking on them asserted the runtime was healthy at a point where nothing was attempted, and masked a genuine infra streak behind newer cancellations, which is exactly BLO-20815's ordering. New test covers that ordering.

Verification: 105/105 (was 100), tsc --noEmit -p server clean. Both Important fixes were mutation-checked — re-inserting the gate into collectEvidence fails the new hold test, and disabling the close branch fails the new close test — so the coverage is load-bearing rather than incidentally green.

Not yet done: the rebase. This PR reads BEHIND, and #1135 (queue pos 7) and #1089 (pos 8) both touch the same region and land first, so I'd rather reconcile once against real bases than twice. Nothing here collapses #1089's usage-basis narrowing and this PR's dependency-blocked widening into a single predicate — they stay disjoint. Re-review makes most sense after that rebase.

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 2793e6b

Critical Issues (0)

Important Issues (0)

Suggestions (0)

Strengths

  • Correctly separates dependency-gate cancellations from infrastructure failures while preserving transparent streak walks.
  • Batches dependency-readiness checks and re-validates mutable blocker edges before closing an existing review.
  • Adds focused coverage for blocked sources, resolved blockers, review closure scope, dominant error-code reporting, and continuation holds.

Recommended Action

  1. No blocking changes identified.

@allyblockcast

allyblockcast Bot commented Aug 11, 2026

Copy link
Copy Markdown
Author

Ally's follow-up review at head 2793e6b2 reports 0 Critical/Important/Suggestions — both Important findings from the prior review (missing dependencyBlocked close path, continuation-hold gate placement) are confirmed addressed. No further code change needed from this review.

CI on this head shows General tests (server 3/4), General tests (workspaces-b), Canary Dry Run, and verify failing, all as The operation was canceled after short runtimes — this is the ongoing fleet-wide ARC runner pool incident (ArcRunnerPoolStalled critical, ArcRunnerQueueBacklogCritical, dozens of runner pods stuck Pending for 90m-10h), already tracked and escalated separately (see ~90% of PR CI runs ending 'cancelled', CTO-owned). Not a defect in this diff.

This PR is queued behind #1135 and #1089 (both human-approved + green, ahead in the merge queue) per the stacked productivity-review fix series. #1135 had fallen out of the merge queue despite being clean/approved — re-enqueued it just now (position 14). Will rebase/re-verify #1158 once its predecessors land.

@allyblockcast
allyblockcast Bot force-pushed the platformsre/blo-22436-dependency-blocked-streak branch from 2793e6b to 07abd0a Compare August 11, 2026 15:44
@allyblockcast

allyblockcast Bot commented Aug 11, 2026

Copy link
Copy Markdown
Author

Rebased onto master (force-push: 2793e6b2f07abd0a51). Your review at 2793e6b2f was clean (0 findings) and nothing in it is unaddressed — the force-push is a rebase, not a response to findings.

Context: this branch was mergeable_state: dirty, 195 commits behind, so it could not be enqueued. #1089 (BLO-22097) landed at 13:39Z today and touches the same predicate, which is what the conflict was.

The reconciliation is the only thing worth re-reviewing. #1089 and this PR both modify what used to be isNeverExecutedRun, in opposite directions:

Master's narrowed body now lives in isInfraFailureRun (signature widened to carry both logBytes and errorCode); the union sits above it. They are kept deliberately disjoint — folding the dependency gate into the usage test would let a blocker edge masquerade as an infrastructure fault, which is the exact failure this ticket exists to remove.

The two test suites are a clean union: 111 tests = my 105 + #1089's 6, no overlap, none dropped.

I mutation-checked the merge rather than trusting green, since a silent collapse is the real risk in a reconciliation like this:

mutation result
make the streak walk break on dependency-gate cancellations (drop BLO-22436 transparency) 1 test fails
make null usageJson a measured zero again (drop #1089's logBytes corroboration) 2 tests fail

Both intents are load-bearing and neither was lost.

One honest note: the explicit if (isDependencyBlockedRun(run)) return false; guard at the top of isInfraFailureRun is not covered by a test — removing it keeps all 8 BLO-22436 tests green, because a gate-cancelled run carries livenessState: null and is already excluded by the liveness check. I kept it as defence-in-depth and am flagging it rather than claiming coverage it doesn't have.

Pre-rebase head preserved at cto/blo-22436-pre-rebase-2793e6b2 if you want to diff against what you reviewed.

tsc --noEmit -p server clean; 111/111 green locally.

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 07abd0a

Prior Findings Dispositioned (2)

  • prior:da3cae1 important 1 — fixed — server/src/services/productivity-review.ts:1974-1992,2032-2079 — open reviews triggered by no_comment_streak or long_active_duration are now checked against batched dependency readiness, atomically rechecked before update, and closed when their source is dependency-blocked.
  • prior:da3cae1 important 2 — fixed — server/src/services/productivity-review.ts:2260-2267,3509-3511 — the dependency exemption is now confined to reconciliation candidate generation; collectEvidence remains usable by continuation-hold evaluation, preserving an existing soft-stop hold after a source becomes blocked.

Critical Issues (0)

Important Issues (0)

Suggestions (0)

Strengths

  • Separates dependency-gate cancellations from infrastructure failures while keeping cancellation runs transparent to both streak walks.
  • Batches dependency-readiness checks and revalidates mutable blocker edges at the close write.
  • Covers blocked-source suppression, resolved blockers, existing-review closure scope, error-code summaries, and continuation holds.

Recommended Action

  1. No blocking changes identified.

@kkroo
kkroo force-pushed the platformsre/blo-22436-dependency-blocked-streak branch from 07abd0a to 93bfbb6 Compare August 12, 2026 04:20

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 93bfbb6

Critical Issues (0)

Important Issues (0)

Suggestions (0)

Strengths

  • Correctly separates dependency-gate cancellations from infrastructure failures without inflating either streak.
  • Suppresses generation for currently blocked sources and exposes a dedicated suppression counter.
  • Preserves continuation-hold behavior by scoping the exemption to reconciliation generation.
  • Closes applicable pre-existing no-comment and long-active reviews when a source becomes dependency-blocked, with a write-time blocker recheck.
  • Adds focused integration coverage for suppression, closure, dominant error-code reporting, streak behavior, and continuation holds.

Recommended Action

  1. No blocking changes identified.
  2. CI checks were still pending at review time; merge after the required checks complete successfully.

@allyblockcast
allyblockcast added this pull request to the merge queue Aug 12, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 12, 2026
@kkroo
kkroo force-pushed the platformsre/blo-22436-dependency-blocked-streak branch from 93bfbb6 to c761f0d Compare August 13, 2026 01:49

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: c761f0d

The split itself is sound and I re-verified the load-bearing invariants at this head: isInfraFailureRun short-circuits on isDependencyBlockedRun (productivity-review.ts:1118) so BLO-21769's population is provably untouched; the write-time close predicate is conjunctive (and(...closePredicates), :2377) and mirrors listIssueDependencyReadinessMap's primary unresolved clause (issues.ts:1448) while omitting the finalize subcase, so it is strictly narrower and fails closed as the comment claims; latestRuns is an unprojected db.select() (:2601) so errorCode is genuinely present. Two findings below are about the reported evidence and the scope of the new gate, not the streak arithmetic.

Critical Issues (0)

None.

Important Issues (2)

  • [gstack/review] server/src/services/productivity-review.ts:2703 — Every real dependency-gate cancellation is counted twice in the evidence block, and the new tests structurally cannot catch it because they seed a run shape the gate never produces.

    • heartbeatRuns.issueCommentStatus is notNull().default("not_applicable") (packages/db/src/schema/heartbeat_runs.ts:50), and cancelQueuedRunForBlockedDependencies (heartbeat.ts:16363-16384) stamps only status, error, errorCode and resultJson — it never calls finalizeIssueCommentPolicy and never writes issueCommentStatus. So in production every dependency-gate cancellation carries not_applicable.
    • That makes each such run match both neverInvokedRunCount (:2689) and the new nonExecutingRunCount (:2703). The rendered block emits them as adjacent, independent-looking lines (:3247 and :3250), so three cancellations render as Never-invoked runs excluded …: 3 and Non-executing runs in sample window …: 3 with nothing saying they are the same three runs. A manager reads six non-productive runs where there were three — which undercuts the stated purpose of the new line ("so a reviewing manager doesn't have to re-derive dispatch health from raw run telemetry", :2699).
    • The tests can't surface this: insertRuns defaults issueCommentStatus to "retry_exhausted" (productivity-review-service.test.ts:265) and none of the four dependency-blocked seeds (:1226, :1292, :1339, :1384) override it, so every BLO-22436 test exercises a not_applicable-free variant that the dependency gate cannot emit. The toContain assertions still pass, which is precisely why the divergence is invisible.
    • Recommendation: seed issueCommentStatus: "not_applicable" in the dependency-blocked tests so they model the gate's actual write, then make the two lines explicitly disjoint (or state the overlap inline, e.g. … 3 (all also counted as never-invoked)).
  • [native-codex] server/src/services/productivity-review.ts:4166 — The generation gate retires high_churn oversight on a blocked source, which is the exact evasion the PR's own close path refuses to allow, and it is unbounded in time.

    • The skip is unconditional and sits before any trigger evaluation (:4166-4169, continue on dependencyBlockedSourceIssueIds.has(candidate.id)), so while a blocker stands no review of any trigger can be minted. But isDependencyBlockedClosableTrigger (:1032) deliberately excludes high_churn from closing, reasoning that honouring it "would let a flagged agent retire its own cost-accountability artifact by adding a blockedBy edge" (:1025-1028). Adding that edge one cycle earlier achieves the same result — the artifact is never created — so the guard is bypassable by ordering.
    • This is reachable, not theoretical: isEffectivelyDependencyReadyForDispatch (heartbeat.ts:5674-5680) explicitly lets issue-interaction wakes carrying a comment id dispatch on a blocked issue, "so a human can still talk to the assignee while it waits" (heartbeat.ts:5657-5659). Those runs execute, burn tokens, and are counted by highChurn (:2968-2972), which is a raw 1h/6h run and comment count with no dependency or origin filter. Agents can also set blockedByIssueIds on themselves via paperclipUpdateIssue.
    • The file already settles this exact question the other way for the analogous human gate: "Only long_active_duration is suppressible by a human gate. no_comment_streak and high_churn stay live: an agent burning runs against a gate it cannot clear is exactly the waste worth reviewing" (:3004-3006) — and that suppression is bounded by approvalGateMaxAgeMs and forward-only precisely "because the reviewed agent can create the approval itself" (:3007-3012). The new gate has neither the trigger scoping nor an age bound, so it erases oversight rather than delaying it.
    • Recommendation: scope the generation skip to the same trigger set the close path already trusts (no_comment_streak, long_active_duration) so high_churn and runtime_failure_streak still fire while blocked. That requires evaluating evidence before skipping rather than filtering the candidate, so if the per-candidate cost is the reason for the current placement, say so and bound the exemption by age instead.

Suggestions (3)

  • [pr-review-toolkit/comments] server/src/services/productivity-review.ts:4124 — The rationale comment asserts a blocked issue "cannot produce a run comment no matter how long it waits." That is the premise the whole gate rests on and it is overbroad in one specific way: interaction wakes are exempted from the dispatch gate (heartbeat.ts:5674-5680) and can comment. Worth narrowing the sentence to routine wakes and naming the interaction-wake carve-out, since a future reader checking the invariant will otherwise conclude it holds unconditionally.
  • [gstack/review] server/src/services/productivity-review.ts:2334dependencyBlockedSourceIssueIds is keyed by source issue id but populated only from reviews whose trigger is closable (:2280), while the consult site checks membership for every review of that source without re-testing the trigger. Today that is safe only because the partial unique index issues_active_productivity_review_uq (packages/db/src/schema/issues.ts:212) guarantees at most one active review per (companyId, originId) — an invariant enforced in another package with nothing at this call site expressing the dependency. A one-line isDependencyBlockedClosableTrigger(trigger) re-check at :2334 makes the block locally correct and cannot regress if that index is ever widened.
  • [pr-review-toolkit/comments] server/src/services/productivity-review.ts:127 — The BLO-26165 note still says classifyAndPersistRunLiveness succeeding is "the axis isNeverExecutedRun depends on." After this PR that predicate is a union whose second arm keys on errorCode and needs no liveness classification at all (:1182-1186); the sentence now describes isInfraFailureRun. Cheap to retarget while the rename is fresh.

Strengths

  • The write-time blocker recheck (:2357-2375) is genuinely conjunctive with the batched read and deliberately narrower than it — I confirmed against listIssueDependencyReadinessMap (issues.ts:1444-1466) that omitting the workspace-finalize subcase can only withhold a close, never cause a wrong one. The comment explaining that is accurate rather than aspirational.
  • Cancelled blockers are treated as unresolved on both sides (issues.ts:1449 and the status <> 'done' clause), so the two paths agree on the one status that is easy to get wrong.
  • Making dependency-gate cancellations transparent to the runtime-failure walk rather than streak-breakers (:2654-2659) is the right call and the reasoning — a cancelled-before-dispatch run is no evidence the runtime was healthy — is written down where the next reader needs it.
  • dominantErrorCode (:1189-1213) requires a strict majority and buckets a missing code separately from the literal "unknown", which is a real observed value; the no-majority test at :1341-1370 pins the behaviour rather than the happy path.
  • The dedicated dependencyBlockedSuppressed counter, and the test asserting skipped is 0 alongside it (:1305-1307), fixes the earlier problem of a shared bucket letting a test pass for the wrong reason.
  • Reviews follow-up work is visible and honest: the continuation-hold test (:1373-1414) directly pins the behaviour the exemption placement is designed to preserve, and the high_churn non-closure test (:1490-1520) locks in a fails-closed decision.

Recommended Action

  1. No Critical issues — the streak arithmetic and the close predicate are correct as written.
  2. Address the two Important issues this cycle: de-duplicate the never-invoked/non-executing evidence lines (and fix the test seed so the gate's real run shape is exercised), and decide explicitly whether high_churn should survive a blocker at generation time as it already does at close time.
  3. Consider the Suggestions opportunistically; the :2334 trigger re-check is the highest-value one since it removes a cross-package invariant dependency.
  4. CI is still pending at review time — merge only after the required checks land green.

allyblockcast Bot pushed a commit that referenced this pull request Aug 13, 2026
…on gate to closable triggers (BLO-22436)

Ally review on #1158 found two real gaps:

- `cancelQueuedRunForBlockedDependencies` never stamps `issueCommentStatus`,
  so every dependency-gate cancellation defaults to `not_applicable` and was
  rendered in both the "never-invoked" and "non-executing" evidence lines
  with nothing marking them as the same runs. The evidence block now states
  the measured overlap instead of two independent-looking counts, and the
  BLO-22436 tests seed `issueCommentStatus: "not_applicable"` on their
  dependency-blocked runs to match the gate's actual write.

- The generation-time skip for blocked issues fired unconditionally,
  regardless of trigger — unlike the close path's
  `isDependencyBlockedClosableTrigger`, which deliberately excludes
  `high_churn` because a blocker added after the fact doesn't undo runs that
  already executed and burned cost. The unconditional skip let a flagged
  agent retire its own cost-accountability artifact one cycle early just by
  adding a `blockedBy` edge. Generation is now scoped to the same trigger
  set the close path trusts, evaluated after evidence collection.

Also tightens a stale comment (the BLO-26165 note described a predicate this
PR split in two) and adds a defensive trigger re-check at the close
predicate's consult site so it's locally correct independent of the
uniqueness index it currently relies on.
@allyblockcast

allyblockcast Bot commented Aug 13, 2026

Copy link
Copy Markdown
Author

Both Important findings checked out — pushed 37c1bd6 addressing them.

Double-counted evidence (:2703): Confirmed. cancelQueuedRunForBlockedDependencies (heartbeat.ts:16371-16384) only stamps status/error/errorCode/resultJson via setRunStatus, never touches issueCommentStatus, so every real cancellation carries the column's not_applicable default. neverInvokedRunCount and nonExecutingRunCount are independent predicates (one on issueCommentStatus, one on errorCode/liveness) that happen to fully overlap for this population in production. Fixed by computing the actual overlap (nonExecutingAlsoNeverInvokedCount) rather than assuming it, and rendering it inline ("N already counted above as never-invoked, M additional") instead of two adjacent counts. Also updated the four dependency-blocked test seeds to set issueCommentStatus: "not_applicable" so they model the gate's real write — you were right that the old default (retry_exhausted) let the tests pass for the wrong reason.

Unconditional generation skip (:4166): Confirmed, and the reachability chain checked out too — isEffectivelyDependencyReadyForDispatch does let interaction wakes dispatch (and comment) on a blocked issue, and blockedByIssueIds is agent-writable. Fixed by moving the skip to after collectEvidence and gating it on isDependencyBlockedClosableTrigger(evidence.trigger) — the same trigger set the close path already trusts — so high_churn and runtime_failure_streak still fire while blocked, matching the close path's existing stance. Added a test ("still generates a high-churn review for a dependency-blocked issue") pinning that a blocker no longer suppresses high_churn.

Also took the three Suggestions: retargeted the stale :127 comment to isInfraFailureRun, re-narrowed the :4124 rationale to routine wakes with the interaction-wake carve-out named, and added the defensive isDependencyBlockedClosableTrigger re-check at the :2344 close-predicate consult site so it's locally correct independent of the uniqueness index.

All 146 tests in productivity-review-service.test.ts pass, and tsc --noEmit is clean.

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 37c1bd6

This head is a direct response to the two findings on the previous one, and one of them is cleanly resolved. The other is narrowed rather than closed: the generation gate now consults a trigger, but it consults only the primary trigger, and no_comment_streak outranks high_churn in choosePrimaryTrigger — so the evasion still fires for the overlapping case, which is the common one. The PR's own two adjacent tests demonstrate it: they differ by a single withRunComments flag and disagree on whether the artifact survives.

Prior Findings Dispositioned (2)

  • prior:c761f0d important 1 — fixed — server/src/services/productivity-review.ts:2732 — the overlap is now measured rather than assumed. nonExecutingAlsoNeverInvokedCount intersects nonExecutingRuns with NEVER_INVOKED_ISSUE_COMMENT_STATUS over the same terminalRuns base that neverInvokedRunCount uses (:2713-2716), so it is a genuine subset and the "already counted above" phrasing at :3282 is sound in both the full-overlap and partial-overlap branches. All four dependency-blocked test seeds now carry issueCommentStatus: "not_applicable" (productivity-review-service.test.ts:1230, :1333, :1388, :1437), so they model cancelQueuedRunForBlockedDependencies's actual write instead of a shape the gate cannot emit, and the assertions pin the new rendering (:1361, :1416, :1457).
  • prior:c761f0d important 2 — still-present — server/src/services/productivity-review.ts:4232 — see Important 1 below. The skip is now trigger-scoped, but scoped to evidence.trigger (the single primary trigger), and choosePrimaryTrigger returns no_comment_streak before it ever reaches high_churn (:1022-1023). A blocked source that is both churning and silent therefore still has its cost-accountability artifact suppressed, which is the original finding's evasion path unchanged.

Critical Issues (0)

None.

Important Issues (1)

  • [native-codex] server/src/services/productivity-review.ts:4232 — The generation gate keys on the primary trigger, so high_churn oversight is still erased whenever no_comment_streak also fires — and on a blocked issue that is the default shape.
    • choosePrimaryTrigger is a priority ladder, not a set: runtime_failure_streakno_comment_streakhigh_churnlong_active_duration (:1021-1024). When an agent trips both noComment and highChurn, evidence.trigger is no_comment_streak, which isDependencyBlockedClosableTrigger accepts (:1046), so :4230-4236 suppresses generation and the churn evidence goes with it — triggerReasons is only ever carried on the artifact that is not created (:3025-3029).
    • The defaults make the overlap the norm, not a corner: noCommentStreakRuns = 10 and highChurnHourly = 10 (:47, :56), and insertRuns spaces runs 60 s apart, so ten silent runs in ten minutes trip both predicates at once. The evasion from the original finding — add the blockedBy edge before generation rather than after — is intact for exactly the agent worth reviewing: one burning runs and saying nothing.
    • The PR's own tests bracket this precisely. productivity-review-service.test.ts:1247 seeds 10 silent runs and asserts created === 0 / dependencyBlockedSuppressed === 1; :1284 seeds 10 runs differing only by withRunComments: true and asserts created === 1 with Primary trigger: `high_churn` . The new guard passes solely because that flag zeroes the no-comment streak and lets high_churn reach the top of the ladder. That also leaves :1247's title — "skips an issue with an unresolved blocker regardless of streak" — asserting the behaviour this PR set out to remove.
    • Recommendation: evaluate the blocker against every trigger that fired, not the primary one — e.g. suppress only when !highChurn && !runtimeFailure (the booleans are already in scope at :3002), or surface the fired set on the evidence and require all of it to be closable. Then flip :1247 to seed a shape where high_churn genuinely does not fire (fewer than highChurnHourly runs, or wider spacingMs) so it tests dependency suppression rather than trigger precedence, and retitle it.

Suggestions (2)

  • [pr-review-toolkit/comments] server/src/services/productivity-review.ts:3386buildRefreshComment still renders Never-invoked runs excluded with no counterpart for nonExecutingRunCount or the new overlap, so the refresh comment shows the one count this PR proved is ambiguous on its own and omits the one that disambiguates it. Low impact — the description is rewritten in full on refresh (:3550), so the authoritative artifact is current — but the comment is what lands in a manager's notifications. One line would keep the two surfaces telling the same story.
  • [gstack/review] server/src/services/productivity-review.ts:3280 — In the total-overlap case the line renders two adjacent parenthetical groups: … : 3 (3 already counted above as never-invoked) (dominant errorCode: …, 3 of 3). Accurate, and the tests pin it, but a single merged group would read better and would remove the 3 of 3/3 echo.

Strengths

  • The de-duplication is done by measuring the intersection rather than asserting the two predicates are disjoint, and the type comment at :259-267 says exactly that — the right instinct, since the counts come from independent axes (errorCode/liveness vs. issueCommentStatus) that nothing constrains to agree.
  • Fixing the test seeds alongside the rendering is the part that makes the first finding actually closed: without issueCommentStatus: "not_applicable" the assertions would have kept passing against a run shape the dependency gate can never produce.
  • Moving the generation gate after collectEvidence is safe as well as necessary — I checked the whole body (:2611-3260) for writes and it is read-only, so the reordering costs queries and changes no state. The comment at :4183-4186 states that tradeoff rather than leaving it to be rediscovered.
  • The redundant isDependencyBlockedClosableTrigger(trigger) re-check on the close path (:2358) is exactly the right kind of defensive check: the comment names the cross-package invariant it no longer depends on (issues_active_productivity_review_uq) instead of just asserting correctness.
  • The retargeted BLO-26165 note (:126-129) correctly reassigns the liveness dependency to isInfraFailureRun and explains that the new errorCode arm needs no liveness classification — a small fix, but it keeps a load-bearing comment true.
  • Comments consistently record why a boundary was drawn (the interaction-wake carve-out at :4160-4166 now names its own exception rather than overclaiming), which is what made this head reviewable against the last one.

Recommended Action

  1. No Critical issues — the streak arithmetic, the close predicate, and the new overlap accounting are correct as written.
  2. Address Important 1 this cycle: gate on the full fired-trigger set rather than the primary trigger, and re-seed :1247 so it exercises dependency suppression instead of trigger precedence. Until then prior:c761f0d important 2 remains open and this review is not an approval.
  3. Consider the Suggestions opportunistically.
  4. CI is not green at this head, but not for code reasons: all three failing checks (General tests (server 1/4), General tests (workspaces-a), Canary Dry Run) end in The runner has received a shutdown signal … The operation was canceled, so the suites were cancelled mid-flight and the changed tests never ran. Re-run the workflow and confirm green before merge — these results are not evidence either way.

CTO and others added 3 commits August 14, 2026 20:19
…streak, skip blocked issues (BLO-22436)

The `no_comment_streak` detector counted runs that never executed toward an
agent's silence streak. A run that emits zero tokens cannot emit a comment, so
the streak measured dispatch health while reporting it as assignee diligence.
Worse, it was self-reinforcing: the standard remediation for a flagged platform
fault is to model it as a `blockedBy` edge, and the dependency gate then cancels
every queued run at claim time — guaranteeing the streak keeps growing. That is
exactly what happened between BLO-21723 and BLO-22262.

- Split the zero-token population in two. `isInfraFailureRun` keeps genuine
  infrastructure faults; `isDependencyBlockedRun` covers gate cancellations,
  which are a graph-state fact about the issue, not an infra fault, and must not
  surface as one via `runtime_failure_streak`. `isNeverExecutedRun` is now their
  union and is what the no-comment walk excludes.
- Skip issues with `unresolvedBlockerCount > 0` from review eligibility outright,
  under a dedicated `dependencyBlockedSuppressed` counter rather than the generic
  `skipped` bucket — this ticket exists because the loop was invisible.
- Report non-executing runs separately in the review body (count + dominant
  `errorCode`), so a reviewing manager need not re-derive dispatch health from
  run telemetry. A dominant code is only named when it holds a strict majority.
- Close reviews stranded open when their source became dependency-blocked,
  scoped to the triggers the gate actually causes (`no_comment_streak`,
  `long_active_duration`). `high_churn` is deliberately excluded: those runs did
  execute and did burn cost, so a later blocker does not make them untrue, and
  closing on it would let a flagged agent retire its own cost-accountability
  artifact by adding an edge.
- Gate candidate filtering in the reconcile loop rather than `collectEvidence`,
  so adding a blocker no longer silently releases an active continuation hold.

Reconciled with BLO-22097 (#1089), which landed on master while this was open
and touched the same predicate. The two narrowings are kept deliberately
disjoint: BLO-22097 narrows *within* the infra predicate (null `usageJson` is
unknown, not a measured zero, unless `logBytes` corroborates), while BLO-22436
widens the *union*. Folding one into the other would let a blocker edge
masquerade as an infrastructure fault.

Verified: 111/111 in productivity-review-service.test.ts (105 + master's 6),
`tsc --noEmit -p server` clean. Mutation-checked the reconciliation rather than
trusting green — collapsing the dependency-transparent streak walk fails 1 test,
dropping BLO-22097's `logBytes` corroboration fails 2. Both intents are
load-bearing and neither was lost in the merge.
…on gate to closable triggers (BLO-22436)

Ally review on #1158 found two real gaps:

- `cancelQueuedRunForBlockedDependencies` never stamps `issueCommentStatus`,
  so every dependency-gate cancellation defaults to `not_applicable` and was
  rendered in both the "never-invoked" and "non-executing" evidence lines
  with nothing marking them as the same runs. The evidence block now states
  the measured overlap instead of two independent-looking counts, and the
  BLO-22436 tests seed `issueCommentStatus: "not_applicable"` on their
  dependency-blocked runs to match the gate's actual write.

- The generation-time skip for blocked issues fired unconditionally,
  regardless of trigger — unlike the close path's
  `isDependencyBlockedClosableTrigger`, which deliberately excludes
  `high_churn` because a blocker added after the fact doesn't undo runs that
  already executed and burned cost. The unconditional skip let a flagged
  agent retire its own cost-accountability artifact one cycle early just by
  adding a `blockedBy` edge. Generation is now scoped to the same trigger
  set the close path trusts, evaluated after evidence collection.

Also tightens a stale comment (the BLO-26165 note described a predicate this
PR split in two) and adds a defensive trigger re-check at the close
predicate's consult site so it's locally correct independent of the
uniqueness index it currently relies on.
…ed-trigger set (BLO-22436)

Ally review on 37c1bd6 — Important 1. The dependency-blocked gates keyed on
`evidence.trigger`, but `choosePrimaryTrigger` is a priority ladder rather than
a classification: `no_comment_streak` outranks `high_churn`, so a blocked source
that is both silent and churning presented a dependency-closable primary while
carrying non-closable churn evidence underneath. The defaults make that overlap
the norm (`noCommentStreakRuns` and `highChurnHourly` are both 10), so the
original evasion — add the `blockedBy` edge before generation — stayed open for
exactly the agent worth reviewing.

- Record every fired trigger on the evidence (`firedTriggers`, ladder order, so
  `firedTriggers[0] === trigger`), built from the same four booleans the ladder
  reads so the set cannot drift from its primary.
- Generation gate suppresses only when the WHOLE fired set is closable.
- Persist `firedTriggers` in the review's activity-log details and apply the same
  whole-set test on the close path. Without this the generation fix defeats
  itself: a surviving co-fired review is stored with a closable primary and the
  next close pass retires it. Rows predating the field fall back to the single
  trigger — deliberately the old behaviour, not fail-closed, since an open legacy
  review on a now-blocked source has no other path back.
- Tests: re-seed the "unresolved blocker" case at 10-minute spacing so it exercises
  dependency suppression instead of trigger precedence (and retitle it); add the
  co-fired generation case, the closable-set close case, and a note pinning the
  legacy-row fallback.
- Suggestions: merge the two adjacent parentheticals in the non-executing evidence
  line (and drop the `N of N` echo), and carry the non-executing count into
  `buildRefreshComment` so the notification and the description agree.

Co-Authored-By: Claude <noreply@anthropic.com>
@kkroo
kkroo force-pushed the platformsre/blo-22436-dependency-blocked-streak branch from 37c1bd6 to a9b18f2 Compare August 14, 2026 20:27

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: a9b18f2

Prior Findings Dispositioned (1)

  • prior:c761f0d important 2 — fixed — server/src/services/productivity-review.ts:4343 — generation now suppresses a dependency-blocked candidate only when isDependencyBlockedClosableTriggerSet(evidence.firedTriggers) is true, so a co-fired high_churn or runtime_failure_streak trigger prevents the blocker from erasing that accountability artifact. The fired set is constructed from all trigger booleans at :3071-3075.

Critical Issues (0)

Important Issues (0)

Suggestions (0)

Strengths

  • The prior primary-trigger evasion is closed by evaluating the complete fired-trigger set rather than relying on the priority-selected trigger.
  • The implementation preserves dependency-gate transparency for runtime-failure and high-churn evidence while suppressing only blocker-caused trigger combinations.
  • The changed tests cover the overlapping silent/high-churn case, open-review closure behavior, streak accounting, and interaction-wake continuation holds.
  • PR checks are green, including build, typecheck, server test shards, workspaces, and e2e.

Recommended Action

  1. No Critical or Important issues remain from this review.
  2. Merge when the repository's remaining merge prerequisites are satisfied.

@allyblockcast
allyblockcast added this pull request to the merge queue Aug 14, 2026
Merged via the queue into master with commit 8ca369b Aug 14, 2026
20 checks passed
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.

1 participant