feat(reminders): expiration, self-cancellation, and delivery timeout fix#752
Merged
Aaronontheweb merged 4 commits intodevfrom Apr 26, 2026
Merged
feat(reminders): expiration, self-cancellation, and delivery timeout fix#752Aaronontheweb merged 4 commits intodevfrom
Aaronontheweb merged 4 commits intodevfrom
Conversation
…meout (#741) Recurring reminders fired indefinitely when their purpose was fulfilled (e.g., PR merged) because there was no expiration or self-completion mechanism. Separately, Mode B delivery observation timed out at 30s while LLM turns regularly take 30-60s, causing false failures. Expiration: - Add ExpiresAt (nullable) to ReminderDefinition and ReminderInfo - Check expiration on fire before executing or rescheduling - Disable expired recurring reminders during startup reconciliation - Add ExpiresIn parameter to set_reminder tool, REST API, and CLI Self-completion: - Add complete_reminder tool (soft-disable, preserves history) - Add prompt guidance for recurring reminders with reminder ID Delivery timeout: - Align DeliveryObservedTimeout with ExecutionTimeoutSeconds (300s) Spec and tests: - Add 3 requirement sections and 9 scenarios to scheduling spec - Add CompleteReminderToolTests, expiration tests for manager and tool
… for self-completion CompleteReminderTool added an unnecessary fifth scheduling tool that could confuse smaller models. The LLM can call cancel_reminder when a recurring reminder's purpose is fulfilled — no separate concept needed. Also from code review: - Remove redundant CancelScheduleOnlyAsync in reconciliation loop (DisableReminderInternalAsync already calls it) - Rename FormatNextFire → FormatTimestamp (now used for both next-fire and expiration timestamps) - Update stale DeliveryObservedTimeout comment to reflect new intent
…ensions.Time.Testing Four test files in Netclaw.Actors.Tests defined identical private FakeTimeProvider classes despite the project already referencing the Microsoft.Extensions.TimeProvider.Testing package. Replace them with the official FakeTimeProvider from Microsoft.Extensions.Time.Testing.
Prevent queued recurring reminders from executing after expiration and reject one-shot reminders that include expiration metadata. Also align reminder API/CLI/operations guidance so expiration is visible and documented consistently.
Merged
Aaronontheweb
added a commit
that referenced
this pull request
Apr 29, 2026
) * chore(openspec): archive 19 completed changes and sync delta specs Archive all implemented OpenSpec changes, syncing their delta specs to main specs in chronological order before moving to archive. Resolves 13 cross-change spec conflicts by applying older changes first so newer implementations take precedence. Archived changes: - add-discord-init-reminder-dm-support - background-job-execution - channel-ingress-attachments - channels-content-delivery-guarantees - containerize-daemon-and-evals - device-pairing - discord-channel-with-interactions - exposure-modes - graceful-config-restart-drain - hub-auth-framework - inbound-webhooks - mcp-audience-tool-grants - mcp-server-approval-defaults - multi-speaker-attribution - public-audience-security-hardening - session-cwd-tracking - structured-tool-call-metadata - tool-approval-gates - working-context-grounding New main specs created: audience-context-filtering, daemon-container, daemon-exposure, device-pairing, feature-selection-wizard, hub-auth, inbound-webhooks, netclaw-discord-socket, project-instructions, session-cwd, tool-call-metadata Remaining active changes: reminder-delivery-contract (partial), subagent-explicit-model-selection (blocked on #648) * chore(openspec): archive reminder-delivery-contract and sync specs Implementation is complete (PR #692, #752). Remaining eval cases tracked in #793.
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
Two production reminder failures exposed lifecycle and timeout gaps:
akka-pr-8186-check1interval reminder fired 60+ times over 21 hours after its purpose was fulfilled, because there was no expiration mechanism and the stuck session reported "success" in 5ms without the LLM ever running.dance-recital-115OneShot reminder completed successfully (Slack message delivered, user replied) but was reported as failed becauseDeliveryObservedTimeout(30s) expired 3 seconds before the 33-second LLM turn finished.Changes
Expiration — recurring reminders now support an optional
ExpiresAttimestamp:ExpiresAtMs/ExpiresAttoReminderDefinitionandReminderInfo(backwards-compatible nullable field)ExpiresInparameter onset_remindertool, REST API, and--expires-inCLI flagSelf-cancellation — recurring reminder prompts now include guidance telling the LLM to call
cancel_reminderwhen the task's purpose is permanently fulfilled (e.g., PR merged, deploy completed), including the reminder's own ID.Delivery timeout —
DeliveryObservedTimeoutaligned withExecutionTimeoutSeconds(300s). A delivery-required reminder is now allowed the full execution window for the LLM to produce output.Cleanup — replaced 4 hand-rolled
FakeTimeProviderclasses inNetclaw.Actors.Testswith theMicrosoft.Extensions.Time.Testing.FakeTimeProviderthat was already a package dependency. RenamedFormatNextFire→FormatTimestampsince it now formats both next-fire and expiration timestamps.Related
akka-pr-8186-check1issue but is being addressed in docs(openspec): draft Discord channel interactions planning spec #713 as part of the Discord/channel unification work.Test plan
dotnet build— zero errors, zero warningsdotnet test— 1152 tests pass (Actors.Tests), 90 reminder-specific tests passdotnet slopwatch analyze— no new violations--expires-in 2m, verify auto-disable after expirationcancel_reminderguidance appears in recurring reminder execution prompts