Skip to content

Rolling out the bot

This page is for whoever runs the bot. By the end of it you will have reminders reaching a handful of testers, a way to see what the bot is doing, and a clear moment to open them to the whole server. It also lists what to check before you switch on each other feature. Gemini is Google’s AI service and Linear is the ticket tracker the team plans work in.

Jump to ticket prompts, asking, meeting transcripts, voice notes, the rollout steps, how to tell it is working or turning it off.

REMINDER_ALLOWLIST limits every DM the bot starts on its own: reminders, follow-ups, setup DMs, scheduled summaries, interest digests, ticket prompts, the action item DMs of meeting transcripts and the result a voice meeting that ended by itself sends to whoever started it.

It does not limit /summary now or /interests now, which reply only to the person who ran them. It does not limit asking: once asking is on, every member can use /ask and tag the bot with a question. It does not decide who may upload a transcript or start voice notes.

A member gets channel reminders only when all three are true:

  • They have a timezone and working hours. See Timezone and working hours.
  • They have not left the server.
  • They are on the allowlist, if you set one.

A member missing a timezone or working hours gets one setup DM instead, once ever, never repeated even if it fails to send. At most 25 go out per check.

Example of the bot in Discord

Doge Bot sends you a DM

Hi Sam, set your timezone and working hours so reminders reach you at the right time. Open the server and run /hours set in any channel with your start time, end time and timezone.

The greeting drops the name when the bot has none: Hi, set your timezone ....

Set TICKET_PROMPTS_ENABLED to true. Ticket prompts also need GEMINI_API_KEY and a LINEAR_API_KEY (an API key from Linear) that can read and create issues in every team members may file into. See Configuration. Set the allowlist first.

QuestionAnswer
Who gets prompted?Only the author of a new message of 8 or more words that proposes a feature the bot finds no matching Linear ticket for. If the Linear search is rate limited or times out, the bot prompts anyway. With an allowlist set, only authors on it are checked at all. Edits, old messages and bots are ignored.
How is the prompt sent?As a DM to the author. Nobody else sees it.
How often?At most TICKET_PROMPT_DAILY_CAP prompts per member per rolling 24 hours (default 3, 1 to 50). A rolling 24 hours counts back from now, not from midnight.
How many checks reach the AI?At most TICKET_PROMPT_MAX_CHECKS_PER_DAY per rolling 24 hours across all members (default 2000, 1 to 100000). Each author is also limited to 20 checked messages per hour. Both counts live in memory, so a restart resets them.
Which teams?A member linked to a Linear user is offered their own Linear teams, and the ticket is assigned to them. Anyone else is offered all public teams and the ticket is unassigned. Private teams are never searched, and are offered only to a member of them. See Linking members to Linear.
Which team is preselected?When a member is offered two or more teams, the AI reads the idea and the team names and descriptions and the form opens on the team it thinks fits. It is only a default: the member can change it, and nothing is filed without the form. Linked members’ private team names and descriptions are sent to Gemini for this.
Which labels?Up to three, chosen by the AI from labels that already exist for the team. The bot never creates a label.
Cycles?A cycle is Linear’s name for a sprint. The form shows a Cycle field only when an offered team has cycles on in Linear. The member picks No cycle, Current cycle or Next cycle, and the key must be allowed to read cycles.
Who can see a created ticket?Everyone in your Linear workspace. The ticket holds the member’s display name, Discord id and a link to their message.

Nothing is filed until the member submits the form. Link your members before you open prompts to everyone (step 6 below). See Ticket prompts for what members see.

Set ASK_ENABLED to true. Asking needs GEMINI_API_KEY and does not use Linear: it searches Discord messages and meetings only. The allowlist does not apply, so everyone can ask the moment you switch it on.

QuestionAnswer
Who can ask?Every member, with /ask or by tagging the bot with a question.
What does an answer use?Only channels the asker can view right now, and meetings posted in those channels. A public answer is narrowed further to channels everyone can read. See What the bot stores.
Where is the answer?/ask answers in a reply only the asker can see, unless public is set. A tag question is always answered in the channel, as a reply to the message.
How much?At most ASK_MAX_PER_MEMBER_PER_DAY questions per member (default 50, 1 to 1000) and ASK_DAILY_CAP across everyone (default 500, 1 to 100000), counted over a rolling 24 hours from when the question is claimed. A member can have one question running at a time, and the bot answers four at once, then says it is busy.
How many tags are read?At most 20 per member per hour and MENTION_MAX_CHECKS_PER_DAY across everyone (default 2000, 1 to 100000) reach the AI to tell a follow-up from a question. Both counts live in memory, so a restart resets them. Past either limit a tag on a reply is treated as a follow-up request and any other tag is ignored.
What permissions?To answer a tag in a channel the bot needs View Channel, Send Messages (Send Messages in Threads in a thread), Embed Links and Read Message History. Without them it stays silent there.
What is kept?A record of each question: the question, the search arguments and the ids of what the bot found, the model, the token counts and the thumbs up or down. Never the answer or any message text. It is deleted after ASK_LOG_RETENTION_DAYS days (default 30, 1 to 3650), at every start and then daily.

Nothing is posted in a channel unless the asker tags the bot or sets public. See Asking the bot questions for what members see.

Set MEETING_TRANSCRIPTS_ENABLED to true. Transcripts need GEMINI_API_KEY. LINEAR_API_KEY is optional: without it the bot posts the summary with no ticket links, sends no action item messages and creates no tickets. Set the allowlist first.

QuestionAnswer
Where does it work?In one text channel, ai-meeting-transcripts unless you set MEETING_CHANNEL_NAME or rename it with /notes-admin summary-channel set (which works whenever meeting transcripts are on). The bot does not create it at startup. With voice notes on, the first voice meeting creates it, which needs Manage Channels in the server. With voice notes off, create the channel by hand with the same name, or uploads are ignored.
What permissions?View Channel, Send Messages and Read Message History in the channel.
Who can see the summary?Everyone in the server: the bot creates the channel with no permission overwrites, which assumes one server. A voice meeting in a private voice channel is posted in that channel’s own text chat instead.
Who can upload?Anyone who can post in the channel. The allowlist does not stop an upload from being read and sent to Gemini.
Who gets action item DMs?The people the meeting names as owners, when exactly one member has that name, and the uploader for the rest. At most 10 people are messaged per transcript, and the rest go to the uploader. Items open already selected. Nothing is sent without LINEAR_API_KEY. With an allowlist set, only people on it are sent a DM. The uploader’s reply counts people whose DMs failed or are closed, not people the allowlist left out.
Can it change existing tickets?Yes, only when an owner presses the button. An action item for work already tracked is offered as an update: the bot sets the status to Backlog, Todo, In Progress or In Review, or adds a comment. It never sets Done or Canceled, and a team with no matching state gets a comment instead. The person pressing needs an admin-made link to a Linear user and membership of the ticket’s team, otherwise nothing changes. See Linking members to Linear. The key must be allowed to update issues and add comments.
How much?At most MEETING_MAX_PER_DAY transcripts in any rolling 24 hours across everyone (default 20, 1 to 1000), 5 per person per day, 10 uploads per person per hour, and files up to MEETING_MAX_FILE_KB (default 512, 1 to 2048). The counts live in memory, so a restart resets them. A run that ends because Gemini was unavailable, or because the bot shut down, gives its slot back; other failures keep it, since the model may have been billed. When Gemini is down, uploads pause for a minute.
Which team and cycle?The team comes only from a team name the transcript states. When it names none and there is new work to file, the AI may suggest a public team, which only moves that team first in the owner’s team picker. Each owner can also pick No cycle, Current cycle or Next cycle for the new tickets that press creates. An update never moves a ticket into a cycle.
What is kept?The summary, attendee names and action items stay. The transcript text is deleted after MEETING_TRANSCRIPT_RETENTION_DAYS days (default 90, 1 to 3650), at every start and then daily.
Who can see a created ticket?Everyone in your Linear workspace. The ticket holds a link to the approver’s Discord profile and a link to the summary.

Nothing is filed or changed until the owner presses Create selected (or Create ticket for a lone item, or Apply selected when every item is an update). Tell people the channel is public before they upload, and that text typed there is stored like any message. See Meeting transcripts for what members see.

Set VOICE_NOTES_ENABLED to true, on top of meeting transcripts. The bot never joins a voice channel by itself: a member in the channel runs /notes start. Set the allowlist first.

QuestionAnswer
Who can start it?Anyone in the server who is in a voice channel, unless an admin blocked that channel with /notes-admin exclude. A member with Manage Server can still start in a blocked channel.
Who can stop it?Anyone in the call, the person who started it, or a member with Manage Server. It also stops when the channel empties, at VOICE_NOTES_MAX_MINUTES or when the bot is disconnected.
Who is recorded?Everyone who speaks while the bot is in the channel. The bot posts a notice in the voice channel’s chat. There is no per-person opt-out, so leave the channel.
Who can see the summary?An open voice channel (everyone can see and join it): everyone in the server, in the shared summary channel. Any other: only the people who can access that voice channel, in its own chat.
What leaves the bot?The speech goes to the PlaySafe transcription hosts (the speech-to-text servers set in ASR_HOSTS), held in memory only. The text goes to Gemini. No audio is kept.
How much?Voice meetings count with transcripts: MEETING_MAX_PER_DAY across everyone and 5 per person who started notes, in any rolling 24 hours. One meeting at a time.
What if the bot restarts?A running meeting is saved and finished at the next start.
What permissions?Connect, View Channel and Send Messages in the voice channel. Manage Channels in the server only to create the shared summary channel.

See Voice notes for what members see and Voice notes for admins for the admin commands.

  1. Set the allowlist. Put your own Discord user id, and the testers’, in REMINDER_ALLOWLIST as a comma separated list. To copy an id, turn on Developer Mode in Discord’s Advanced settings, then right-click a member and choose Copy User ID. Leave it unset only when you want everyone. The variable and the others are on Configuration.

  2. Start the bot and read the log lines. The reminders line confirms what the engine loaded:

    [reminders] enabled, tick 15 min, late limit 60 min, allowlist 3 members

    tick is how often the engine checks for due reminders, and late limit is how long after its due time a reminder may still be sent. allowlist 0 members means everyone is eligible. [reminders] disabled means the switch is off. Each other feature logs its own line when its switch is on: [summaries] enabled, ..., [ask] enabled, 50/member/day, cap 500/day, retention 30 d, [tickets] enabled, ..., [meetings] enabled, ... and [voice] enabled, .... All but [ask] and [voice] show the same allowlist figure. The allowlist limits only the direct messages a voice meeting sends, so [voice] has no figure.

    off: needs ... instead of enabled means a key or switch is missing and the feature does nothing:

    • [ask] off: needs GEMINI_API_KEY
    • [tickets] off: needs GEMINI_API_KEY and LINEAR_API_KEY
    • [meetings] off: needs GEMINI_API_KEY
    • [voice] off: needs MEETING_TRANSCRIPTS_ENABLED and GEMINI_API_KEY

    The meetings channel is not checked at startup: confirm it exists yourself, or let a voice meeting create it.

  3. Check your rules. Run /reminder-rules list. Any rule showing not set for its channel does nothing yet. The standup rule fills itself only from a channel named stand-up with a hyphen. If yours is called standup, run /reminder-rules edit name: standup channel: #standup. Set channels as described in Reminder rules.

  4. Have testers set up. Each tester runs /hours set with a timezone, then /reminders list to see what applies to them. A tester who saves their hours in the middle of a work day and has not posted yet gets their stand-up reminder at once, which makes a quick check of the setup.

  5. Watch a day. A tester who has not posted in the watched channel gets a DM when their due time passes. Compare with the log, see below.

  6. Link members to Linear (ticket prompts and meetings). The bot has already matched members to Linear users by exact name, at startup. Run /linear-admin list and look at the Unsure and No match sections. Fix each one with /linear-admin link, so their tickets go to their own team and are assigned to them. An unlinked member still gets prompts, but chooses from all public teams and the ticket is unassigned. This works even while TICKET_PROMPTS_ENABLED is off. See Linking members to Linear.

  7. Open it up. Once the output looks right, remove the allowlist entries and restart the bot. Everyone who qualifies is now eligible.

Reminder activity is in the bot’s log. On the server, filter it:

Terminal window
docker compose logs bot | grep reminders
Log lineMeaning
[reminders] tick: sent=1 failed=0 followups=0 nudges=2Something was sent or failed this tick. A tick where all four counts are 0 logs nothing.
[reminders] reminder standup not delivered: dm_closedA DM could not be delivered. See below.
[reminders] Discord confirm failed for rule standup; will retry next tick: ...The bot could not read the channel to double-check a post. It sent nothing and tries again next tick.
[reminders] reminder standup failed after claim: ...An unexpected error after the bot claimed the reminder. It is recorded as failed with the reason internal_error.
[reminders] previous-post lookup failed for rule standup: ...The bot could not look up the member’s previous post. The reminder is still sent, without the link.
[reminders] tick failed: ...The whole check failed. The next tick tries again.

The four counts are: sent, channel reminders delivered; failed, channel reminder and follow-up DMs that could not be delivered; followups, follow-ups delivered; nudges, setup DMs delivered. A failed setup DM is not counted in failed. It logs its own [reminders] setup nudge not delivered: ... line.

Silence is normal. No log line means nothing was due, or everyone due had already posted.

A failed DM is recorded and not retried. The reason appears after not delivered:.

ReasonWhat it meansWhat to do
dm_closedThe member has server DMs off or has blocked the bot.Nothing to fix.
discord_<code>Discord returned another error. The number is Discord’s code, for example discord_10013.Note the code and the rule name.
Other textAny other send error, cut to 300 characters.Read the text.

Each reminder is attempted once per rule, member and work day. Fixing a closed DM setting does not trigger a second attempt for the day already missed.

  • Down time. If the bot is down for longer than the late limit, it does not send the reminders it missed. Follow-ups are the exception: they are sent however late. With reminders on, the bot will not start if REMINDER_LATE_LIMIT_MINUTES is below REMINDER_TICK_MINUTES, so the late limit is never shorter than one tick.
  • Weekends. A work day that starts on a Saturday or Sunday never produces channel reminders. The check uses the day the shift starts, so a Friday overnight shift, or a long wait after a Friday, can still send a reminder on Saturday. There is no holiday support.
  • Allowlist and tags. A tag sets a follow-up only while reminders are on and only for people the allowlist lets through. Anyone else gets no answer. A tag that is a question ignores the allowlist and gets a public reply whenever asking is on.
  • Allowlist and follow-ups. Members outside a non-empty allowlist can still set follow-ups. One that comes due while the member is outside the list is marked skipped and is not sent later, even if you widen the list. One not yet due when you widen the list is sent as normal.
  • Changed hours. If a member changes their hours after being reminded, they do not get a second reminder for the same day.

The full reasoning is in The reminder engine.

To stopDo thisEffect
One channel watch rule, for everyone/reminder-rules disableApplies from the next check. Nothing else changes. The follow-up rule cannot be disabled.
One reminder, for yourself/reminders toggleMembers can do this themselves.
All remindersSet REMINDERS_ENABLED to false and restartThe log says [reminders] disabled. No channel reminders, follow-up DMs or setup DMs go out. Scheduled summaries, ticket prompts and meeting transcripts have their own switches, SUMMARIES_ENABLED, TICKET_PROMPTS_ENABLED and MEETING_TRANSCRIPTS_ENABLED, and keep working.
AskingSet ASK_ENABLED to false and restartThe log says [ask] disabled. /ask is removed and a tag that is a question gets no answer. Answers already posted stay, and their thumbs buttons still work.
Ticket promptsSet TICKET_PROMPTS_ENABLED to false and restartThe log says [tickets] disabled. No message is checked and no new prompt is sent.
Meeting transcriptsSet MEETING_TRANSCRIPTS_ENABLED to false and restartThe log says [meetings] disabled. No upload is read, and voice notes are off with it. Action item messages already sent keep working while LINEAR_API_KEY is set.
Voice notesSet VOICE_NOTES_ENABLED to false and restartThe log says [voice] disabled. /notes is removed, /notes-admin stays while meeting transcripts are on, and the bot never joins a channel. A meeting running at the restart is saved first and is not finished while voice notes are off.

Follow-ups members set while reminders are off are kept. They are sent after you turn reminders back on, however late.