aipmgithub
suggest-only · you approve everything

a 10x pm for everyone.
always in the background.

it watches every GitHub and Slack thread and finds who owes a reply. you get a draft for their dm, and nothing sends unless you send it.

+ suggest-only+ low-noise+ deterministic+ off-by-default
pr #482no reviewer · 4hissue #511@mentioned · 1d#supportowes a reply · 2haipmevaluates · drafts
drafted · ready for you
r
@rui
Slack dm
re #482
#482 is open 4h with no reviewer. want me to grab one?
snooze
next@maya #511@sam #support
~/aipm
# replay captured webhook + Slack payloads
# ship to Cloudflare's edge
$
a human approves every nudge. nothing leaves the Worker on its own.
get started

three commands

set three secrets, point both webhooks at the Worker, then deploy. that's the install.

  • one org-wide GitHub App, so every repo's threads show up.
  • one Slack app, every channel it's invited to.
cloudflare-native

one primitive per concern

every concern maps to a Cloudflare primitive. no servers, no queue to host, no database to run.

webhook + Slack ingressWorkers
staleness + draft-age sweepsCron Triggers
decouple ingest from the restQueues
serialize per-thread updatesDurable Objects
relational stateD1
delivery-id dedupe, flagsKV
summaries + judgmentWorkers AI · AI Gateway
ship itWrangler
platform-neutral

one interface, any platform

github and slack are just adapters behind one interface. implement it and the whole engine runs on a new platform — the llm provider is an adapter too.

githubslack+ anything
platform.ts
interface Platform {
  id: PlatformId;
  listThreads(query): Promise<Array<Thread>>;
  getThread(nativeId): Promise<Thread>;
  getTimeline(nativeId): Promise<Array<TimelineEvent>>;
  discoverLinks(thread): Promise<Array<Link>>;
  postMessage(target, body): Promise<{ id: string }>;
  editMessage(messageId, body): Promise<void>;
  react(messageId, emoji): Promise<void>;
  notifyPerson(identity, body): Promise<void>;
}
aipm engine
how it works

five stages, queues between them

a thread moves through one stage at a time. one Durable Object per thread serializes the work, so two events can't make two nudges. the engine never names GitHub or Slack. both are adapters.

01
ingest

a webhook or sweep lands. the adapter turns it into a thread, then upserts links and participants.

WorkersKV
02
evaluate

detectors read the timeline and decide what's owed. no model runs here. signals fire or clear.

Workers
03
synthesize

it rewrites the thread's notes comment. it re-posts only when the content hash changed.

Workers AIAI Gateway
04
route

open signals become nudges. apply prefs, pick the channel by priority, dedupe, back off.

Durable ObjectsD1
05
aggregate

what wasn't worth a dm rolls up. one digest per person, one cluster-notes summary per org.

CronQueues
the model is on a tight leash. it runs in two places: judging if a reply answered, and wording the nudge. everything else is plain logic.
01
suggest-only

it edits its own things only. its notes, its dms. anything on your turf is a draft you approve with one reaction.

02
low-noise

one nudge per person, thread, and signal. a second one buys nothing, so it drops to a digest. mute and snooze always win.

03
deterministic

plain logic decides what's owed. the model does two things: judge if a reply answered, and word the nudge.

04
off-by-default

every capability ships turned off. you turn each one on when you trust it, one at a time.

the detectors

seven things it watches

each one is plain logic over the timeline. every threshold is config. the quiet window counts business days. when a thread closes, its signals clear.

@mentioned, no response
1 business day
triggerwebhooknudgesmentioned personviadm / digest
clears when they reply
review requested
1 business day
triggerwebhooknudgesreviewerviadm
clears when review submitted
unaddressed review comments
1 business day
triggerwebhooknudgespr authorviadm
clears when author replies / pushes
pr open, no reviewer
4h
triggerwebhook + sweepnudgesauthorviadm
clears when reviewer added
draft pr aged
> 7 days
triggersweepnudgesauthorviadigest
clears when marked ready / closed
in-progress stale
> N days
triggersweepnudgesowner / assigneeviadigest (dm if high pri)
clears when thread updated
blocker cleared
immediate
triggerwebhooknudgesblocked thread's ownerviadm
clears when fires once
and your own

signals are data. add a detector. a contract test feeds it a timeline fixture and checks the right signal comes out.