Skip to content

fix(review): recover lost panel-retrigger clicks instead of silently erasing them (#9000) - #9584

Merged
loopover-orb[bot] merged 1 commit into
mainfrom
fix/panel-retrigger-recovery-9000
Jul 28, 2026
Merged

fix(review): recover lost panel-retrigger clicks instead of silently erasing them (#9000)#9584
loopover-orb[bot] merged 1 commit into
mainfrom
fix/panel-retrigger-recovery-9000

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Closes #9000

Summary

The lost-click half of #9000, completing the issue. Fix (2) — exhaustive skip auditing — was verified already-landed (my comment on the issue), and its CI enforcement merged as #9583. This PR is fix (1) and root-cause (a).

The defect

Three checkbox states looked identical to a maintainer:

state box after intent honored?
processed unticked (panel republished) yes
deferred for CI stays ticked yes — #7626 pending marker
delivery lost stays ticked no — nothing recorded, nothing will ever happen

Only the third is broken (three observed live on #8972), and it is unrecoverable from our own state alone: the tick exists only in the live comment body, because the webhook that would have told us about it never became a job.

Worse: the panel renderer emits its checkboxes unticked unconditionally, so the next republish would overwrite the ticked box — erasing the only evidence of the click, on the exact pass best placed to honor it.

The fix, at zero additional API cost

createOrUpdateIssueCommentWithMarker already fetches the existing comment for its marker search. It now returns that pre-overwrite body, and every panel republish — webhook pass, re-gate sweep, backlog convergence — doubles as the detector. No new sweep, no new persistence, no extra GitHub calls; the issue's "within one sweep interval" acceptance is bounded by the republish cadence that already exists.

On detection (ticked box + no recent processing + no pending marker), recovery:

  1. records github_app.pr_panel_retrigger_recovered — the named reason orb(observability): invariant — every decision or non-decision records a named reason; no silent gates on dispatch paths #9003's invariant demands, doubling as the loop guard
  2. persists the existing fix(review): a manual re-run request loses its forceAiReview intent when deferred for pending CI #7626 pending marker, so the next review pass consumes it as forceAiReview exactly like a deferred click
  3. enqueues an agent-regate-pr job immediately (with the queue: no 'current focus' concept exists, and oldest-first ordering is defeated by 5 of 8 regate producers omitting prCreatedAt #9499 prCreatedAt sort key) — a PR with a lost click is precisely the PR that cannot count on a natural pass

False-positive proofing — the part that took the design care

  • A pass that is the retrigger (forceAiReview) skips detection: overwriting its own tick is the receipt acknowledgement.
  • A raced delivery is caught by an actor-agnostic audit lookup (new countAuditEventsForTargetSincehasAuditEventForDelivery is deliberately actor+delivery-keyed, the wrong shape when the delivery never existed).
  • A deferred click is guarded by ordering, not luck: the retrigger handler now marks the pending marker before its readiness check (consuming it right back on the immediate path), so the CI-wait placeholder inside that very pass sees the marker and stands down. A crash between mark and consume degrades to one extra forced review — the safe direction for an explicit user click.

The #7626 sibling test's "never creates a pending marker" assertion is updated to the invariant it documents (no pending intent survives the immediate path) rather than its old mechanism (key absence), since consumption can leave the one-shot consumed sentinel on adapters without a real delete.

Acceptance, as the issue states it

A ticked rerun checkbox always results in either a fresh review or an explicit, named skip event within one sweep interval — never silence.

Holds: processed → fresh review; deferred → marker honored on CI settle; lost → recovered, named, and re-reviewed on the enqueued pass. The end-to-end test pins the full loop, including that the recovery pass does not re-diagnose its own republish and that the lost click buys exactly one fresh review.

Testing

Eight tests in the new suite (regression, end-to-end, both race guards, ghost PR, two no-ops, empty-list guard) plus three github-comments assertions extended to pin previousBody — including that it is the posted body, not the re-render, on the byte-identical skip path.

Changed lines across all three files: 0 uncovered statements, 0 uncovered branches. Full suite: 24,039 passed.

…erasing them (#9000)

The lost-click half of #9000, completing the issue (the skip-auditing half landed
earlier; its enforcement went in as #9583).

THE DEFECT. Three checkbox states looked identical to a maintainer: processed (panel
republished, box reset), deferred for CI (box stays ticked, honored later via the
#7626 pending marker), and DELIVERY LOST (box stays ticked, nothing recorded, nothing
will ever happen -- three such losses observed live on #8972, 2026-07-26). Only the
third is broken, and it is unrecoverable from our own state alone: the tick exists
only in the live comment body, because the webhook that would have told us about it
never became a job. Worse, the next panel republish -- whose renderer emits its
checkboxes unticked unconditionally -- would OVERWRITE the ticked box, erasing the
only evidence of the click on the exact pass best placed to honor it.

THE FIX, at zero additional API cost. createOrUpdateIssueCommentWithMarker already
fetches the existing comment for its marker search; it now returns that pre-overwrite
body. Every panel republish (webhook pass, re-gate sweep, backlog convergence) doubles
as the detector: a ticked rerun box in the body being replaced, with no recent
processing and no pending marker, IS a lost click. Recovery then:

  - records github_app.pr_panel_retrigger_recovered (the named reason #9003 demands,
    and the loop guard against re-detection),
  - persists the #7626 pending marker, so the next review pass consumes it as
    forceAiReview exactly like a deferred click, and
  - enqueues an agent-regate-pr job immediately (with the #9499 prCreatedAt sort key)
    -- a PR with a lost click is precisely the PR that cannot count on a natural pass.

No new sweep, no new persistence: the issue's "within one sweep interval" acceptance
is bounded by the republish cadence that already exists.

FALSE-POSITIVE PROOFING, the part that took the design care:

  - A pass that IS the retrigger (forceAiReview) skips detection -- overwriting its
    own tick is the receipt acknowledgement, not a loss.
  - A recently-processed retrigger (actor-agnostic audit lookup, new
    countAuditEventsForTargetSince -- hasAuditEventForDelivery is deliberately
    actor+delivery-keyed, the wrong shape when the delivery never existed) means the
    delivery raced the pass rather than being lost.
  - A DEFERRED click is guarded by ordering, not luck: the retrigger handler now marks
    the pending marker BEFORE its readiness check (consuming it right back on the
    immediate path), so the CI-wait placeholder inside that very pass sees the marker
    and stands down. A crash between mark and consume degrades to one extra forced
    review -- the safe direction for an explicit user click.

The #7626 sibling test's "never creates a pending marker" assertion is updated to the
invariant it documents ("no pending INTENT survives the immediate path") rather than
its old mechanism (key absence), since consumption can leave the one-shot consumed
sentinel behind on adapters without a real delete.

TESTS. Eight in the new suite: the #8972 regression (natural pass recovers the tick:
event + marker + job with the sort key), full end-to-end (the recovery job's pass
consumes the marker, spends exactly one fresh AI review, and does not re-diagnose its
own republish), the raced-delivery guard, the deferred-click guard (CI genuinely
pending, marker untouched afterward), the no-head ghost PR (marker guard skipped, job
omits the optional sort key), unticked and first-publish no-ops, and the empty
event-type-list guard. Plus the three github-comments assertions extended to pin
previousBody, including that it is the POSTED body, not the re-render, on the
byte-identical skip path. Changed lines: 0 uncovered statements, 0 uncovered branches.
@loopover-orb

loopover-orb Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-28 19:37:17 UTC

6 files · 1 AI reviewer · no blockers · readiness 100/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This closes issue #9000's lost-click half: `createOrUpdateIssueCommentWithMarker` (src/github/comments.ts:168-193) now surfaces the pre-overwrite comment body as `previousBody`, and every panel-republish call site feeds that into a new `maybeRecoverLostPanelRetrigger` (src/queue/processors.ts:4785) that detects a ticked-but-unprocessed retrigger checkbox and recovers it by persisting the #7626 pending marker plus enqueuing an immediate `agent-regate-pr` job. The reordering in `maybeProcessPrPanelRetrigger` (marker now set before the readiness check, then one-shot consumed on the immediate-ready branch) correctly prevents the CI-wait placeholder's own republish from misdiagnosing a legitimately-deferring click as lost, and the `forceAiReview === true` / recent-audit-event / pending-marker guards appear to compose correctly across the three hook sites so a single lost click is recovered exactly once per pass. Test coverage is unusually exhaustive (recovered, raced-processed, deferred, ghost-PR, unticked, no-prior-comment, empty event-type list) and all CI checks passed; no schema change is introduced so no migration is needed.

Nits — 5 non-blocking
  • The recovery job's `deliveryId` is a fixed `panel-retrigger-recovery:{repo}#{prNumber}` with no per-invocation uniqueness (src/queue/processors.ts, `maybeRecoverLostPanelRetrigger`'s `job` construction) — worth confirming the queue's dedup semantics don't silently drop a second, later, genuinely-lost click for the same PR under the same key.
  • `isCheckedPrPanelRetrigger`, `getTransientKey`, `PENDING_PR_PANEL_RETRIGGER_MARKER`, `markPendingPrPanelRetrigger`, and `consumePendingPrPanelRetrigger` aren't shown as new imports in the diff — worth a quick confirm they're already defined/imported elsewhere in this large file (very likely, given the sibling fix(review): a manual re-run request loses its forceAiReview intent when deferred for pending CI #7626 logic, but not verifiable from the diff alone).
  • `src/queue/processors.ts` and `src/db/repositories.ts` are both far past the repo's stated file-length threshold, though this PR only adds to existing patterns rather than introducing the smell.
  • Consider a short doc-comment cross-reference between `maybeRecoverLostPanelRetrigger`'s three call sites (prReadyForReview CI-wait, reviewing-placeholder publish, final publish) noting they're mutually exclusive-in-practice because `previousBody` chains sequentially within a pass — useful for a future reader modifying publish ordering.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #9000
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 14 registered-repo PR(s), 13 merged, 300 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 14 PR(s), 300 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: significant
Linked issue satisfaction

Addressed
The PR directly implements fix (1) from the issue: it makes every panel republish detect a previously-ticked rerun checkbox with no matching processing/pending marker, records a named audit event (pr_panel_retrigger_recovered) satisfying the 'never silence' acceptance criterion, and enqueues an immediate re-gate job to recover the lost intent, matching the issue's proposed idempotent recovery appr

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is registered but has no active allocation in the current snapshot.
  • Public profile languages: Python, TypeScript, Ruby, Go, MDX, Shell, Solidity, JavaScript
  • Official Gittensor activity: 14 PR(s), 300 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Then work through the remaining 1 step in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@JSONbored JSONbored self-assigned this Jul 28, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.03%. Comparing base (1ee2293) to head (b5a6f0e).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9584      +/-   ##
==========================================
- Coverage   89.83%   89.03%   -0.81%     
==========================================
  Files         875      878       +3     
  Lines      110958   111036      +78     
  Branches    26413    26434      +21     
==========================================
- Hits        99680    98859     -821     
- Misses       9992    11155    +1163     
+ Partials     1286     1022     -264     
Flag Coverage Δ
backend 94.15% <100.00%> (-1.47%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/db/repositories.ts 96.85% <100.00%> (+<0.01%) ⬆️
src/github/comments.ts 100.00% <100.00%> (ø)
src/queue/processors.ts 94.74% <100.00%> (+0.03%) ⬆️

... and 4 files with indirect coverage changes

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LoopOver approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit 643383c into main Jul 28, 2026
7 checks passed
@loopover-orb
loopover-orb Bot deleted the fix/panel-retrigger-recovery-9000 branch July 28, 2026 19:37
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.

orb(review): panel re-run checkbox is unreliable end-to-end — clicks silently lost, forced passes can silently skip the review

1 participant