AI teammates you call by name in Basecamp.

Mention Jack Sparrow on a Basecamp card and a minute later he answers there: a number, a log line, or a plan that becomes a pull request.

Chirag Lathiya · AI Agency · Automation ·

We have a teammate called Jack Sparrow. Nobody opens a dashboard to use him and nobody installs anything. You type @Jack Sparrow in a Basecamp comment, ask what you would ask a colleague, and a minute later there is a reply on the same card: a number pulled from the database, the line in a log that explains a failure, or a plan for a fix that becomes a pull request once you agree to it.

Jack is not a chat window bolted onto a project tool. He is a small program that lives on our own server, wakes when his name is used, and works inside the same limits we would give a new engineer on their first week. He has company. Itachi works the same way on another board, and Naruto sits in our customer WhatsApp groups. This post is how we built them, what they are allowed to touch, and what went wrong on the way.

What is an AI teammate in a project management tool?

An AI teammate is an agent with its own account in the tool your team already uses, so people reach it the same way they reach a person: by name, on the card where the work is. It reads the thread, does the work it is allowed to do, and answers in a comment that everyone on the card can see.

That last part is the point. The request, the answer and any approval live on the card, next to the work, where the rest of the team will find them later. Nobody has to learn a new tool, and nothing the bot says happens in a private window.

We run three of them:

  • Jack Sparrow works on the Feedspace board. He answers questions about usage and data, reads the scraper code and its logs, plans and makes code changes, and can send a broken scraper to our repair pipeline.
  • Itachi works the same contract on another board: mention him to ask, assign him a card to have it built.
  • Naruto lives in the WhatsApp groups we share with customers. He reads the conversation and reports what he saw to our internal system, which decides what to do with it.

How does mentioning a name wake an agent up?

Jack checks his Basecamp inbox every 60 seconds, and a webhook gets him there sooner. The webhook does not carry instructions. It only says that something happened, and Jack then reads the card himself.

  1. @mentionon a Basecamp card
  2. Waketimer or webhook
  3. Read the cardnot the notification
  4. Do the workone lane, one tool set
  5. Reply, mark readin that order
One mention, start to finish. The notification only rings the bell: the agent reads the card itself and marks the mention read after its reply is posted.

The full loop for one mention:

  1. A timer on the server starts a run every minute. Separately, a small relay receives Basecamp's webhook for new comments and card assignments and writes one line to a queue file, which starts a run within seconds.
  2. Jack lists his unread notifications.
  3. For each one, he fetches the whole thread again from Basecamp rather than trusting the notification text.
  4. He only acts on a comment that mentions him by name and was written by somebody else.
  5. He works out what kind of request it is, does the work, and posts one comment.
  6. Only after that comment is posted does he mark the notification as read.

There is no database of what he has answered. The unread inbox already is one. If a run dies halfway through, the notification stays unread and the next run picks it up, so nothing is answered twice and nothing is silently dropped.

The webhook exists for a reason we did not expect. Basecamp sends no notification when a card is assigned to someone, so "assign the card to Jack and he builds it" cannot be seen by polling at all. Here is the shape of the two pieces of scheduling, with our paths taken out:

# agent.timer: start a run a minute after boot, then every minute
[Timer]
OnBootSec=60
OnUnitActiveSec=60
Persistent=false

# agent-wake.path: start a run the moment the relay writes to the queue
[Path]
PathChanged=/run/agent/wake
Unit=agent.service

Persistent=false is deliberate. A missed minute does not need catching up, because the unread notification is still waiting, and catching up would only produce a burst of runs after a reboot.

Why run the agent on your own server instead of GitHub Actions?

Because the data Jack reads is on that server and should stay there. Our other card agent, the one in From a Basecamp card to a pull request, runs on GitHub Actions and works only on code. Jack answers questions about live data, and the endpoint he queries accepts connections from the same machine only. Running him on a hosted runner would mean opening that endpoint to the internet.

Two smaller reasons settled it. Basecamp's command line login is stored once and refreshes itself, which a fresh runner per job cannot do without minting a token every time. And reading the server's own logs is easiest from the server.

The cost is that the agent shares a machine with production, so we paid for that in isolation:

  • Its own system user, with memory and CPU caps sized for the actual box.
  • No database password. Jack cannot read the application's environment file at all. We check that by trying, and the read must fail.
  • An allow-list for the model's environment. The model only sees the variables we name, so a secret added next month is hidden by default rather than by somebody remembering.
  • Session logs that expire after a week, because a session record holds every tool result, and those can include customer data.

What can the agent read, and what is it never allowed to touch?

It can read data, logs, code and web pages, each through a narrow tool built for that job. It cannot change anything without a person approving a plan first, and some parts of the codebase are off limits even then.

ToolWhat it gives the agentThe limit
Data queriesWhat it gives the agent: Answers about usage, accounts and importsThe limit: Read only, one query at a time, a time limit, a cost check that refuses a full scan of a big table, and a confirmation step above about 200,000 rows examined
Log readerWhat it gives the agent: The recent logs of five named servicesThe limit: Five services by name only, a hard cap of 2,000 lines, and credentials stripped from the output before it can reach a card
Page fetchWhat it gives the agent: What a website returns to our server right nowThe limit: One GET request: a URL and a byte count, no method, body, headers or output file, at most three redirects
Fetch tiersWhat it gives the agent: Whether a blocked page opens in a browser from our serverThe limit: Plain HTTP first, then browser impersonation, a real browser and a stealth browser, cheapest first and never a paid service
CodeWhat it gives the agent: The scraper source and the repo's own notesThe limit: Read only when answering a question

The data limits matter more than they look. We have no read replica, so the server Jack queries is the one answering live customer traffic. The safety rules sit in the endpoint, not in the agent's instructions, so a cleverly worded comment cannot talk its way past them.

The log reader is narrow for the same reason. The person asking is writing a Basecamp comment, and the agent turns that sentence into a command. With five named services and a line cap, the most an instruction hidden in a comment can choose is which of five logs to read.

One honest caveat. Leaving a shell out of the agent's tool list is not a sandbox. The coding tool still offers some commands and approves what it considers safe. What actually limits a code change is a check after the model finishes, which the next section covers.

How does a Basecamp comment become a pull request?

In two passes, with a person in between. The first pass reads the code and posts a plan. The second only starts when the person who asked replies to approve that plan, and it builds exactly what was agreed.

  1. Requesta comment on a card
  2. Planread only, posted
  3. APPROVEa person replies
  4. Buildedits a fresh copy
  5. Checkpaths, validator, run
  6. Pull requesta person merges
From comment to pull request. Nothing is edited until a person approves the plan, and the agent never merges its own work.
  1. Plan. Jack reads the relevant code with read-only tools and posts what he intends to change, then stops.
  2. Approve. The person who asked replies on the card. A reply of "go with the plan and raise the PR" counts even without a mention, as long as it mentions nobody else.
  3. Build. Jack edits a fresh copy of the repository with edit tools only. Every git command after that, the branch, the commit and the push, is run by our code, not by the model.
  4. Check. Our code compares the changed files against a list of places the agent may never edit: its own program, the CI workflows, the repair pipeline's safety gates, the rules every AI session in the repo reads, and the hooks that block secret reads. A changed scraper must also pass the same structural validator we run by hand, and since mid September the changed scraper is also run before it may become a pull request.
  5. Review. The pull request goes up for review. A person merges it, or closes it.

We added the plan step after a pull request went straight from one model pass to review, and review found three things a person reading a plan would likely have caught first: a wrong assumption about an existing variable, a sibling scraper that was not checked, and a changed field whose other users were never traced. A plan costs one extra comment. A missed assumption costs a review cycle.

Anyone on the board can ask for a change, because the worst a stranger can cause is a pull request somebody closes. Six pull requests opened by Jack from Basecamp cards have been merged so far.

How does the agent fix a broken scraper on its own?

It hands the job to a separate repair service with stricter rules. Saying heal trustpilot on a card, or just "threads stopped working, please fix it", puts that scraper on a queue that the repair service drains every five minutes.

The repair service, live since 9 September, does one repair at a time, because two browsers at once is what restarts the server. For each scraper it:

  1. checks the server has room to run, and waits if it does not,
  2. reads what has already been tried for this scraper,
  3. fetches the real page from our server, cheapest method first,
  4. tries a known fix, then asks the model for a patch on a copy of the code,
  5. measures the result itself rather than taking the model's word for it: for a broken selector, the fixed scraper has to fetch the real page and extract at least one review,
  6. hands over a pull request, a card explaining what it found, or a question on the card.

When it needs a human answer, it asks on the same card the request came from, and a reply there resumes it. Only three people can start a repair, because a repair spends paid scraping credit the moment it is queued, with no pull request in front of it to review.

When the request is in ordinary words, the model only decides that it is a repair request. Code then works out which scraper from the comment, the card title and the card body, and asks one question if it is not exactly one. A misread comment can cost a question. It can never cost a paid run.

Can the same idea work outside Basecamp?

Yes. Naruto is the same pattern pointed at WhatsApp. He sits in the groups we share with customers, reads the conversation, and posts what he saw to our internal operations system through a single authenticated endpoint. A customer's feedback goes in as a note on their record, filed as coming from WhatsApp.

  1. Customer groupon WhatsApp
  2. Naruto readsnotes what it saw
  3. Reportsone secure endpoint
  4. System decidesit holds the data
  5. Team actson the record
The bot observes, the system decides. Naruto only reports; the system that already holds the customer's data decides what is news.

The split is the useful part: the bot observes, the system decides. Naruto never flags a customer as at risk himself. He reports what he read, and the system that already holds the account manager, the last delivery and the open balance decides whether it is news. He keeps no state of his own, so reporting the same thing two nights running is harmless. The system works out what changed.

Some of his jobs need no model at all. Holiday notices go out to each customer group seven days and two days before the office closes, and the system records which groups were told.

What went wrong while we built it?

The pattern in almost every failure was silence. An agent that breaks rarely crashes. It just stops answering, and nothing anywhere turns red.

  • Five bugs between the first dry run and the first correct answer, every one silent. The list of notification types the agent handled missed "Mention", the only one that mattered. The memory cap was copied from a different server. Neither command the agent calls was on the service's path. The agent could not reach the data endpoint for two separate permission reasons. And one setting contradicted the read-only tool list it was given. Each run exited cleanly.
  • Two hours behind on a busy card. The agent answered one mention per wake. A code change took 90 to 197 seconds, people typed faster than that, and from one point every answer went to the previous question. It now works a card until nothing is waiting, up to three answers a run.
  • A mention about the bot is not a mention to it. A comment talking about Jack carries the same mention as one written to him. Those now get one line back, "Nothing for me to do here", because a mention left unanswered would block every question behind it.
  • Two functions with the same name. The agent had its own credential scrubber that removed one secret, while the process held five. Both functions were called the same thing and both looked like they worked. There is now one shared copy, and the program refuses to start if it cannot load.
  • Running out of model usage looked like ignoring people. The runs logged an error and posted nothing. Now a usage limit gets one notice per outage, and the question stays queued until it is answered.

How fast does an AI teammate respond, and what does it cost to run?

A question is answered on the card in about 40 seconds, and a planned code change takes about two and a half minutes. Running it adds no hosting cost, because the agents run on a server we already have, and the model is only called when there is real work to do.

We measured this from the server's own logs over three busy days in August, when the agent was started 4,107 times:

Kind of runTypical timeLongest seenHow often
Nothing waitingTypical time: 1 secondLongest seen: 9 secondsHow often: 4,057 of the 4,107 runs
Answering a questionTypical time: about 40 secondsLongest seen: 59 secondsHow often: Part of the 50 runs with work
Planning or building a changeTypical time: about 2.5 minutesLongest seen: 197 secondsHow often: Part of the 50 runs with work
Investigating a failureTypical time: variesLongest seen: 219 secondsHow often: Part of the 50 runs with work

Three things keep the cost down:

  • Idle runs never reach the model. Checking the inbox and finding nothing takes two calls to Basecamp and about a second, so a run every minute costs almost nothing.
  • The model's time goes on thinking, not waiting. Inside a code change, 60 to 80 percent of the run is the model reasoning. The rest is reading, searching and editing files.
  • No new infrastructure. The agents share a server that was already running, each under its own system user with memory and CPU caps, so they cannot slow the services around them.

What changed for the team?

The biggest change is that routine questions and small fixes no longer wait for a developer to be free. They are asked where the work already is, answered on the same card in under a minute, and the answer stays there for whoever looks next.

The taskBeforeWith an AI teammate
A question about usage or dataBefore: Ask a developer and wait until they are free to run the queryWith an AI teammate: Mention Jack on the card and read the answer there in about 40 seconds
Why did a scraper fail?Before: Someone with server access reads the logsWith an AI teammate: Jack reads the service logs, with secrets removed, and quotes the line that explains it
A small code changeBefore: A developer picks it up from the board when they canWith an AI teammate: A written plan in a few minutes, a pull request once it is approved, a person merges
A broken scraperBefore: Triaged and fixed by handWith an AI teammate: Queued from the card, picked up within five minutes, and checked against the real page before a pull request
Where the answer livesBefore: Wherever it was askedWith an AI teammate: On the card, next to the work, for the whole team

What that adds up to:

  • Developers keep their focus. Questions that used to interrupt someone are answered without them, and the ones that do need a person arrive with the log line or the query result already attached.
  • Changes start reviewed. Every code change begins as a plan a person approves, so review starts from an agreed scope instead of a surprise diff. Six pull requests started from Basecamp cards this way have been merged so far.
  • Nothing new to learn. The team keeps using Basecamp the way it already did. The only new habit is typing a name.
  • A record by default. The request, the answer and the approval sit on the card, so the next person with the same question finds it there.

How to set up an AI teammate for your own team

The parts are small. The care goes into the limits.

  1. Give the bot its own account in your project tool, so people mention it like a colleague and its comments are clearly its own.
  2. Run it where the data is, under its own system user, with memory and CPU caps sized for that machine.
  3. Poll the inbox, and use a webhook only to wake it up. Treat every notification as a doorbell: fetch the thread yourself and act only on an explicit mention from someone else.
  4. Make the unread inbox your state. Mark a notification read only after the reply is posted.
  5. Give it narrow tools, not a shell. One for data, one for logs, one for fetching a page, each with its limits enforced in code.
  6. Put the safety rules in the endpoints, not in the instructions. No database password, read-only queries, a cost check, redaction on anything that leaves the server.
  7. Plan first for any change. Post a plan, wait for approval, let your code run git, check the changed paths afterwards, and keep merging for a person.
  8. Say something every time it is mentioned, even if it is "nothing for me here" or "out of usage, still queued". Silence is the failure you will not see.
  9. Run it by hand before you schedule it. A run that exits cleanly proves nothing. Only a posted, correct answer does.

If you would rather not build it yourselves, this is the kind of custom AI agent work we take on, along with the workflow automation around it.

Key takeaways

  • An AI teammate works best inside the tool the team already uses, reached by name, answering on the card where the work is.
  • Running the agent on your own server keeps live data off the internet, at the price of isolating it carefully.
  • A notification is a doorbell, not an instruction: re-read the thread and act only on an explicit mention.
  • Narrow tools with limits enforced in code are safer than a broad tool list and good instructions.
  • Every code change goes plan, approval, pull request, human merge. The agent never merges its own work.
  • The bot observes and the system decides, whether the channel is Basecamp or WhatsApp.
  • The payoff is focus: routine questions and small fixes stop waiting for a developer, and every answer stays on the card.
  • An agent's failure mode is silence, so make it say something every time.

Frequently asked questions

Routine work stops waiting for a developer. Questions about data or a failing job are answered on the card in about 40 seconds, small code changes start as a plan a person approves, and broken scrapers can be sent for repair from the card itself. The team keeps using Basecamp as before, and every request and answer stays on the card as a record.

Ours cannot. Jack opens pull requests, but a person reviews and merges every one. The agent's own code, the CI workflows and the repair pipeline's safety gates are also off limits to it, checked against the changed files after the model finishes rather than trusted from its instructions.

Only through a gate that enforces the rules itself. Our agent holds no database password. It queries a read-only endpoint on the same server that allows one SELECT at a time, applies a time limit and a denylist, refuses full scans of large tables, and asks for confirmation above about 200,000 rows examined.

Both. Polling the bot's own notification inbox every minute is simple and reliable, and the unread state doubles as a work queue. A webhook adds speed and catches card assignments, which Basecamp does not send as notifications. The webhook only wakes the agent; it never carries instructions.

For us, about 40 seconds for a question and about two and a half minutes for a planned code change, measured from the server's logs. Most scheduled runs find nothing to do and finish in around a second.

No. Ours run on a server we already had, under a dedicated system user with its own memory and CPU caps. The main running cost is model usage rather than hosting.

The pattern carries over. Our WhatsApp bot, Naruto, reads customer groups and reports what it saw to our internal system through one authenticated endpoint. The rule that makes it safe is the same: the bot observes and reports, and the system that holds the data decides.

Schedule a call