We use cookies to measure site usage with Google Analytics. We only turn that on if you say yes. See our Privacy Policy.

Documentation

Brain Task Reminders

Open tasks a brain is waiting on, tracked in one place and gathered into a single daily digest at the time you choose, by email or in-app.

A brain often needs something it doesn't have yet — a photo, an answer only one person has, a document to upload. Brain task reminders give that "waiting on" state a durable home: it's tracked, mirrored where you can read it, and swept into reminders on its own schedule instead of depending on someone remembering to bring it up.

Opening and closing a task

Two MCP tools do the work mid-session:

  • open_brain_task: description, addressee ("owner" or "share"), due_date (optional, YYYY-MM-DD), and schedule (optional; see Cadence below). brain_id is optional too: omit it for a plain to-do and it resolves to your account's default to-do brain automatically; see Agent-First Onboarding for that and the onboarding.next_step mechanism these tools carry. Pass brain_id explicitly to file the task against a specific named brain instead. Addressing a task to "share" requires a share_id from a prior share_brain call; the recipient must already be shared with the brain.
  • close_brain_task — task_id. Closing removes the task from the next digest, the brain's note, any still-staged reminder for it, and the top-of-session surface, and ticks its box in the brain's Obsidian checklist if it has one.
open_brain_task({
  brain_id: "<uuid>",
  description: "Photo of the front lawn, wide angle",
  addressee: "owner",
  due_date: "2026-10-01",
  schedule: "remind me Friday"
})

Where open tasks show up

  • The brain's compiled note carries an ## Open Tasks section, split into "Yours" (owner-addressed) and "Owed by others" (share-addressed), regenerated from the table — never hand-edited. It updates immediately when a task opens, closes, or reopens, not just on the next full compile.
  • The admin UI — /admin/brain-tasks (linked from /admin/brain) lists every open task across your brains, with Close, Silence, and Reopen actions, plus a Staged Reminders panel described below.
  • The next session with that brain surfaces open tasks at the top, via the same compiled-note section get_brain returns.

An Obsidian checklist for a brain

A brain can keep its tasks in a note in your Obsidian vault, as ordinary checkboxes. Pick the note on /admin/brain-tasks under Obsidian checklists, or call patch_brain with tasks_note_id. The note has to be one that lives in your vault, so the file stays yours: BlackOps edits the task lines and leaves every other line alone, apart from the email redaction every BlackOps write to your vault applies.

Each task line ends in a block ID that links it to its task:

- [ ] Reconnect LinkedIn 📅 2026-10-09 ➕ 2026-10-06 ^bt-<task id>

The dates use the Obsidian Tasks plugin's format, so its queries and date pickers keep working. From there the two sides stay in step:

  • Tick a box in Obsidian and the task closes in BlackOps once the change reaches GitHub. It drops out of the digest, the brain's note and any staged reminder. This works from any note BlackOps syncs with your vault, not just the checklist.
  • Close a task anywhere else (the admin UI, close_brain_task, a reply to a reminder, a done: line in a daily note's Work Log) and its box gets ticked, with the date it was done.
  • Untick a box in the checklist and the task reopens. Only the checklist can do this. An old daily note still shows tasks as open after they close, so an unticked box anywhere else changes nothing.
  • Type a new unticked line in the checklist and it becomes a task on that brain, with its 📅 date as the due date. If an open task already has the same wording, the line is linked to it instead of making a duplicate.
  • Open a task for yourself anywhere else and it is added to the bottom of the checklist. Tasks addressed to someone you shared the brain with stay out of it.

Deleting a line does not close its task. The line comes back the next time the brain's tasks change. Tick it instead.

BlackOps writes to the file as it is on GitHub at that moment, so it never overwrites an edit you pushed a second earlier. If you tick or untick a box at the same time a task changes in BlackOps, your change wins. Every checklist is also re-checked every 10 minutes, so a change that failed to sync (GitHub briefly unavailable, say) catches up on its own. Edits that are still only on your device reach BlackOps when your vault syncs.

The digest, once a day, at your delivery time

Reminders are gathered into ONE digest per day, at a delivery time you control — never at an arbitrary moment a task happens to become due. A 15-minute tick checks, per site, whether the owner's local delivery time has passed today and today hasn't already been gathered; once both hold, it gathers everything due and sends:

  • Owner-addressed tasks — a reminder goes out immediately, no staging, since nothing needs previewing for a message to yourself.
  • Share-addressed tasks — the reminder is staged behind a hold window (2 hours by default) rather than sent immediately. You get one digest email listing exactly what's queued and to whom, delivered before anything staged can possibly go out, with a one-click cancel link per item. Cancelling skips only that occurrence — the task stays open and comes up again next cycle. Silencing a task (from the admin UI) is the separate, explicit action for "stop asking about this" rather than "not today."

Delivery time defaults to 09:00 America/New_York and is stored per site (brain_task_settings); shared recipients inherit it rather than getting a setting of their own. Set both from Admin > Settings, under Reminders and Daily Digest — the timezone there is also the fallback timezone for daily-feed items that have no daily note timezone of their own. It's a separate field from the Highlights review timezone above it on the same page, which only controls the Daily Highlights Review email.

Cadence: when a reminder repeats

By default, reminder cadence backs off the longer a task goes unaddressed (starting daily, doubling up to a configurable cap), and tightens back to daily once a due date is close or has passed — the message states the age in days and, once due, how many days overdue. next_reminder_at always advances to the next scheduled calendar day, never to "now plus N days" — an off-schedule or manual send can't permanently shift a task off its slot.

Pass an optional natural-language schedule to open_brain_task for a specific rhythm instead:

  • "daily", "weekdays"
  • "remind me Friday", "every Friday", "once a week" — recurring weekly by default
  • "every 3 days", "every other day" — a fixed-day interval
  • "just this Friday", "once" — a one-off; after it fires, the task reverts to the automatic backoff rather than going silent

An unparseable phrase is rejected at write time with a clear error — it's never silently ignored. Whatever the cadence, a due date passing always overrides it: an overdue task is never reminded less than daily. The current cadence, in your original phrasing, is visible on the task in the admin UI and in the brain's mirrored note.

Replying closes the loop

A shared recipient answers by replying to the reminder email — no login required, the same inbound path share_brain already uses. The reply is classified before it closes anything: a strict, typed judgment (not prose-parsing) decides whether the reply clearly resolves the ask, only partially addresses it, asks a question back, or is unrelated. Only a clear resolution closes the task; anything else leaves it open, and a classification failure always fails open rather than silently marking something done. You can reopen a task from the admin UI if a reply closed something it shouldn't have.

Auto-close and check-ins

Two mechanisms stop a task from reminding indefinitely once the thing it's waiting on has actually happened elsewhere, instead of requiring someone to remember to call close_brain_task.

Linked tasks auto-close. If a task's description names a GitHub issue or PR (https://github.com/<owner>/<repo>/issues/<n> or .../pull/<n>), that link is recorded when the task opens. Before every digest gather, the linked issue or PR is checked against GitHub, and the task closes on its own the moment any of these is true:

  • the linked PR is merged
  • the linked issue is closed
  • a trigger comment — /apply by default, configurable — is posted on the linked issue by the repo's owner, an org member or a collaborator. Comments from anyone else, and from bots, are ignored, so on a public repo a stranger or an automated "reply /apply to proceed" prompt can't close your task

The close note names the evidence ("Issue #1304 closed", "PR #42 merged at …", "/apply comment on issue #1304 by @…"), and the digest email shows a one-line "Auto-closed N tasks" summary so you know why something you were expecting disappeared from the list. This needs a GitHub App installation on the linked repository; without one, auto-close silently never fires for that site rather than erroring. Only the first GitHub issue/PR link in the description is tracked — if a task mentions a reference issue before the one it's actually waiting on, point the real link first, or leave the other one as plain text.

Unlinked, owner-addressed tasks get a check-in instead. A task with no GitHub link, addressed to you (not to a shared recipient — a share-addressed task's reply already closes it, see below), gets a periodic check-in email with one link, rather than being reminded forever. The first check-in lands 5 days past the task's due date (or 5 days past opening, if it has none); if you don't answer, it checks in again every 5 days — the clock advances when the check-in is sent, so a cycle where the digest email itself fails to go out still counts toward that gap. The link opens a page asking Done, Still open, or Drop it — clicking the link itself does nothing but show that page, so an email security scanner fetching the link to scan it can't accidentally trigger an answer; the choice only applies once you pick a button on the page. Picking Done or Drop it closes the task (with a note saying which); picking Still open just resets the clock for another 5 days. Each check-in's link is single-use — answering it once is final.

A task is one or the other, never both: adding a GitHub link turns off the check-in clock, and a task with no link is never touched by auto-close.

Configuration

Most thresholds are environment variables with sane defaults — see BRAIN_TASKS_HOLD_WINDOW_MINUTES, BRAIN_TASKS_BASE_INTERVAL_DAYS, BRAIN_TASKS_MAX_INTERVAL_DAYS, BRAIN_TASKS_DUE_SOON_DAYS, the BRAIN_TASKS_JEV_* reply-classification knobs, BRAIN_TASKS_AUTO_CLOSE_TRIGGERS (comma-separated trigger comments, default /apply), BRAIN_TASKS_CHECKIN_FIRST_DAYS (default 5), and BRAIN_TASKS_CHECKIN_INTERVAL_DAYS (default 5). None require a redeploy to change.

Delivery time and timezone are the one exception: they're per-site data (brain_task_settings), not an environment variable, since every site can want a different delivery time. Both are editable from Admin > Settings > Reminders and Daily Digest (GET/PATCH /api/admin/brain-tasks/settings), admin-gated and scoped to the admin's own site.

The Tasks API: listing, updating, and reopening

Beyond opening and closing a task mid-session, a full API lets any front end (a dashboard, a bot, a script) read and manage tasks across the workspace.

list_brain_tasks (MCP) / GET /api/v2/tasks (REST): lists tasks workspace-wide, or scoped to brain_ids. Filters: status ("open", "closed", or "all"; omit it for "open", or for "all" when changed_since is given, since a delta poll needs to see a task that just closed), due_before / due_after (YYYY-MM-DD), addressee, tags, and changed_since (an ISO timestamp; only tasks updated at or after that instant). Pagination is keyset-based: pass the returned next_cursor (or cursor in REST) to fetch the next page; a null next_cursor means you have everything.

list_brain_tasks({ status: "open", tags: ["client"] })

A front end that polls for changes passes back the updated_at of the newest task it has already seen as changed_since, and gets back only what changed (open or closed) rather than re-fetching everything. updated_at also bumps on reminder bookkeeping (a sent reminder, a bumped reminder_count), not only on a content edit, so a changed_since poller will occasionally see a task "changed" with nothing a user would call a change. A poller that only cares about content should compare the fields it tracks, not just react to the task showing up in the feed.

GET /api/v2/tasks resolves to a single site (explicit site_id/domain, or the token's default). PATCH/reopen/close instead look the task up by ID across every site the token can access, so a task ID works regardless of which site you listed it from.

update_brain_task (MCP) / PATCH /api/v2/tasks/:id (REST): updates description, due_date, schedule, or tags on an open or closed task. Pass only the fields that change. due_date: null clears the due date; schedule: null reverts the cadence to the automatic age-based backoff. An unparseable schedule phrase is rejected with a clear error, never silently ignored. The task's Obsidian checklist, if any, is kept in sync.

reopen_brain_task (MCP) / POST /api/v2/tasks/:id/reopen (REST): reopens a closed task with the cadence it had before closing. open_brain_task and close_brain_task (above) round out create and close.

Read-only access

A front end that only needs to read tasks (a dashboard, a bot) shouldn't hold a token that can also write them. Mint one from /admin/tasks/read-token (admin-only); it issues an API token scoped to read, which GET /api/v2/tasks and list_brain_tasks accept normally, but every write endpoint above (and open_brain_task / close_brain_task) rejects with 403. A token created before scopes existed, or one without a scopes array at all, keeps full access; the restriction only applies when a token explicitly carries scopes that don't include write.

This is a general read-only API token, not a tasks-only one. The write-scope gate lives in validateV2Auth, so it applies to every v2 route, not just tasks, but by the same token, a scopes: ['read'] credential can GET anything else the account can reach (posts, notes, and so on), not only tasks data. Treat it like any other API token: give it only to a front end you trust with read access to the whole account, and revoke it from /admin/tasks/read-token if that front end is retired. A future, narrower tasks:read scope is possible but not built in phase 1.

Tags

v1 adds one tags column (free-form strings); no priority or area fields yet. list_brain_tasks/GET /api/v2/tasks filter by a tag list with OR semantics (any overlap matches). update_brain_task/PATCH trims, dedupes, and caps what you send (20 tags, 64 characters each) rather than rejecting an over-limit request.

Notes

  • limit above the maximum (200) is clamped, not rejected, on both list_brain_tasks and GET /api/v2/tasks.
  • Changing schedule on update_brain_task/PATCH resets the task's next reminder to now, so a task moved to a tighter cadence doesn't wait out its old slot.

Related

  • Agent-First Onboarding: the default to-do brain, agent attribution on each task, and the onboarding.next_step suggestions these tools carry.
  • Daily Feed: your open owner-addressed tasks also appear in the daily feed, so the daily note can list them alongside queued todos.
  • Brain Triage Lane: a task is something a brain is waiting on from a person. Triage is different — it's content a brain is waiting to decide about.
Want this page as machine-readable markdown? GET /docs/features/brain-task-reminders.md