Skip to content

Fix duplicate post-turn idle blocked alerts for unchanged sessions#1175

Merged
Yeachan-Heo merged 2 commits intodevfrom
fix/issue-1174-idle-blocked-dedupe
Apr 3, 2026
Merged

Fix duplicate post-turn idle blocked alerts for unchanged sessions#1175
Yeachan-Heo merged 2 commits intodevfrom
fix/issue-1174-idle-blocked-dedupe

Conversation

@Yeachan-Heo
Copy link
Copy Markdown
Owner

Summary

  • fingerprint post-turn idle notifications before the blocked hook event is emitted
  • persist the idle fingerprint in idle cooldown state so unchanged idle sessions stay suppressed
  • add regression coverage for unchanged idle repeats and real state transitions

Root cause

src/scripts/notify-hook.ts re-entered the session-idle emit path after every lead-session turn, but src/notifications/idle-cooldown.ts only tracked lastSentAt. Once the cooldown elapsed, OMX could re-announce the same unchanged idle state and emit another post_turn_idle_notification blocked event without any transition gate.

Verification

  • npm run build
  • npx biome lint src/scripts/notify-hook.ts src/notifications/idle-cooldown.ts src/notifications/__tests__/idle-cooldown.test.ts
  • npm run check:no-unused
  • node --test dist/notifications/__tests__/idle-cooldown.test.js dist/cli/__tests__/lifecycle-notifications.test.js

notify-hook re-entered the session-idle path after every lead-session turn while the idle cooldown module only remembered the last send timestamp. Once the timer elapsed, OMX could re-announce the same blocked idle state with no transition gate.

This change fingerprints the idle summary at the emit site and persists that fingerprint in idle-cooldown state. Unchanged idle fingerprints stay suppressed, while progress, terminal states, or summary changes reopen the alert path without broadening downstream hook contracts.

Constraint: Fix must stay source-side and avoid downstream hook contract changes
Rejected: Hook-local in-memory dedupe | notify-hook runs per turn and needs durable state
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep idle fingerprints compact and user-visible; do not add non-meaningful fields without regression coverage
Tested: npm run build
Tested: npx biome lint src/scripts/notify-hook.ts src/notifications/idle-cooldown.ts src/notifications/__tests__/idle-cooldown.test.ts
Tested: npm run check:no-unused
Tested: node --test dist/notifications/__tests__/idle-cooldown.test.js dist/cli/__tests__/lifecycle-notifications.test.js
Not-tested: End-to-end lifecycle delivery against a live notification provider
@github-actions github-actions bot added the size/L label Apr 3, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b47e019ef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


// Cooldown of 0 means disabled — always send
if (cooldownSecs === 0) return true;
if (cooldownSecs === 0 && !normalizedFingerprint) return true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Respect cooldown=0 even when fingerprints are provided

shouldSendIdleNotification now treats OMX_IDLE_COOLDOWN_SECONDS=0 as “always send” only when no fingerprint is passed, but notify-hook always passes a fingerprint. In that common path, unchanged fingerprints are still suppressed, which regresses the documented behavior that cooldown 0 disables throttling entirely and can silently drop repeated idle notifications/hooks for users who explicitly opted out of throttling.

Useful? React with 👍 / 👎.

Comment on lines +75 to +79
.split('\n')
.map((line) => line.trim())
.filter(Boolean);
const preferred = source.at(-1) || '';
const normalized = preferred.replace(/\s+/g, ' ').trim();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Derive fingerprint summary from status content, not last line

The fingerprint summary is taken from the last non-empty line of the assistant message. For multi-line replies that end with stable boilerplate (for example, a repeated closing prompt), different idle/blocker updates can collapse to the same summary and be treated as unchanged, so shouldSendIdleNotification suppresses notifications even when the underlying idle reason changed.

Useful? React with 👍 / 👎.

@Yeachan-Heo
Copy link
Copy Markdown
Owner Author

OMX review session result: request-changes.

Blocker summary:

  • src/notifications/idle-cooldown.ts regresses the documented OMX_IDLE_COOLDOWN_SECONDS=0 contract once fingerprints are present.
  • src/scripts/notify-hook.ts now always passes a fingerprint, but the new zero-cooldown fast path only returns true when there is no fingerprint.
  • Result: repeated identical idle states stay suppressed even when cooldown is explicitly set to 0, so the opt-out path no longer works in the real hook flow.

Repro used in review:

  • npm run build
  • node --test dist/notifications/__tests__/idle-cooldown.test.js dist/cli/__tests__/lifecycle-notifications.test.js
  • npm run check:no-unused
  • direct probe against dist/notifications/idle-cooldown.js with OMX_IDLE_COOLDOWN_SECONDS=0 returned {"shouldSendAgain":false} for the same fingerprint

Please restore the zero-cooldown opt-out semantics in the fingerprinted path, then I'll re-check quickly.


[repo owner's gaebal-gajae (clawdbot) 🦞]

PR #1175 still suppressed repeated idle notifications when fingerprints were present because the zero-cooldown path only bypassed throttling for non-fingerprinted sends. This follow-up makes cooldown=0 short-circuit before any persisted fingerprint comparison and adds regression coverage for unchanged fingerprint repeats.

Constraint: Preserve existing fingerprint dedupe whenever cooldown remains above zero
Rejected: Drop persisted fingerprints when cooldown is zero | unnecessary write-path coupling and the read path should already treat zero as a global bypass
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep zero-cooldown semantics ahead of both timestamp and fingerprint checks so disabled throttling stays consistent across idle notification paths
Tested: npm run build; node --test dist/notifications/__tests__/idle-cooldown.test.js; direct pre/post reproduction script for unchanged fingerprint with OMX_IDLE_COOLDOWN_SECONDS=0
Not-tested: Full npm test suite
@Yeachan-Heo
Copy link
Copy Markdown
Owner Author

Follow-up fix pushed for the blocker around OMX_IDLE_COOLDOWN_SECONDS=0 on the fingerprinted idle-notification path.

Fix summary

  • reproduced the regression locally: after recording an idle notification with a fingerprint, shouldSendIdleNotification() still returned false when OMX_IDLE_COOLDOWN_SECONDS=0
  • updated src/notifications/idle-cooldown.ts so zero cooldown short-circuits before any fingerprint comparison
  • added regression coverage in src/notifications/__tests__/idle-cooldown.test.ts for unchanged fingerprint repeats with cooldown disabled

Verification

  • npm run build
  • node --test dist/notifications/__tests__/idle-cooldown.test.js
  • direct repro check before fix: unchanged fingerprint + OMX_IDLE_COOLDOWN_SECONDS=0 returned false
  • direct repro check after fix: unchanged fingerprint + OMX_IDLE_COOLDOWN_SECONDS=0 returned true

Pushed on fix/issue-1174-idle-blocked-dedupe in commit ae4293c3.

@Yeachan-Heo Yeachan-Heo merged commit e114a25 into dev Apr 3, 2026
15 checks passed
@Yeachan-Heo Yeachan-Heo deleted the fix/issue-1174-idle-blocked-dedupe branch April 3, 2026 05:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant