# Slack Daily Brief

> A five-line Slack brief that leads with what changed — and says so in one line when nothing did. You approve every post; it never schedules.

- Agent: Claude Code
- Plan: Starter (1 vCPU, 2 GiB RAM, 10 GiB disk)
- Forks: 0 · Stars: 0
- Creator: https://www.jurniti.com/creator-ca0ff2
- Tags: automation, briefing, claude-code, reporting, slack, small-business
- Requires config (you provide): ANTHROPIC_API_KEY
- Skills: slack-brief

**To fork:** open https://www.jurniti.com/templates/slack-daily-brief and use the Fork action — it starts a Stripe Checkout for a new VM seeded with this template's persist disk (same flat plan price, no fork fee).

## README

# By week two, everyone muted the channel

The automated digest posted the same seven-row status table every morning. Then the
one morning something actually broke, nobody read it.

## Lead with what changed, or say nothing changed

**If nothing material changed, the brief is one line saying so.** That's a correct
output, not a failure — and it's the single biggest reason a digest survives past
week two.

Five rules keep it readable:

1. **Lead with the delta**, never a status table
2. **Five lines maximum** — a digest that scrolls gets collapsed, then muted
3. **Every number carries its comparison** — vs yesterday, vs last week, or it isn't
   worth a line
4. **Name the one decision needed**, or say there is none. A brief with no ask is a
   newsletter.
5. **No `@channel`, no `@here`.** Ever. A person is mentioned only when the brief
   names an action that's theirs.

It reads yesterday's brief out of `~/slackbrief/sent/` to compute the delta. That
file is what makes this about change instead of state.

## A human approves every single post

There's no schedule and no standing permission. It drafts, stops, and shows you.
You say post, and it posts *that one brief*.

Ask it to run this daily on its own and it refuses — an agent with standing
permission to post to a team channel is one bad parse from telling everyone the
wrong number, and nobody reviews the fifth one.

## It won't fill a gap to keep the format

A missing or stale source shows as `[no data: source]`. Never yesterday's number
carried forward, never a silently dropped line — silence reads as "zero" or "fine",
and both may be wrong.

## Post-only, one channel

The webhook can post and nothing else. No read scopes, no channel listing, no DMs,
no user lookup. Anything needing to *read* Slack is out of scope and it says so.

The webhook URL never appears in a draft, a log, or a report — and the build scan
proves it never lands on disk.

## What you supply

`SLACK_WEBHOOK_URL` — an Incoming Webhook for one channel — plus your own Claude
Code login. Sources are whatever you drop in `~/slackbrief/sources/`: a metrics CSV,
a P&L export, a task list.

## Where this stops

It drafts and, on your word, posts one brief. It does not schedule, does not post
unattended, and cannot read Slack.

It won't state a number it didn't read this run, won't guess a cause (`cause
unknown`), and won't comment on a named colleague's performance — briefs report
systems, not people.

Verified on build: the skill, the working tree, the `HUMAN_APPROVES_EVERY_POST`
guard, all five brief rules and the never-invent rule are present on a fresh fork,
and no webhook URL exists anywhere in the snapshot. Running it on your own sources is
yours to do.
