Skip to content

The wolf forgot how to script#347

Open
LElkUNP wants to merge 1 commit into
jerpint:mainfrom
LElkUNP:fix/wolf-script-action
Open

The wolf forgot how to script#347
LElkUNP wants to merge 1 commit into
jerpint:mainfrom
LElkUNP:fix/wolf-script-action

Conversation

@LElkUNP

@LElkUNP LElkUNP commented May 21, 2026

Copy link
Copy Markdown
Contributor

What happened

Loup wakes up on schedule, howls, marks the cron "dispatched" in jobs.jsonl — but nothing actually runs. Every wolt with a "action": "script" cron in wolf.json has been silently spawning empty Claude Code sessions instead of executing its shell command. The session has no prompt, so dispatch_session() bails. The session is reaped seconds later. No bash runs, no apps come back up, no digest fires, no Loucie watcher polls — but the howl keeps going every cycle so it looks fine from the outside.

This lab's apps-keepalive (Milo/apps-keepalive.sh) is the most visible casualty — onboarding portal, ressources, calendar widget, radicale, and four Slack/Telegram bots all silently die after the first container rebuild. La Trame's start-apps cron is the same. Found while testing rebuild survival after migrating to a named tunnel — apps wouldn't come back up, which led to discovering Loup wasn't actually running anything.

Root cause

fire_cron() in container/creatures/wolf.py unconditionally calls dispatch_session() regardless of the action field:

def fire_cron(entry: dict):
    ...
    _log_job(name, "session", event="started", owner=owner)
    link = dispatch_session(entry)   # <-- always session, never script
    _log_job(name, "session", event="dispatched", ...)

The action: "script" schema documented in existing wolf.json files across the platform (Loup, la-trame) is unimplemented — the field is read but never branched on.

What changed

  • New dispatch_script(entry) helper — runs entry["command"] via subprocess.Popen with start_new_session=True (fire-and-forget, doesn't block the wolf loop).
  • fire_cron() now reads entry.get("action", "session") and routes:
    • action: "script"dispatch_script() → logs as action: "script" in jobs.jsonl
    • anything else (including missing) → dispatch_session() → unchanged behavior.
  • Notifications are suppressed for script crons that don't declare a notify: field. Heartbeat keepalives shouldn't howl every two minutes.

Backwards compatible: any cron without an explicit action continues to be treated as a session, matching the pre-patch behavior.

Test plan

  • Wolt with "action": "script" (Loup's apps-keepalive, every 2 min): rebuild container, wait for next */2 tick, all 5 services come up within 15 seconds. Confirmed twice across two consecutive rebuilds.
  • Wolt with session-type cron (default action, prompt set): Claude Code session still spawns correctly via the original code path.
  • jobs.jsonl distinguishes action: "script" from action: "session" events.
  • Reproduces on v0.4.36 / current main (2ca2fd4) before the patch — every script cron in this colony's wolves was a no-op for ~6 weeks.

🐺 the wolf can speak bash again

fire_cron() was always calling dispatch_session() regardless of the action
field, so every cron with "action": "script" silently spawned an empty Claude
Code session with no prompt — dispatch_session bailed on the missing prompt,
the session was reaped, and the bash command never ran.

Adds dispatch_script() and routes fire_cron() by action type. Defaults to
"session" so any cron without an explicit action keeps current behavior.
Notifications stay quiet for script crons unless they declare notify: — no
need to howl every two minutes for a heartbeat keepalive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 21, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the jerpint's projects Team on Vercel.

A member of the Team first needs to authorize it.

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