You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
LLM cron prompts that say "if everything is clean, reply with NO_REPLY" produce summaries followed by NO_REPLY at the end. The current regex (^NO_REPLY\b in bot/src/stream-relay.ts:274 and bot/src/cron-runner.ts:393) only matches when NO_REPLY is at the start, so the summary is delivered as a real message.
Evidence
Reproduces consistently across all 5 workspace-health crons (2026-04-27 03:30–03:50 MSK batch). Sample agent outputs that were delivered instead of suppressed:
All checks complete. Everything is clean — no real issues found.
NO_REPLY
All checks complete. Everything is clean — no errors, no warnings, no drift, no fact mismatches.
NO_REPLY
All checks complete. Let me compile the results:
• Size audit: OK (335M, no bloat)
• Hook integrity: OK
• Config check: 1 warning (settings.local.json missing outputStyle — minor, file doesn't exist)
[... 6 more bullets ...]
The only finding is the settings.local.json warning, which is informational — the file simply doesn't exist, and it's optional.
NO_REPLY
100% failure rate (5/5 crons leaked).
Why prompt strengthening didn't fix it
Two prompt-side mitigations were tried and both failed:
Per-cron prompts were strengthened to "your ENTIRE response must be exactly the literal token NO_REPLY — nothing else, no preamble, no summary" with explicit warnings about the regex. (commit bb3d22d in operator's workspace)
Platform rule (.claude/rules/platform/communication.md) was strengthened in PR docs(rules): strengthen NO_REPLY communication rule #110 (merged 2026-04-26) with the same explicit wording, regex citation, and wrong/right examples.
Both changes were live before the failed batch. All 5 crons produced summary-then-NO_REPLY anyway. The model's "report what you did" tendency from RLHF training overrides explicit instructions to output a bare token. Prompt engineering is not the right tool here.
Someone writing prose ending with NO_REPLY (e.g., a meta-discussion about the suppression mechanism) would be silently suppressed. Two mitigations:
The \s*NO_REPLY\s*$ requires NO_REPLY to be the entire last line (alone, only whitespace around it). This is a rare prose pattern.
Documentation: update .claude/rules/platform/communication.md to note that NO_REPLY on its own line at end of message ALSO suppresses delivery, so agents can write naturally.
Test plan
Add unit tests in bot/src/__tests__/stream-relay.test.ts covering: NO_REPLY at end alone, NO_REPLY at end with surrounding whitespace, NO_REPLY at end with content above, NO_REPLY at end on same line as content (should NOT suppress).
Add unit tests in bot/src/__tests__/cron-runner.test.ts (or wherever cron suppression is tested) for the same patterns.
Manually trigger one workspace-health cron via launchctl kickstart — verify suppression when output ends with NO_REPLY.
Problem
LLM cron prompts that say "if everything is clean, reply with NO_REPLY" produce summaries followed by
NO_REPLYat the end. The current regex (^NO_REPLY\binbot/src/stream-relay.ts:274andbot/src/cron-runner.ts:393) only matches whenNO_REPLYis at the start, so the summary is delivered as a real message.Evidence
Reproduces consistently across all 5 workspace-health crons (2026-04-27 03:30–03:50 MSK batch). Sample agent outputs that were delivered instead of suppressed:
100% failure rate (5/5 crons leaked).
Why prompt strengthening didn't fix it
Two prompt-side mitigations were tried and both failed:
bb3d22din operator's workspace).claude/rules/platform/communication.md) was strengthened in PR docs(rules): strengthen NO_REPLY communication rule #110 (merged 2026-04-26) with the same explicit wording, regex citation, and wrong/right examples.Both changes were live before the failed batch. All 5 crons produced summary-then-NO_REPLY anyway. The model's "report what you did" tendency from RLHF training overrides explicit instructions to output a bare token. Prompt engineering is not the right tool here.
Root cause
bot/src/stream-relay.ts:274:bot/src/cron-runner.ts:393:Both require
NO_REPLYto be at the start of the trimmed output. Agents reliably put it at the end.Proposed fix
Accept
NO_REPLYeither at the start (current behavior, backward compat) OR alone on the last non-empty line (new). Pseudocode:Apply identical change to both files.
False-positive risk
Someone writing prose ending with
NO_REPLY(e.g., a meta-discussion about the suppression mechanism) would be silently suppressed. Two mitigations:\s*NO_REPLY\s*$requires NO_REPLY to be the entire last line (alone, only whitespace around it). This is a rare prose pattern..claude/rules/platform/communication.mdto note thatNO_REPLYon its own line at end of message ALSO suppresses delivery, so agents can write naturally.Test plan
bot/src/__tests__/stream-relay.test.tscovering: NO_REPLY at end alone, NO_REPLY at end with surrounding whitespace, NO_REPLY at end with content above, NO_REPLY at end on same line as content (should NOT suppress).bot/src/__tests__/cron-runner.test.ts(or wherever cron suppression is tested) for the same patterns.launchctl kickstart— verify suppression when output ends withNO_REPLY.^NO_REPLYcases still suppress (bedtime-reminder, vitaminka, etc.).Related
^NO_REPLYstartsWith behavior (exact-match → startsWith). This issue is the opposite axis.