Repository navigation
fix: redeliver suppressed Discord requests and remind when overdue - #291
Merged
Merged
Conversation
…uests A Discord request's check close is a live condition, but the OMP main-fallback episode finished it as covered whenever the episode closed on an acknowledged queue. When the supervisor was inside one long turn, the request's row could be acknowledged away with nobody having presented it, so the request waited for some unrelated wake - about 20 minutes in the 22:45 incident. Suppressed closes now record their suppression time, and the boundary that closes an episode releases a check whose sink still reports it pending instead of covering it; the released close goes through the ordinary delivery path. The adapter supplies that sink predicate, so only the local Communication Officer path changes: the inbox record decides whether a request is answered, and the drain's new presentation note in state/.ext-presented/ keeps an already-presented request from being repeated. A request can still outlive every close that reached main, so each watcher cycle re-surfaces one request older than FM_EXT_OVERDUE_SECS (default 300) as check: ext-request-overdue <slug> age=<m>m, at most once per FM_EXT_OVERDUE_REMIND_SECS (default 600). The reminder stops with the record, and an ack-only request stays quiet while a task linked to it is still live.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A request's
check:close now survives the main handling episode that coalesced it, and a request nobody presents re-surfaces on a bounded cadence.The reminder claims its cadence window before asking the current-state reconciliation, so a live task is re-checked once per window and a crashed one surfaces within it. The marker disappears with the record.
Evidence
After: the final-head smoke prints
released gen=1 close=... by=queue-drainedand deliverscheck: ext-request <slug>(step 4 ofsmoke-final.log).tests/fm-omp-primary.test.shandtests/fm-ext-bridge.test.shpass on the final head, covering the truncated three-request batch, the multiline slow-poll result, presented/answered negatives, the path-containing check that must keep the covered path, reminder cadence, a crashed worker, a missing status log, and zero/negative thresholds.bin/fm-lint.sh, ShellCheck 0.11.0).Merge Danger
Door: two-way
The change is additive: a released close goes through the existing delivery path, and only closes carrying a real request token can change behaviour.
Blast Radius: supervision wake delivery only
A mis-mapped token could re-deliver one close that was already presented - a redundant notification, never a lost one - and a below-minimum threshold is clamped to 60 seconds rather than looping.
Pipeline
Updates from git push no-mistakes
✅ **Intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 2 issues found → auto-fixed (5) ✅
.omp/extensions/fm-primary-omp.ts:90- Required: a suppressed actionable request check "must be delivered once that episode closes" unless presented or removed. The new predicate rejects actual watcher payloads: bin/fm-watch.sh:911 batchesext-request A; ext-request B, which bin/fm-ext-lib.sh:2442 treats as one invalid slug; bin/fm-watch.sh:2202 prefixes poll results with the check-script path, which this regex rejects outright. If these closes are suppressed and a later ack-through empties the queue without presentation, bin/fm-primary-watch-core.ts:1958 still covers and discards them. The same mismatch defeats successor preference at bin/fm-primary-watch-core.ts:1910. Normalize both existing producer formats at the adapter/library boundary and evaluate every referenced request, preserving unrelated check behavior.bin/fm-ext-overdue-lib.sh:36- Required: "with no live task it must be reminded." The addedstatus_is_terminal_verb "$last" || return 0equates a nonterminal historical status with current liveness. A linked worker can writeworking:, then crash while its acknowledged inbox request remains; its metadata and status survive, so every overdue scan suppresses that request indefinitely. A missing status also counts as live. The exemption consumer is bin/fm-ext-overdue-lib.sh:72, and docs/configuration.md:878 repeats this incorrect definition. Use the existing current-task reconciliation boundary, which distinguishes active runs from dead endpoints, before granting the exemption.🔧 Fix applied.
1 warning still open:
bin/fm-ext-lib.sh:2492- Required: reminders must run on a bounded cadence, "never a tight loop." Setting FM_EXT_OVERDUE_SECS=0 and FM_EXT_OVERDUE_REMIND_SECS=0 passes both numeric checks, and the new clamp[ "$raw" -ge "$floor" ] || raw=$floorpreserves zero. Consequently, bin/fm-ext-overdue-lib.sh:71 always passes the age gate and :76 always passes the cadence gate, even immediately after writing the marker. bin/fm-watch.sh:924 then repeatedly appends and rings on successive watcher launches, accumulating notifications while main is busy. Validate both settings at bin/fm-ext-lib.sh:2472 and :2485 so the effective reminder cadence remains positive.🔧 Fix applied.
2 issues (1 error, 1 warning) still open:
bin/fm-ext-lib.sh:2492- Required: reminders must run on a bounded cadence, "never a tight loop." Setting FM_EXT_OVERDUE_SECS=0 and FM_EXT_OVERDUE_REMIND_SECS=0 passes both numeric checks, and the new clamp[ "$raw" -ge "$floor" ] || raw=$floorpreserves zero. Consequently, bin/fm-ext-overdue-lib.sh:71 always passes the age gate and :76 always passes the cadence gate, even immediately after writing the marker. bin/fm-watch.sh:924 then repeatedly appends and rings on successive watcher launches, accumulating notifications while main is busy. Validate both settings at bin/fm-ext-lib.sh:2472 and :2485 so the effective reminder cadence remains positive.bin/fm-ext-lib.sh:2466- Round 1's parser fix leaves truncated batches behind. Required: a suppressed actionable request check “must be delivered once that episode closes” unless presented or removed. This predicate checks only IDs retained in the payload. bin/fm-watch.sh:911-913 includes three requests but marks every batch row rung; the slow-poll producer at bin/fm-watch.sh:2226 emits multiline results, while bin/fm-primary-watch-core.ts:307-309 retains only the first line. If the retained requests are answered while an omitted, ack-only request remains unpresented, a later ack-through empties the queue and bin/fm-primary-watch-core.ts:1958-1974 covers the close. The omitted request waits for the overdue backstop instead of episode redelivery. The same incomplete set affects successor preference at bin/fm-primary-watch-core.ts:1910. Preserve every represented request ID in a single close payload at the producer/close-normalization boundary before deciding that the batch is handled.🔧 Fix applied.
3 issues (1 error, 2 warnings) still open:
bin/fm-ext-lib.sh:2492- Required: reminders must run on a bounded cadence, "never a tight loop." Setting FM_EXT_OVERDUE_SECS=0 and FM_EXT_OVERDUE_REMIND_SECS=0 passes both numeric checks, and the new clamp[ "$raw" -ge "$floor" ] || raw=$floorpreserves zero. Consequently, bin/fm-ext-overdue-lib.sh:71 always passes the age gate and :76 always passes the cadence gate, even immediately after writing the marker. bin/fm-watch.sh:924 then repeatedly appends and rings on successive watcher launches, accumulating notifications while main is busy. Validate both settings at bin/fm-ext-lib.sh:2472 and :2485 so the effective reminder cadence remains positive.bin/fm-ext-lib.sh:2466- Round 1's parser fix leaves truncated batches behind. Required: a suppressed actionable request check “must be delivered once that episode closes” unless presented or removed. This predicate checks only IDs retained in the payload. bin/fm-watch.sh:911-913 includes three requests but marks every batch row rung; the slow-poll producer at bin/fm-watch.sh:2226 emits multiline results, while bin/fm-primary-watch-core.ts:307-309 retains only the first line. If the retained requests are answered while an omitted, ack-only request remains unpresented, a later ack-through empties the queue and bin/fm-primary-watch-core.ts:1958-1974 covers the close. The omitted request waits for the overdue backstop instead of episode redelivery. The same incomplete set affects successor preference at bin/fm-primary-watch-core.ts:1910. Preserve every represented request ID in a single close payload at the producer/close-normalization boundary before deciding that the batch is handled.bin/fm-ext-lib.sh:2433- Simplification: round 3 left fm_ext_check_payload_slugs unused after moving release decisions to the inbox scan. Its only callers are parser assertions at tests/fm-ext-bridge.test.sh:2508, :2514, and :2521. No current requirement needs this separate parsing path. Remove the helper and its parser-only assertions, retaining the behavioral predicate tests.🔧 Fix applied.
4 issues (2 errors, 2 warnings) still open:
bin/fm-ext-lib.sh:2492- Required: reminders must run on a bounded cadence, "never a tight loop." Setting FM_EXT_OVERDUE_SECS=0 and FM_EXT_OVERDUE_REMIND_SECS=0 passes both numeric checks, and the new clamp[ "$raw" -ge "$floor" ] || raw=$floorpreserves zero. Consequently, bin/fm-ext-overdue-lib.sh:71 always passes the age gate and :76 always passes the cadence gate, even immediately after writing the marker. bin/fm-watch.sh:924 then repeatedly appends and rings on successive watcher launches, accumulating notifications while main is busy. Validate both settings at bin/fm-ext-lib.sh:2472 and :2485 so the effective reminder cadence remains positive.bin/fm-ext-lib.sh:2466- Round 1's parser fix leaves truncated batches behind. Required: a suppressed actionable request check “must be delivered once that episode closes” unless presented or removed. This predicate checks only IDs retained in the payload. bin/fm-watch.sh:911-913 includes three requests but marks every batch row rung; the slow-poll producer at bin/fm-watch.sh:2226 emits multiline results, while bin/fm-primary-watch-core.ts:307-309 retains only the first line. If the retained requests are answered while an omitted, ack-only request remains unpresented, a later ack-through empties the queue and bin/fm-primary-watch-core.ts:1958-1974 covers the close. The omitted request waits for the overdue backstop instead of episode redelivery. The same incomplete set affects successor preference at bin/fm-primary-watch-core.ts:1910. Preserve every represented request ID in a single close payload at the producer/close-normalization boundary before deciding that the batch is handled.bin/fm-ext-lib.sh:2433- Simplification: round 3 left fm_ext_check_payload_slugs unused after moving release decisions to the inbox scan. Its only callers are parser assertions at tests/fm-ext-bridge.test.sh:2508, :2514, and :2521. No current requirement needs this separate parsing path. Remove the helper and its parser-only assertions, retaining the behavioral predicate tests.bin/fm-ext-lib.sh:2435- Round 3's R4 fix introduced an overly broad ownership guard:*ext-request*matches script paths as well as request events. A legitimate PR check such asstate/fm-ext-request-redeliver.check.sh: mergedtherefore returns pending whenever an unrelated inbox request remains unpresented. The closing boundary then repeats the PR notification, contradicting the required constraint “no other wake class changes behaviour.” Narrow the shared guard to an actual ext-request/ext-request-overdue event token while preserving the inbox-wide scan. Related changed sites: .omp/extensions/fm-primary-omp.ts:90 accepts the same substring; bin/fm-primary-watch-core.ts:1910 changes successor preference and :1958 releases the unrelated close.🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
git diff 7c71cfb43ea01263763dae7689d1dd9085f7391f HEADto scope acceptance scenarios.python3 .validation-temp/probe.pylaunched real OMP RPC with production extension/watchers and a localhost model endpoint.python3 .validation-temp/episode.pyexercised suppressed-request redelivery after unread queue consumption.python3 .validation-temp/guards.pyexercised real drain presentation, inbox dismissal, and duplicate suppression.python3 .validation-temp/batch-resume.pycompleted the actual watcher batch and successor-preference checks after correcting driver setup.python3 .validation-temp/reminders.pyexercised production watcher cadence, threshold clamps, dismissal, and real tmux liveness reconciliation; repeated with FM_POLL=1.Awaited or stopped all owned children, verified the private tmux server stopped, removed disposable files, and confirmed cleangit status --short.✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed ✅
🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.