In short
Pulse turns every meeting at the agency — a client call, an internal standup, a sales conversation, a phone call to a prospect — into a specific piece of information delivered to a specific person, in the place where their work actually lives. Not a report someone might open later. A comment under the project task, a post in the project channel, a direct message to one named person.
It was built between February and August 2026, developed against live production throughout rather than beside it. And it was built in Claude Code — not „with AI assistance”, but in Claude Code as the engineering environment, with purpose-built tooling, an enforced quality process and persistent project memory.
This case study is about that second part: what it looks like when one person maintains a system this size, and why the output survives review instead of merely looking finished.
The starting point
A marketing agency meets a lot. With clients, on standups, in departmental sessions, on sales calls. Every one of them leaves behind a transcript, a note, or nothing at all.
The problem was never that nobody took notes. It was that notes land somewhere nobody returns to. A Team Leader does not notice the client has raised the same concern three months running. A Project Manager hears about a commitment a week late. A salesperson never learns the client called and could not get through, because the alert went by email to a manager.
Underneath that sits something harder: nobody had a view of the whole. How many projects are in poor shape right now? Who runs meetings well, and who turns them into a monologue? What do clients keep circling back to without ever saying it outright?
„Just run the transcripts through a language model” is a trivial answer here, and on its own it delivers nothing. The difficulty is not the analysis. It is everything around it: where the transcript comes from, how you classify the meeting, which task the result attaches to, who gets messaged, what happens when the model returns malformed output, and how you make failures loud instead of silent.
That is an engineering problem, not a prompting problem. And that is where Claude Code works.
What Pulse does
Briefly, because it is not the point of this document.
The system scans team calendars and joins matching meetings on its own. Afterwards it pulls the transcript, classifies the meeting from its title, and routes it to the right module. The analysis layer — a language model working from prompts kept outside the codebase, in editable documents — returns structured output: commitments, risks, goals, sentiment, tasks.
The model is swappable by design: prompts live in documents, the model name is a configuration value, and individual modules can run different models. Meeting analysis, the chatbot and the feedback module each have their own setting today. That was an assumption from day one, not a later patch.
From there the result fans out to people: a comment on the project task, for the Team Leader. A post in the project channel, for the Project Manager. A direct message to one person — individual coaching feedback, participation metrics, a draft LinkedIn post, a warning about a risk in a process being discussed. A row in the database, so the same question can be asked again in six months.
This is the shape of the coaching message one person receives after a meeting — privately, not in the team channel. The point is development: the person gets something concrete to do differently next time. Conclusions about a person are seen by a limited group; project material is seen by the people working on that project, so they know the history of the engagement. The example is anonymised; every real one carries a name.
Meeting: [EXT] client.com | campaign review
👋 Hi! A few observations from yesterday's review with the client.
🚀 WHAT WORKED
— When the client said „these aren't the results we expected”, you did not start explaining — you first asked what they were comparing against. It turned out to be last Q4, i.e. peak season. That reframed the rest of the conversation.
— You closed with specifics: who, what, by when. The client repeated it back in their own words, so you know you are in agreement.
🔧 ONE THING TO ADJUST
— Next quarter's budget came up in minute 47, as people were wrapping up. Next time raise it in the first half — that leaves room to respond, not just to note it down.
💡 AN IDEA FOR THE AGENCY (not a task for you)
This is the third meeting this month where a client compared results against the wrong period. It may be worth making year-on-year the default comparison in the report, rather than relying on each PM to correct it every time.
Every point is anchored to a specific moment in the meeting. Without that anchoring it would be a collection of generalities — the kind nobody reads twice.
The same foundation carries the rest: a weekly scored health review of every project, feedback for the people running those meetings on what to do differently next time, phone prospecting with automatic notification of whoever owns the contact, handover of sales context to the delivery team the moment a lead is won, a Gmail extension that summarises a thread into our work system, and a chatbot that answers questions about meeting history.
The safeguards people ask about first
At every meeting the bot posts in the chat — internal and external — and the participants decide whether it records. The example above is rewritten and anonymised; we do not publish screenshots of real messages, because every one carries a name. We process the data in line with our privacy policy and our contracts.
How it was built
This is the actual case study.
Claude Code as an environment, not an autocomplete
The standard use of a coding assistant looks like this: a human writes, the assistant suggests the next line. Here the arrangement is inverted. Claude Code receives a task stated in plain language — „a user reported that in the summaries, headings and body text swapped sizes” — and carries it from diagnosis to deployment.
To make that repeatable rather than occasional, we built fourteen purpose-made tools into the repository, each covering a specific area of the system or a specific procedure. Each one is a document stating when to use it, what the rules are, what the known traps are, and what to return at the end.
Above them sits an orchestrator — a tool that writes no code. It reads intent, plans, delegates to the right tool, and enforces the quality gates.
Quality gates that actually stop the work
This is the part we are proudest of, because it addresses the real failure mode of working with a model: code that looks finished is not finished.
Every change passes three independent reviews, each asking a different question.
Testing asks whether it works at all — happy path, edge cases, malformed input. The tester fixes nothing; it returns a report.
Usability review judges the change from the perspective of the person who has to live with it, on a 1–10 scale. It does not assess code elegance. It asks whether a Team Leader will understand this message without instructions, and whether they get something useful when it goes wrong. The acceptance threshold is 9/10. Below that we iterate automatically, without stopping to ask.
Code review asks something else entirely: does the code do what it claims? Because a change can score 10/10 on usefulness and still silently drop data on an empty array.
Only a clean result from all three permits a commit. A human approves every commit — Claude Code never makes one on its own. That rule is written into the project configuration and it holds.
A system that improves itself
The repository holds a task log: one line per completed task. The user's goal, the outcome, the tools used, and any idea for a new tool.
At the start of every session Claude Code reviews the recent entries looking for repetition. When the same procedure surfaces a third time and its steps have stabilised, it proposes turning it into a tool. It does not create one on its own — it proposes, citing the evidence from the log.
That is how our publishing tool came to exist. Twice in a row a task ended with content being pasted onto a page by hand. On the third occurrence Claude Code noticed, proposed the tool, and — once approved — wrote it along with its supporting script.
The tool has one property we asked for explicitly, and it captures the whole point: by default it writes nothing. It first compares the local content against what is live on the page, shows line by line what would disappear, and waits for a human decision. The reason: prompts sometimes get corrected directly in the panel, the repository never learns about it, and a blind overwrite would erase those corrections without trace.
That is not caution written into a prompt as a platitude. It is caution derived from a specific incident, encoded into the tool so it cannot be skipped.
One week in August
Theory reads well. Here is what actually happened — five tasks from a single week, each showing a different kind of work. This is not a highlight reel pulled from six months. It is what an ordinary week in the task log looks like.
The model dropped one brace
24 August. A client meeting analysis crashed while parsing the model's response. Cause: a single missing opening brace inside a task array. The content was complete; the entire analysis was lost.
Fixing the obvious cause would have taken fifteen minutes. Instead, Claude Code wrote a property-based test: take four response shapes, remove each structural character in turn, and verify the repair either recovers the content exactly or honestly refuses.
The test surfaced three defects older than the change that prompted it. Each, under the right conditions, quietly merged two tasks into one or buried an entire report section — and reported success. Nobody knew about them, because they never raised an error.
Thirteen parallel agents worked the task: an inventory of every model call in the system, a retry-risk analysis, three independent adversarial attempts to break the repair, and a completeness critic. The critic found a genuine blind spot in the test itself — one of the fixtures could not detect that a missing closing bracket hides note content inside the task list.
Usability review returned 8/10. After the scope boundaries were stated explicitly, 9/10. Only then did it ship.
A Polish quotation mark
22 August. The Gmail extension destroyed a finished note. Cause: the model opened a quotation with the Polish „ and closed it with a plain ", truncating the content midway — while the response was formally flagged complete.
Four layers of fix, including a non-obvious one: it turned out the prompt template was showing the model an incorrect format to copy. Two adjacent lines, one right and one wrong. The model copied the wrong one.
This is a class of bug that is almost invisible. The prompt reads correctly — a human reviews it and everything looks fine, because the correct example sits right next to the broken one. Only the model's behaviour reveals which of the two patterns it actually copied. The fault lies with neither the code nor the model, but with an instruction that offered two contradictory answers to the same question.
The investigation also revealed that an earlier repair step had been dropping fields silently for some time, and reporting it as success.
A dash that was a heading
26 August. A user report: „the assistant swapped my headings with the body text in the summaries”. Paragraphs were rendering larger than section titles.
Cause: a line of dashes placed directly beneath text. In Markdown that is not a horizontal rule — it is a heading underline, promoting the paragraph above it to a level-two heading. The model followed the prompt correctly. The prompt instructed it to build a trap.
The more interesting part happened alongside. Verifying the fix required sending a message. Claude Code inspected how that module gets triggered and noticed the recipient list was set to „everyone” — testing in production would have messaged the entire team. Instead it sent two test messages through the same delivery mechanism but outside the production run, and compared how they rendered.
It also disproved an assumption written into the prompt a month earlier — a ban on headings justified by the claim that direct messages do not render headings reliably. A screenshot from the user proved the opposite. The assumption had never been measured; now it was, and the ban is gone.
A call nobody heard about
26 August. A client called a salesperson's line. Nobody picked up; it went to voicemail. The system recorded „no owner found” and notified no one.
Diagnosis: the owner-resolution logic had three levels, and all three asked about the caller's number, or about what the telephony provider had assigned. None asked about the number that was dialled. That number had been in the spreadsheet the whole time. The only thing missing was a record of whose number it was.
The new level was deliberately placed below the existing one rather than above it — because the number map is maintained by hand and goes stale, while the provider's data reflects what actually rang. As a result, the change does not touch a single notification that currently routes correctly. It speaks only where there is currently silence.
Alongside it, a mechanism that keeps the map from rotting: every row now carries a note about the state of the dialled number — including on notifications that succeeded. The gap has to be visible before it costs a lead.
Test coverage: 68 assertions, the first test this module ever had. Four of them guard decisions rather than code.
A provider that was wrong
25 August. A webhook flagged „missed” delivered a ten-minute answered call. A comment reading „we couldn't reach them” landed on the prospect's task — false, in the exact place where work on that lead lives.
That same morning, the same mechanism correctly classified a genuine failed attempt. So the signal could neither be discarded nor trusted unconditionally.
The resolution rests on two independent signals, because there is no way to know in advance which fields will arrive: the presence of a recording together with talk duration, and the elapsed time between call start and webhook delivery. Thirteen seconds is a failed attempt. Six hundred and twenty-two is a conversation.
The note carries the call's duration, and that is not decoration. „Call took place” is an inference, not a directly observed fact — and the salesperson is the only person who knows what really happened. Without the duration stated, there is nothing to check it against.
The mechanism works in one direction only: absence of evidence leaves the existing behaviour untouched. Erring toward silence costs a missing comment. Erring the other way puts a falsehood on a client's task.
What this adds up to
Five stories, one common thread: none of them is about generating code.
Each is about something harder — noticing the problem sits somewhere other than where it was reported. That testing in production would have messaged the whole team. That the prompt template was teaching the model an error. That the data had been in the spreadsheet all along. That a month-old assumption had never been measured.
And that is the real value we got from Claude Code: not faster typing, but maintaining a system that would normally require a team.
The system, in numbers
Repository read taken 27 August 2026, six months after the first commit.
- 24 142
- lines of production code
- 61
- files
- 230
- commits, February–August 2026
- 224
- of them from one person
- 14
- purpose-built tools in the repo
- 15
- tables in the knowledge base
- ~350
- meetings a month
- ~110
- projects in the weekly review
- ~80
- people across MTA Group — every agency team
Documentation runs to 5,885 lines across 30 files. Production modules cover five meeting types, seven kinds of direct message, two browser extensions and one chatbot. The system has been in production since February 2026. Time saved is the one figure here we do not measure directly — the agency's own estimate is hundreds of hours a month, and it is an estimate, not a reading.
Three things that turned out to matter
Record reasons, not just decisions. Our changelog runs 1,595 lines and every entry answers „why”, not only „what”. That is why Claude Code, picking up a task a month later, knows a given mechanism exists because of a specific incident — and does not „fix” it back.
A quality gate needs the authority to stop the work. The 9/10 threshold functions only because falling below it returns us to revisions automatically, without a discussion. A soft threshold would be decoration.
Separate „is this good for the person using it” from „does the code do what it claims”. Those are two different questions and neither substitutes for the other. A change can be excellent to receive and still lose data on empty input.
What is next
Pulse keeps growing. The near-term direction is extending the chatbot to questions that currently require manual digging.
The working method stays the same. A human frames the problem and approves the result. Claude Code runs everything in between — and stops when there is something worth asking about.