fix: suppress NO_REPLY at end of message (closes #111)#112
Merged
Conversation
Closes #111. Agents in pipeline-style cron prompts (workspace-health, memory-consolidation, backup-git) reliably produce a recap followed by NO_REPLY at the end. The current /^NO_REPLY\b/ regex only matches at the start, so the recap was being delivered as a Telegram message (9 leaked messages across 4 crons in a 2-day window). Extracts the suppression check into bot/src/no-reply.ts (shared by stream-relay.ts and cron-runner.ts) so both delivery paths agree on exactly which patterns suppress. Accepts: - NO_REPLY at the start of output (existing — backward compat with #80) - NO_REPLY alone on the last non-empty line (new) Does not match same-line patterns like 'Done. NO_REPLY' or substring prefixes like 'NO_REPLY_EXTRA'. Test coverage in bot/src/__tests__/{stream-relay,cron-runner}.test.ts includes the verbatim leaked-output samples from the issue. Doc update in .claude/rules/platform/communication.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes a delivery suppression gap where cron/pipeline prompts often emit summaries ending with NO_REPLY, causing the summary to be delivered instead of suppressed (closes #111). The PR centralizes the suppression logic so both interactive streaming and cron delivery paths apply identical matching rules.
Changes:
- Extend
NO_REPLYsuppression to also match whenNO_REPLYappears alone on the last non-empty line (while preserving the existing start-of-message suppression behavior from #80). - Extract shared suppression logic into
bot/src/no-reply.tsand reuse it from bothstream-relay.tsandcron-runner.ts. - Add unit tests covering end-of-message suppression and negative cases (same-line
... NO_REPLYandNO_REPLY_EXTRA).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| docs/plans/completed/2026-04-27-issue-111-no-reply-end-of-message.md | Adds a completed plan/write-up documenting the issue, rationale, and test expectations for the suppression change. |
| bot/src/no-reply.ts | Introduces shouldSuppressNoReply() implementing the shared start-of-message or last-line-only suppression rules. |
| bot/src/stream-relay.ts | Replaces inline regex suppression with the shared shouldSuppressNoReply() check for interactive streaming delivery. |
| bot/src/cron-runner.ts | Replaces inline regex suppression with the shared shouldSuppressNoReply() check for LLM cron output delivery. |
| bot/src/tests/stream-relay.test.ts | Adds test coverage for end-of-message NO_REPLY suppression and explicitly pinned negative cases. |
| bot/src/tests/cron-runner.test.ts | Adds unit tests for shouldSuppressNoReply() to ensure cron-path behavior matches the same suppression patterns. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Closes #111.
Summary
<recap>\n\nNO_REPLYand the recap was being delivered as a real messageNO_REPLYalone on the last non-empty line of the trimmed output (in addition to the existing start-of-message rule from fix: NO_REPLY check should use trim/startsWith instead of exact match #80)bot/src/no-reply.tssostream-relay.tsandcron-runner.tsagree on exactly which patterns suppressDone. NO_REPLY) and substring prefixes (NO_REPLY_EXTRA) are intentionally NOT matchedEvidence
9 leaked messages across 4 distinct crons in a 2-day window, all the same
<summary>\n\nNO_REPLYshape — full samples in #111. Two prompt-side mitigations (per-cron strengthening + platform rule strengthening in #110) failed to change agent behavior; the model's RLHF "report what you did" instinct overrides explicit instructions to emit a bare token.Test plan
bot/src/__tests__/stream-relay.test.tscover end-of-message NO_REPLY (alone on last line, with whitespace, multi-line content above, single-newline, trailing-newline) and reproduce one of the verbatim leaked samplesbot/src/__tests__/cron-runner.test.tscover the same pattern set for the cron pathDone. NO_REPLYandNO_REPLY_EXTRAsubstring prefix are NOT suppressed (negative cases pinned)npx tsc --noEmitcleannpm test: 1016/1017 pass (1 pre-existing env-specific voice.ts failure unrelated to this change)