Someone types the agent's name in a Basecamp comment and asks what they would ask a colleague. About forty seconds later the answer is 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 somebody agrees to it.
It is not a chat window bolted onto a project tool. It is a small program on a server I already had, which wakes when its name is used and works inside the limits I would give a new engineer in their first week. This post is the production version: what it is allowed to touch, how fast it actually is, and what went wrong on the way.
Two things worth saying up front. The numbers below come from a client's production system that I built and still run — measured from that server's own logs, not estimated. The open-source recipe in this series is the same loop with the client-specific tools taken out, so it installs in about five minutes and reads live data only if you give it something to read.
Contents
- What it does
- What it will not do
- How a mention wakes it up
- Why it runs on my own server
- What it can read, and what it can never touch
- How a comment becomes a pull request
- How fast it is, and what it costs to run
- What went wrong
- What changed for the team
- Set it up yourself
What it does
- Reads a new card against your codebase and posts a plain-English analysis on it.
- Answers in the comments. You reply, it answers, flags what is missing, and agrees the scope with you before any code exists.
- Opens the pull request when you move the card to your build column, or assign the card to it.
- Watches the rest of Basecamp too — to-dos, messages, docs and files, check-ins and the schedule. You switch on the handlers you actually want.
This is the first automation in the series, and the one the other 34 were built from. The pattern — analyse on arrival, discuss in the comments, build on an explicit go-ahead — is the template every other tool follows.
What it will not do
It never moves your cards, merges a pull request, or closes anything. It comments and proposes; your team decides. That constraint is deliberate and it is the same in every recipe in the series — an agent that can write but not commit is one you can leave running without supervising it.
Anyone on the board can ask it for a change, because the worst a stranger can cause is a pull request somebody closes.
How a mention wakes it up
Two triggers, one loop. A timer checks the bot's own Basecamp inbox every 60 seconds, and a webhook gets it there sooner. The webhook carries no instructions. It only says that something happened, and the agent then reads the card itself.
The full loop for one mention:
- A timer 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.
- The agent lists its unread notifications.
- For each one, it fetches the whole thread again from Basecamp rather than trusting the notification text.
- It acts only on a comment that mentions it by name and was written by somebody else.
- It works out what kind of request it is, does the work, and posts one comment.
- Only after that comment is posted does it mark the notification as read.
There is no database of what it 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 I did not expect: Basecamp sends no notification when a card is assigned to someone. "Assign the card to the bot and it builds it" cannot be seen by polling at all. Here is the shape of the two pieces of scheduling, with 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.servicePersistent=false is deliberate. A missed minute needs no catching up, because the unread notification is still waiting, and catching up would only produce a burst of runs after a reboot.
Why it runs on my own server
Because the data it reads is on that server and should stay there. It answers questions about live application data, and the endpoint it queries accepts connections from the same machine only. Running it 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 a server's own logs is easiest from that server.
The cost is that the agent shares a machine with production, so I paid for that in isolation:
- Its own system user, with memory and CPU caps sized for the actual box.
- No database password. It cannot read the application's environment file at all. That is tested by trying, and the read has to fail.
- An allow-list for the model's environment. The model sees only the variables I 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 it can read, and what it can never touch
It can read data, logs, code and web pages, each through a narrow tool built for that one job. It cannot change anything without a person approving a plan first, and parts of the codebase are off limits even then.
| Tool | What it gives the agent | The limit |
|---|---|---|
| Data queries | Answers about usage, accounts and imports | 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 reader | The recent logs of five named services | 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 fetch | What a website returns to the server right now | One GET request: a URL and a byte count. No method, body, headers or output file, and at most three redirects |
| Code | The source and the repo's own notes | Read-only when answering a question |
The safety rules sit in the endpoints, not in the agent's instructions, so a cleverly worded comment cannot talk its way past them. That matters more than it looks: there is no read replica, so the database the agent queries is the one answering live customer traffic.
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 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 that runs after the model finishes, which is the next section.
How a comment becomes 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.
- Plan. The agent reads the relevant code with read-only tools, posts what it intends to change, and stops.
- Approve. The person who asked replies on the card. "Go with the plan and raise the PR" counts, even without a mention, as long as it mentions nobody else.
- Build. The agent edits a fresh copy of the repository with edit tools only. Every git command after that — the branch, the commit, the push — is run by my code, not by the model.
- Check. My code compares the changed files against a list of places the agent may never edit: its own program, the CI workflows, the safety gates, the rules every AI session in the repo reads, and the hooks that block secret reads.
- Review. The pull request goes up. A person merges it, or closes it.
I 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 module 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.
Six pull requests opened from Basecamp cards this way have been merged so far.
The same two-pass shape carries a stricter sibling: a repair service, live since 9 September 2026, that takes a broken job from a card, drains its queue every five minutes, and does one repair at a time because two headless browsers at once is what restarts the server. It verifies its own fix against the real page before it is allowed to open a pull request, rather than taking the model's word for it. Only three people can start a repair, because a repair spends paid credit the moment it is queued, with no pull request in front of it to review.
How fast it is, and what it costs 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 it runs on a server that was already there and the model is called only when there is real work to do.
Measured from the server's own logs over three busy days in August 2026, when the agent started 4,107 times:
| Kind of run | Typical time | Longest seen | How often |
|---|---|---|---|
| Nothing waiting | 1 second | 9 seconds | 4,057 of the 4,107 runs |
| Answering a question | about 40 seconds | 59 seconds | part of the 50 runs with work |
| Planning or building a change | about 2.5 minutes | 197 seconds | part of the 50 runs with work |
| Investigating a failure | varies | 219 seconds | 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 agent shares a server that was already running, under its own system user with memory and CPU caps, so it cannot slow the services around it.
What went wrong
The pattern in almost every failure was silence. An agent that breaks rarely crashes. It stops answering, and nothing anywhere turns red.
- Five silent bugs between the first dry run and the first correct answer. 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. It could not reach the data endpoint, for two separate permission reasons. And one setting contradicted the read-only tool list it was given. Every 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 on 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 the agent carries the same mention as one written to it. 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 it.
- 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.
Every one of those was mine, and every one of them was invisible until somebody on the card asked why the bot had gone quiet.
What changed for the team
Routine questions and small fixes stopped waiting 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 task | Before | With the agent on the board |
|---|---|---|
| A question about usage or data | Ask a developer, wait until they are free to run the query | Mention the bot on the card, read the answer there in about 40 seconds |
| Why did a job fail? | Someone with server access reads the logs | The agent reads the service logs, with secrets removed, and quotes the line that explains it |
| A small code change | A developer picks it up from the board when they can | A written plan in a few minutes, a pull request once it is approved, a person merges |
| Where the answer lives | Wherever it was asked | On the card, next to the work, for the whole team |
What that adds up to: developers keep their focus, and the questions that do need a person arrive with the log line or the query result already attached. Changes start reviewed, because every one begins as a plan somebody approved rather than a surprise diff. Nobody had to learn a new tool — the only new habit is typing a name.
Set it up yourself
Clone the repo, run ./setup.sh, pick Basecamp, and answer a few questions — your IDs, your tokens, your conventions. The installer copies the recipe into your repository, sets your secrets, and prints whatever manual steps are left with your real values already filled in. No server to host, no database to run, nothing to pay for beyond the AI you already use.
The recipe is MIT-licensed and open source: ai-automations. It is one of 35 — see the series overview for the full list and the rules every recipe follows. If your tool is not on the list, name it and I will build the automation for it, free.
If you are wiring up your own rather than installing mine, these are the parts that took the longest to learn:
- Give the bot its own account in the project tool, so people mention it like a colleague and its comments are clearly its own.
- Run it where the data is, under its own system user, with memory and CPU caps sized for that machine.
- Poll the inbox; use the webhook only to wake it. Treat every notification as a doorbell: fetch the thread yourself and act only on an explicit mention from somebody else.
- Make the unread inbox your state. Mark a notification read only after the reply is posted.
- 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.
- 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.
- 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.
- 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.
- Run it by hand before you schedule it. A run that exits cleanly proves nothing. Only a posted, correct answer does.
Key takeaways
- An agent works best inside the tool the team already uses, reached by name, answering on the card where the work is.
- A notification is a doorbell, not an instruction: re-read the thread and act only on an explicit mention from somebody else.
- The unread inbox is a better work queue than a database you have to keep correct.
- Narrow tools with limits enforced in code beat a broad tool list and carefully worded instructions.
- Every code change goes plan, approval, pull request, human merge. The agent never merges its own work.
- Running it on your own server keeps live data off the internet, at the price of isolating it properly.
- An agent's failure mode is silence, so make it say something every single time.
FAQ
Can it move cards or close to-dos on its own?
No. It comments and opens pull requests; moving a card, merging and closing stay with your team. That constraint is the same in every recipe in the series, and the changed files are checked against a protected-path list after the model finishes rather than trusted from its instructions.
Does it only watch the card board?
No — the same agent can watch to-dos, messages, docs and files, check-ins and the schedule. You switch on the handlers you want and leave the rest off.
Is this one actually in production?
Yes. Basecamp is the production-proven recipe and the pattern the other 34 were built from. The timings and the 4,107 runs in this post are from that client system, measured from its own logs over three busy days in August 2026.
Is it safe to let an agent query production data?
Only through a gate that enforces the rules itself. Mine holds no database password. It queries a read-only endpoint on the same server that allows one query at a time, applies a time limit, refuses full scans of large tables, and asks for confirmation above about 200,000 rows examined.
Polling or webhooks?
Both. Polling the bot's own notification inbox every 60 seconds is simple and reliable, and the unread state doubles as a work queue. The webhook adds speed and catches card assignments, which Basecamp does not send as notifications. It only wakes the agent; it never carries instructions.
Do I need a separate server?
No. Mine runs on a server that was already there, under a dedicated system user with its own memory and CPU caps. The main running cost is model usage, not hosting.
More in this series: Jira · all 35 automations
Working on something similar?
If you are wiring AI into the tools your team already uses, I am happy to look at it with you. Thirty minutes, no deck — bring the workflow that costs your team the most time and we will work out whether it is automatable. Get in touch.