fix(slack): render mrkdwn + keep scheduling on the local daemon#90
Merged
Conversation
Two fixes to how the daemon's Slack bot path behaves: 1. Slack rendering: the Bolt-based slack.ts adapter sent raw Markdown to Slack, so ```lang fences showed the language as a literal line, **bold** showed asterisks, and [text](url) showed raw. It now runs send()/postMessage()/updateMessage() through markdownToSlackMrkdwn, matching slack-polling.ts and slack-user.ts. send() converts before chunking so fenced-code language tags are stripped and char limits apply to the real output; the file attachment keeps original Markdown. 2. Native scheduling: the SDK's built-in `schedule`/`loop` skills (and the CronCreate/RemoteTrigger/ScheduleWakeup tools they call) create Anthropic-hosted claude.ai Routines that never run in the daemon and never show in the user's settings. A prompt warning alone didn't stop the agent from using them, so block those tools outright in getDisallowedTools() and extend the system-prompt guidance to cover both skills, routing to the schedule_task / loop_create MCP tools. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Two fixes to how the daemon's Slack bot path behaves, both surfaced from a real conversation where a "schedule a daily check" request rendered as raw Markdown and created an Anthropic-hosted claude.ai Routine instead of a Nomos daemon task.
1. Slack renders raw Markdown
The Bolt-based
slack.tsadapter — the primary Socket Mode bot path — sent the agent's raw Markdown straight to Slack. Slack doesn't speak CommonMark, so```bashfences showedbashas a literal line,**bold**showed the asterisks, and[text](url)showed raw. A converter (markdownToSlackMrkdwn) already existed and was used byslack-polling.tsandslack-user.ts, butslack.tsnever called it.send(),postMessage(), andupdateMessage()now run throughmarkdownToSlackMrkdwn:send()converts before chunking, so fenced-code language tags are stripped and char limits apply to the real output. The file attachment for very long messages keeps the original Markdown (renders fine in editors).postMessage()/updateMessage()convert at thetextfield, which also fixes streamed responses.2. Scheduling escaped to claude.ai
The SDK's built-in
schedule/loopskills (and theCronCreate/RemoteTrigger/ScheduleWakeuptools they call) create Anthropic-hosted claude.ai Routines — 1-hour minimum, results land on the claude.ai dashboard, and they never run in the daemon or show up in the user's settings. The system prompt already warned against thescheduleskill, but the agent ignored it (and reached forloop, which the prompt didn't mention), creating atrig_...routine.Defense in depth:
getDisallowedTools()— addedCronCreate,CronDelete,CronList,RemoteTrigger,ScheduleWakeupto the existing allowlist that already blocksWorkflow/TaskCreatefor the same "route to Nomos-native equivalents" reason. Applies to all main-agent call sites and team workers (which inherittask.disallowedTools).profile.ts— scheduling guidance now names both thescheduleandloopskills plus those tools, and routes to theschedule_task/loop_createMCP tools.Confirmed nothing in the codebase uses those built-ins internally (Nomos scheduling is all
mcp__-prefixed MCP tools).Testing
pnpm check(format + typecheck + lint) — pass (0 errors)pnpm test— 673 tests pass, including the 21slack-mrkdwntests```bash→ clean code block,**bold**→*bold*, links →<url|text>, numbered lists preserved🤖 Generated with Claude Code