[BUG] Since v2.1.196, scheduled prompts (session cron / wakeup) are no longer parsed as slash commands — scheduled "/compact" silently no-ops (skipSlashCommands: true added to cron onFire)

Open 💬 0 comments Opened Jul 8, 2026 by genesiscz

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet (closest matches: #66246 — programmatic-compaction feature request, #65464 — ScheduleWakeup /loop re-injection; neither describes this routing change)
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code (behavior reproduced on 2.1.202; the responsible 2.1.196 change is still present as of 2.1.204)

What's Wrong?

The session cron scheduler (CronCreate tool) and ScheduleWakeup can schedule a prompt to be enqueued later in the same interactive session. Up to v2.1.195, a scheduled prompt consisting of a slash command (e.g. "/compact", or a skill command with args) was executed through the real slash-command path when it fired — it produced a <command-name>...</command-name> invocation record and actually ran (compaction ran and printed Compacted; skill commands entered the skill).

Since v2.1.196, the same scheduled prompt is injected into the conversation as a plain user message with isMeta: true, bypassing the slash-command parser entirely. The model receives the literal text /compact as conversational input; since there is no tool for programmatic compaction (see #66246), nothing happens.

Pinpointed cause (static bisect of published npm binaries): extracting /$bunfs/root/src/entrypoints/cli.js from the Bun standalone module graph of @anthropic-ai/claude-code-darwin-arm64@<v> for every version published between 2.1.185 and 2.1.197 (2.1.186, .187, .190, .191, .193, .195, .196 — no 188/189/192/194 exist) shows: in 2.1.196 the cron scheduler's onFire enqueue call (adjacent to the "cron_fire" telemetry string) gained two new fields — skipSlashCommands: !0, modelScheduledOrigin: !0. The dequeue-side predicate typeof value === "string" && value.trim().startsWith("/") && !skipSlashCommands then refuses slash parsing for scheduled prompts. skipSlashCommands existed pre-2.1.196 for other injection paths (channel/peer/bridge messages); 2.1.196 extended it to cron fires. So this appears to be a deliberate change (plausibly so model-scheduled prompts can't self-execute privileged commands) — but it's absent from the changelog and the failure is invisible (see below).

Two aggravating factors:

  1. The failure is silent and models narrate false success. The tool result still implies success, and in two separate sessions the model that received the un-parsed /compact text told the user "Scheduled /compact fired as planned and the conversation history has been compacted." — nothing had been compacted. Users only discover this by auditing session JSONLs. (Provable to the millisecond via compactMetadata.durationMs: the eventual boundary at 21:38:31.668 has durationMs: 144075 → compaction started 21:36:07.593 — exactly the user's manual <command-name>/compact</command-name> record at 21:36:07.592, not the scheduled fire at 21:31:00. At the moment the model claimed success, the context still held 607,271 tokens.)
  2. The queued-input path may share the bug: a human-typed /compact, entered while the assistant turn was still streaming, also arrived as isMeta:true plain text and did not execute (v2.1.202). Typed at a fully idle prompt it works normally.

Also affected: ScheduleWakeup — a controlled same-session A/B on v2.1.202 (wakeup fire 17:53:00Z, cron fire 17:55:00Z, both prompt: "/compact", REPL idle at both fire times) produced identical isMeta:true plain-text deliveries and zero compactions. Exception: a wakeup-fired /loop <args> prompt WAS still re-injected as a real invocation on v2.1.197 (2026-07-02), consistent with #65464 — so the post-change routing appears to special-case /loop only.

What Should Happen?

The fired prompt should be processed exactly as if the user typed it: scheduled /compact compacts the session (as it did through v2.1.195) — or, if the new behavior is intentional, the scheduling tools should reject/warn on slash-command prompts instead of silently downgrading them to plain text, and the change should be documented. See "Additional Information" for concrete options.

Error Messages/Logs

# WORKING fire — v2.1.170, 2026-06-16 (most recent observed working /compact; 541k→10k tokens compacted in 126s):
{"ts":"2026-06-16T21:21:00.549Z","type":"queue-operation"}
{"ts":"2026-06-16T21:21:00.566Z","v":"2.1.170","type":"user","isMeta":null,"content":"<command-name>/compact</command-name> <command-message>compact</command-message>..."}
{"ts":"2026-06-16T21:23:06.856Z","v":"2.1.170","type":"system","subtype":"compact_boundary","compactMetadata":{"trigger":"manual","preTokens":541079,"durationMs":126289,"postTokens":10326}}
{"ts":"2026-06-16T21:23:07.450Z","v":"2.1.170","type":"user","content":"<local-command-stdout>Compacted</local-command-stdout>"}

# WORKING fire — v2.1.154, 2026-05-29 (cron {"37 4 29 5 *","/compact"}), from session JSONL:
{"ts":"2026-05-29T02:37:00.251Z","type":"user","isMeta":null,"content":"<command-name>/compact</command-name>\n<command-message>compact</command-message>..."}
{"ts":"2026-05-29T02:38:23.672Z","type":"system","subtype":"compact_boundary","compactMetadata":{"trigger":"manual","preTokens":159413,"durationMs":83420,"postTokens":28021}}
{"ts":"2026-05-29T02:38:24.698Z","type":"user","content":"<local-command-stdout>Compacted ... completed successfully</local-command-stdout>"}

# WORKING fire (skill slash with args) — v2.1.185, 2026-06-29:
{"ts":"2026-06-29T16:05:00.657Z","type":"queue-operation"}
{"ts":"2026-06-29T16:05:00.707Z","v":"2.1.185","type":"user","isMeta":false,"content":"<command-message>genesis-tools:github-pr</command-message> <command-name>/genesis-tools:github-pr</command-name> ..."}

# BROKEN fire — v2.1.202, 2026-07-08 (cron {"18 1 8 7 *","/compact"}); zero compact_boundary entries in the session:
{"ts":"2026-07-07T23:18:00.684Z","type":"queue-operation"}
{"ts":"2026-07-07T23:18:00.789Z","type":"user","isMeta":true,"content":"/compact"}

# BROKEN A/B pair — v2.1.202, 2026-07-08, same session, REPL idle at both fires:
{"ts":"2026-07-08T17:53:00.191Z","type":"user","isMeta":true,"content":"/compact"}   # ScheduleWakeup
{"ts":"2026-07-08T17:55:00.492Z","type":"user","isMeta":true,"content":"/compact"}   # CronCreate

# Model falsely claiming success after a broken fire — v2.1.197, 2026-07-07:
{"ts":"2026-07-07T21:31:00.680Z","type":"user","isMeta":true,"content":"/compact"}
{"ts":"2026-07-07T21:31:22.472Z","type":"assistant","content":"Scheduled `/compact` fired as planned and the conversation history has been compacted. ..."}

Steps to Reproduce

  1. In an interactive session on ≥2.1.196, have the model schedule a one-shot cron with the prompt set to the bare slash command:

``
CronCreate({ cron: "<M> <H> <dom> <mon> *", prompt: "/compact", recurring: false })
``

  1. Leave the REPL idle past the fire minute.
  2. Observe: the "Running scheduled task" banner appears, the model responds conversationally to the literal text /compact, and no compaction happens — no Compacted output, no compact_boundary entry in the session JSONL, only a user entry with string content "/compact" and isMeta: true.
  3. Repeat on ≤2.1.195: the fire produces a real <command-name>/compact</command-name> invocation and the session compacts.

Diagnostic signatures for triage: working fire = <command-name>... user entry (+ caveat entry) at the fire timestamp, followed by system/compact_boundary once summarization finishes (~1–3 min for large contexts); broken fire = a single user entry with string content exactly "/compact" and isMeta: true, preceded by queue-operation entries, and nothing else.

Claude Model

Not sure / Multiple models (Opus 4.x and Fable 5 sessions — model-independent, this is harness message routing)

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.195 (2.1.196, published 2026-06-29, introduced the change)

Claude Code Version

2.1.202 (also reproduced on 2.1.197; change still present in 2.1.204 per bundle inspection)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other (Ghostty; also reproduced under tmux-hosted sessions)

Additional Information

Evidence base: a 90-day sweep of 68 cron-using sessions in one user's local history. Parsed (working) fires observed on 2.1.123, 2.1.138, 2.1.141, 2.1.145, 2.1.150, 2.1.154, 2.1.165, 2.1.170, 2.1.185 (latest: 2026-06-29 16:05Z — four hours before 2.1.196 was published). Broken fires observed on 2.1.197 (from 2026-07-01) and 2.1.202. Neither CronCreate nor ScheduleWakeup exposes any parameter to request invocation routing — isMeta is a recording of the path taken, not a settable flag.

Scheduling /compact at a known-quiet moment (end of a work batch, overnight) is a natural and previously working pattern for long-running agentic sessions. If skipSlashCommands: true on cron fires is intentional, please consider any of:

  1. Make the failure visible: CronCreate/ScheduleWakeup should reject or warn on prompts starting with / instead of silently downgrading them — the tool result currently implies success.
  2. Document the change in the changelog and in the CronCreate/ScheduleWakeup tool descriptions.
  3. Provide a supported path for scheduled compaction (the actual use case behind most of these crons) — see #66246; even a user-consented setting or an allowlist like the apparent /loop exception would cover it.
  4. Or restore pre-2.1.196 behavior for user-visible session crons.

View original on GitHub ↗