Skip to content

fix(jobs): honor notifyChannel in job frontmatter for Discord routing - #254

Open
BCDel89 wants to merge 2 commits into
moazbuilds:masterfrom
BCDel89:bcdel89/job-notify-channel
Open

fix(jobs): honor notifyChannel in job frontmatter for Discord routing#254
BCDel89 wants to merge 2 commits into
moazbuilds:masterfrom
BCDel89:bcdel89/job-notify-channel

Conversation

@BCDel89

@BCDel89 BCDel89 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Job frontmatter supports (and several existing job files already set) a notifyChannel field intended to route completion notifications to a specific Discord channel.
  • The field was never actually parsed anywhere — Job had no notifyChannel property, and forwardToDiscord() unconditionally DM'd every user in discord.allowedUserIds, regardless of what the job file said.
  • Symptom: job runs successfully, log shows a message was sent, but it lands in the operator's DM instead of the intended channel.
  • Fix: parse notifyChannel in parseJobFile(), wire a channel-based Discord sender alongside the existing DM sender in start.ts, and route forwardToDiscord() to the channel when job.notifyChannel is set (falls back to existing DM-all-allowed-users behavior otherwise — no change for jobs that don't set it).

Test plan

  • bun test — 125 pass, 3 pre-existing/unrelated failures (env-dependent session-files.test.ts cases, confirmed unrelated on master)
  • New tests in src/__tests__/jobs.test.ts covering notifyChannel parsing (present + absent)
  • bunx tsc --noEmit — no new errors in touched files
  • Manual: run a job with notifyChannel set and confirm the completion message lands in that channel, not a DM

@TerrysPOV TerrysPOV left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, clean feature. The parsing is consistent with the other frontmatter fields, the routing correctly falls back to the DM-to-allowedUserIds path when notifyChannel is unset, it respects the existing notify: false / error gate, and discordSendToChannel is set up and torn down symmetrically. I confirmed sendMessage is exported from discord.ts, so the dynamic-import destructure resolves. And thanks for adding parse tests.

Three things before merge:

  1. The channel-routing behavior itself isn't tested — the tests cover jobs.ts parsing (present / absent), but not the forwardToDiscorddiscordSendToChannel path, which is the actual feature. Please either add a test that exercises the routing (inject a fake discordSendToChannel and assert a notifyChannel job forwards to the channel, and that an unset job still DMs allowedUserIds), or demonstrate it live — a job with notifyChannel set posting its output to the target channel on a real daemon. This is the same bar applied to #253: an output-routing feature should be shown working, not inferred from mirroring the DM path.

function forwardToDiscord(label: string, result: { exitCode: number; stdout: string; stderr: string }, notifyChannel?: string) {
const text = result.exitCode === 0
? `${label ? `[${label}]\n` : ""}${result.stdout || "(empty)"}`
: `${label ? `[${label}] ` : ""}error (exit ${result.exitCode}): ${extractErrorDetail(result) || "Unknown"}`;
if (notifyChannel) {
if (!discordSendToChannel) return;
discordSendToChannel(notifyChannel, text).catch((err) =>
console.error(`[Discord] Failed to forward to channel ${notifyChannel}: ${err}`)
);

  1. Document the allowlist-bypass semantics. notifyChannel output is sent to the named channel and is not gated by discord.allowedUserIds — everyone who can read that channel sees the job output. That's the intended behaviour, but it's a sharp edge (an operator could route sensitive job output to a public channel), so it's worth a line in the field's doc comment / the jobs docs so it's a deliberate choice, not a surprise.

  2. Version is stale — this targets 1.0.41 but master is 1.0.43 (the only thing making the PR conflict; jobs.ts / start.ts merge clean). Rebase and re-bump both .claude-plugin/*.json to 1.0.44.

BCDel89 added 2 commits July 19, 2026 15:53
Job files could set notifyChannel in frontmatter, but the field was never
parsed or read anywhere — forwardToDiscord() always DM'd every allowed
user regardless. Parse notifyChannel in parseJobFile and route the
completion notification to that channel instead of DMing when set.
Channel-routed output isn't gated by discord.allowedUserIds — anyone
with access to the channel sees the job's output. Call that out in the
Job interface doc comment and commands/jobs.md so it's a deliberate
choice per job, not a surprise.
@BCDel89
BCDel89 force-pushed the bcdel89/job-notify-channel branch from fcaac43 to 0ad7bea Compare July 19, 2026 20:53
@BCDel89

BCDel89 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Addressed:

#3 (version stale) — rebased onto current master (1.0.44), re-bumped to 1.0.45.

#2 (allowlist-bypass semantics) — documented in the notifyChannel doc comment (src/jobs.ts) and added a section to commands/jobs.md explaining it's not gated by discord.allowedUserIds.

#1 (untested routing) — going with the "demonstrate it live" option rather than a mocked unit test, since the fix has been running in production since the original PR merged and the completion-notification path (not just the agent's own in-job posts) has fired repeatedly:

  • [blog-daily] — 2026-07-17 12:01, 2026-07-18 12:01, 2026-07-19 12:01 — message IDs 1527736883906609314 (approx, same batch), 1528008713753985175, 1528371114072477800
  • [blog-daily-pm] — 2026-07-17 18:01, 2026-07-18 18:01, 2026-07-19 18:01 — message IDs 1527736883906609314, 1528099325333340255, 1528461733948297287
  • [blog-daily-evening] — 2026-07-17 22:00, 2026-07-18 22:00

All landed in the sage-blogger channel (the job's notifyChannel), with content literally prefixed [job-name] matching forwardToDiscord's exact output format — confirming the completion-notification path itself is routing correctly, not just agent-initiated posts during the job run. Happy to switch to a mocked test instead if you'd still prefer that over log/message evidence.

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.

2 participants