Skip to content

fix(slack): render mrkdwn + keep scheduling on the local daemon#90

Merged
meidad merged 1 commit into
mainfrom
fix/slack-mrkdwn-and-native-scheduling
Jun 18, 2026
Merged

fix(slack): render mrkdwn + keep scheduling on the local daemon#90
meidad merged 1 commit into
mainfrom
fix/slack-mrkdwn-and-native-scheduling

Conversation

@meidad

@meidad meidad commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

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.ts adapter — the primary Socket Mode bot path — sent the agent's raw Markdown straight to Slack. Slack doesn't speak CommonMark, so ```bash fences showed bash as a literal line, **bold** showed the asterisks, and [text](url) showed raw. A converter (markdownToSlackMrkdwn) already existed and was used by slack-polling.ts and slack-user.ts, but slack.ts never called it.

send(), postMessage(), and updateMessage() now run through markdownToSlackMrkdwn:

  • 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 the text field, which also fixes streamed responses.

2. Scheduling escaped to claude.ai

The SDK's built-in schedule/loop skills (and the CronCreate/RemoteTrigger/ScheduleWakeup tools 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 the schedule skill, but the agent ignored it (and reached for loop, which the prompt didn't mention), creating a trig_... routine.

Defense in depth:

  • Hard block in getDisallowedTools() — added CronCreate, CronDelete, CronList, RemoteTrigger, ScheduleWakeup to the existing allowlist that already blocks Workflow/TaskCreate for the same "route to Nomos-native equivalents" reason. Applies to all main-agent call sites and team workers (which inherit task.disallowedTools).
  • Prompt in profile.ts — scheduling guidance now names both the schedule and loop skills plus those tools, and routes to the schedule_task / loop_create MCP 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 21 slack-mrkdwn tests
  • Verified the converter against the exact failing example: ```bash → clean code block, **bold***bold*, links → <url|text>, numbered lists preserved

🤖 Generated with Claude Code

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>
@meidad meidad merged commit 36e8904 into main Jun 18, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant