cujo
Skip to content
Contents

Discord notifications

Optional, bound by two halves that different people prove, and it decides nothing.

Two halves, proved by different people

Neither half alone does anything, and that is the point: without the repository’s half anyone could point a repository they do not own at their own channel, and without the server’s half anyone could push a repository’s reviews into a server they do not belong to.

HalfQuestion it answersProved byWhere
DeclarationWhich Discord server may have this repository’s reviews?Whoever can merge to the default branchdiscord_guild in .cujo.yml
BindingWhich channel, and which role gets pinged?A member with Manage Server/cujo watch, in that server

The declaration is read from the default branch and never from a pull request’s copy. Reading the merged branch is what makes it proof: code that declares its own authorization is not an authorization.

Setting it up

  1. 01
    Name the server in the repository.
    # .cujo.yml on the default branch
    discord_guild: "222222222222222222"

    Merge it. On an instance that serves a single Discord server, the operator can answer this half once in the environment instead, and no repository needs a commit.

  2. 02
    Bind a channel, from inside Discord.

    /cujo watch repo channel [role], run by someone with Manage Server. Cujo needs View Channel, Send Messages and Embed Links there.

Revoking is a commit. Removing or changing discord_guild is re-checked before every card, so a binding made before the edit stops delivering rather than running forever. A repository that declares nothing, and a repository whose file could not be read, are different facts — only the first revokes.

The commands

CommandDoes
/cujo watch repo channel [role]Sends that repository’s cards to that channel, pinging that role when a review blocks.
/cujo unwatch repoStops sending them. Deliberately the one command that does not require Manage Server: stopping is never gated.
/cujo statusWhere each watched repository currently goes, and the line to paste into another one.
/cujo test repoPosts a sample card. It exercises the token, the channel permissions and the rendering at once, which nothing else can do without waiting for a real pull request.

Every reply is ephemeral — only the person who ran the command sees it.

What arrives in the channel

  • One card per run, edited in place as the run moves. A new push is a new run and a new card; the old one is rewritten to say it was superseded. The card carries the head, the pull request, the findings, and what each check measured — tests done, 1 critical, 41s — with a zero written out rather than implied by an absence.
  • One ping when a run blocks on a person, as a second message. An edit notifies nobody, so the card going amber would be a notification nobody receives. When the run resolves, that message is edited in place and recoloured.

Only the configured role can be mentioned — never everyone, never a role somebody named in a pull request title. With no role configured the ping still posts, without a mention.

Nobody approves from Discord

This is notification and nothing else. Being in a channel is not a claim about a repository, and Discord membership is not repository write access. A block is lifted on the pull request, with /cujo dismiss.

The whole feature is optional. With no bot token configured, the service runs exactly as it otherwise would and says nothing.