27 Days, One Developer, One Subscription

By Chris Trudel

How we shipped a multi-tenant omnichannel legal platform

Between January 2 and January 29, 2026, one developer built a multi-tenant, omnichannel, agentic legal platform from scratch. At the end of those four weeks, a claimant could start a conversation on the phone, continue it in a web chat, and continue it again on the phone, with their claim being created and their data collected along the way. The work ran on a single Claude Max 20x subscription at 60–70% of its weekly usage limits, with no API spend on top. Our pre-implementation plan, written in December and committed to the repository seven minutes after it was created on January 2, put the omnichannel work in weeks 15–18 of a 22-week build. The omnichannel demo happened in week 4.

22-week pre-implementation plan vs. 27-day actual: the planned five phases (Foundation, Core Features, AI Capabilities, Omnichannel, Production) span weeks 1–22, while the actual completion of Phases 1–4 fits inside the first four weeks, with the internal omnichannel demo on Jan 29.

At the time, the workflow engine that ran this build was internal tooling we’d developed for ourselves; we’ve since productized it as Generacy. Everything described in the rest of this post — the phased label vocabulary, the draft-PR-with-checklist pattern, the isolated dev containers, the credential scoping, the per-agent attribution — is what Generacy now provides.

The rest of this post walks the work behind the comparison. We start with the January production data: 414 commits on the default branch, 143 pull requests, median cycle time of 1.17 hours from PR creation to merge, eleven concurrent agents at peak. We trace a single pull request from issue creation through merge to show what “agentic development” actually looks like at the day-to-day level. We name the things we deliberately didn’t build, because that’s the other half of why this works without catastrophe. And we describe where the platform is now: a joint venture with a US personal injury law firm group, going live within 45 days.

What got shipped

In one paragraph: multi-tenant Firestore, self-hosted LangGraph agents on Cloud Run, custom MCP servers for damages calculation and verdict research, and a voice/SMS/email/chat surface. Details below for readers who want them; skip to “The plan, written down, never revised” if you’d rather see the numbers.

The platform’s foundation is Firestore with a two-tier isolation model: deployment-level isolation via separate GCP/Firebase projects, and logical tenant isolation within each deployment via security rules with default-deny patterns. All reads and writes are scoped under /tenants/{tenantId}/..., enforced at the database level. No relational databases anywhere in the stack.

Agents run on LangGraph, self-hosted on Cloud Run rather than LangGraph Cloud, with a custom Firestore checkpointer we wrote to avoid a Postgres or SQLite dependency. The main conversation graph delegates to nested sub-graphs for document generation, damages calculation, and verdict research, with progress streamed to the frontend via SSE. External capabilities are exposed via MCP servers: a jurisdictional damages calculator, two regional verdict-and-settlement servers (Canadian and US, each with Vertex AI embeddings), and a document generation server. Tenant-provided MCP servers plug in via OAuth with configurable tool allowlists.

The omnichannel surface: voice via ElevenLabs (in-app WebRTC and telephony PSTN), SMS via Twilio, email via SendGrid, and in-app chat over SSE. A single conversation can move across channels — same-day continuations keep in-memory context, next-day continuations load a summary from prior conversations. Claimants authenticate via phone with SMS verification, no passwords; professionals authenticate via Google SSO; tenant membership lives in Firebase Auth custom claims.

Cross-cutting: audit logging records every agent action with agent_id, tool_name, sanitized arguments, and result summary, written to both claim-scoped and tenant-scoped collections. Document generation walks an iterative workflow from base templates through markdown to HTML to DOCX (with placeholder post-processing) to PDF. File processing handles 10,000-page PDFs via async Cloud Run jobs and Pub/Sub, with OCR, chunking, and Vertex AI embeddings on the resulting documents.

By the end of January all of this existed, deployed in a working cloud environment, and ran together in an end-to-end intake demo.

The plan, written down, never revised

In December 2025, before any code was written, Chris wrote five documents that described the platform we wanted to build. The documents covered system overview, C4 architecture diagrams, multi-tenancy design, agent architecture, and an epic breakdown decomposing the scope into twenty epics across five phases. The last of those documents contained an Implementation Sequence that put a calendar on the project: foundation in weeks 1–4, core features in weeks 5–8, AI capabilities in weeks 9–14, omnichannel integration (voice, SMS, email) in weeks 15–18, and production readiness in weeks 19–22. Twenty-two weeks, end to end.

The architecture documents lived as drafts outside any git repository. A separate local repository had been started on December 3 for the dev containers and supporting tooling — that “Initial commit” timestamp still survives in the eventual GitHub repo’s history. But the architecture documents themselves had no git timestamp yet. They were a plan, not a project.

On January 2, 2026, at 17:57:47 UTC, the GitHub repository Painworth/ai-lawfirm was created. Seven minutes later, at 18:04:24 UTC, a single commit (7089e7e6) added all five architecture documents to docs/. The full Implementation Sequence — the 22-week, five-phase plan — was committed exactly as it had been written in December:

Phase 1: Foundation (Weeks 1-4)

  1. Epic 1: Infrastructure & DevOps Foundation
  2. Epic 2: Authentication & User Management
  3. Epic 4: Frontend Foundation
  4. Epic 5: Agent Service Foundation
  5. Epic 20: Testing Infrastructure (initial)

Phase 2: Core Features (Weeks 5-8)

  1. Epic 3: Multi-Tenancy Infrastructure
  2. Epic 6: Claim Management
  3. Epic 7: File Management

Phase 3: AI Capabilities (Weeks 9-14)

  1. Epic 8: MCP Infrastructure
  2. Epic 9: Calculator MCP Server
  3. Epic 10: Verdict/Settlement Search MCP Server
  4. Epic 11: Document Generation
  5. Epic 12: Real-Time Updates (SSE)
  6. Epic 13: Workspace State Management

Phase 4: Omnichannel (Weeks 15-18)

  1. Epic 14: Voice Channel Integration
  2. Epic 15: SMS Channel Integration
  3. Epic 16: Email Channel Integration

Phase 5: Production (Weeks 19-22)

  1. Epic 17: Audit Logging & Compliance
  2. Epic 18: Billing & Usage Tracking
  3. Epic 19: Deployment Automation

About an hour later, at 19:07:08 UTC, the autonomous agent pw-dev-bot made its first commit to the repository. The build was underway.

What happened next is the part of the story we want to be careful with, because the strongest evidence for the comparison we’re about to make is that we didn’t manipulate it. The Implementation Sequence quoted above is byte-identical at the last edit to docs/05-epic-breakdown.md in January. Two later commits to that file made substantial changes to other sections — one adding 2,649 lines, another adding 187 — but a diff of just the Implementation Sequence between commit 7089e7e6 and the file’s last January edit produces no output. The week numbers were never adjusted to match what actually shipped. The plan that the build was measured against is the plan that was written down in December.

Twenty-seven days later, on January 29, we ran an internal demo of the full omnichannel intake — the phone-to-chat-to-phone journey described in the opening, with claim creation and data collection happening across channel switches. That is the milestone described in Phase 4 of the Implementation Sequence: voice channel integration, SMS channel integration, email channel integration. The plan put it in weeks 15 through 18 of a 22-week build. The work landed in week 4. The first external end-to-end demo followed eleven days later, on February 9.

How a single issue moves through the workflow

The platform was built one issue at a time, but “one issue” carried a lot of process. To make that concrete, here is what happened to Issue #18 — “3.2: Refactor Firestore Checkpointer for Multi-Tenancy” — from creation to merge.

Issue #18 was a foundational refactor: rework the LangGraph checkpointer so its Firestore paths included a tenant scope, letting agent state for many tenants share a deployment without collision. It carried the labels you’d expect — agent, multi-tenancy, phase:implement, priority:P0, refactor. Those labels aren’t decoration. They’re how Generacy — our workflow engine, built on top of GitHub Spec Kit — decides what to do next.

The engine progresses each issue through six phases: phase:specify, phase:clarify, phase:plan, phase:tasks, phase:implement, phase:validate. When a phase completes, a matching completed:* label is added. The label vocabulary is visible across the closed-issue list — every issue carries the trail of its lifecycle:

GitHub issue-list screenshot showing the phase:* and completed:* label vocabulary across many closed issues.

When phase:specify finishes for Issue #18, the workflow engine opens a draft pull request — PR #65 — on a branch named 018-3-2-refactor-firestore. The branch-naming convention ({issue-prefix}-{slug}) comes from Spec Kit. Every issue in the project follows it, which means the branch name tells you which issue spawned the PR without having to open anything.

The agent — running in an isolated dev container with the full microservices stack and scoped credentials — then makes commits to that branch. For PR #65 the sequence was:

feat: Initial spec from issue #18
feat(agent): Add implementation plan for multi-tenant checkpointer
feat(agent): Add task list for multi-tenant checkpointer refactor
feat(agent): Implement multi-tenant checkpointer support

The first commit (the spec) is created by the workflow engine. The three feat(agent): commits are the agent’s actual work: writing the implementation plan, decomposing it into tasks, and implementing each task. While that’s happening, the PR body shows a live checklist that the workflow engine updates as each phase completes. Items like Tasks - Task list created ✓, Implement - Code implementation complete ✓, and Validate - Validation complete ✓ get checked in turn; Review - PR merged stays open until the human reviews and merges.

PR #65 screenshot showing the commit list and the live phase checklist in the PR body.

When the validate phase completes, the workflow engine marks the PR as ready for review. A human opens it, looks at the diff, and merges. For PR #65 the reviewer was Chris; the merge commit hit develop on January 2 as 9f283f1, squash-merged, branch deleted afterward.

One honest wrinkle that’s worth surfacing here, because it caught us out. The feat(agent): commit-message convention only survives on the feature branch. When the PR is squash-merged and the branch is deleted, the squash commit’s message is the PR title, and the individual feat(agent): commits are no longer reachable from any ref. That’s why the headline numbers earlier in this post classify commits by author identity (pw-dev-bot vs. humans) rather than by message prefix — it’s the only signal that survives the git lifecycle. We thought we’d be able to attribute work post-hoc by grepping commit messages on develop. We can’t. Author identity is the durable record.

This same lifecycle ran 143 times in January — 95% of those PRs opened by pw-dev-bot, with a median time from PR creation to merge of 1.17 hours.

Parallelism in practice

Those 143 PRs weren’t a sequential queue. They averaged 4.86 per working weekday, but at peak — 21:00 UTC on January 5, within the first week — eleven were in flight at the same time. Each had its own draft PR open, its own feature branch, its own commits queueing up, and its own agent working inside its own dev container. Across the 414 commits behind those PRs, 90% were authored by pw-dev-bot; the remaining 39 came from Tosin and Chris.

Concurrent in-flight pull requests across January 2026, hourly: activity peaks at 11 concurrent PRs on Jan 5 at 21:00 UTC, with a quieter mid-month period and a sustained ramp into the Jan 29 demo.

The cycle-time distribution tells the same story from a different angle. Across the 136 merged PRs, the median time from PR creation to merge was 1.17 hours. p75 was 2.18 hours. p95 was 16.67 hours. The longest single PR took just over 24 hours.

How that throughput is possible without collisions is the architectural point. Each agent runs in its own isolated dev container with the full microservices stack — Firebase emulators, Cloud Run service containers, the agent runtime itself — and scoped credentials that can’t reach anything outside that container. Agents don’t share environments, so they can’t step on each other’s state. They don’t have the credentials to escape their sandboxes, so they can’t accidentally do anything to production. That’s how the parallelism stayed safe.

The median PR cycle of 1.17 hours is worth one more beat. That number includes the agent’s plan-tasks-implement-validate work on the PR — not just the merge after it was ready. Half of all merged PRs in January went from “specify phase finished, draft PR opened by the workflow engine” to “squash-merged into develop” in under 70 minutes, including the time the agent took to actually write the code. The model writes the code. The workflow is what lets eleven of them work at once without collision.

The inference economics

All of this ran on a single Claude Max 20x subscription. Across January, weekly usage hit somewhere between 60% and 70% of the plan’s limit. There was no additional API spend on top — no per-token charges, no overflow billing, no second account.

In 27 days that subscription produced 414 commits, 143 PRs, +265,373 / −3,394 lines, and the deployed platform running end-to-end omnichannel intake. All on one plan, used at less than three-quarters of its weekly capacity.

The relevant comparison isn’t “how much would this have cost with someone else’s tool” — it’s “how much would this have cost on API-priced inference.” We didn’t tabulate the counterfactual token consumption to the dollar, but any reasonable estimate puts it at least an order of magnitude higher than the subscription cost. The math isn’t subtle. Engineers reading this can run their own numbers against current API rates and arrive at the same conclusion.

A specific note: this claim is about Claude. We didn’t run the same workload on Codex, Gemini, or any other model. The subscription-economics story is a real claim about a real workload on a specific provider’s specific plan — not a general statement about agentic coding economics across providers.

There is one piece of forward-looking commentary worth including, because we don’t think it’s a controversial one. Subscription-window inference changes the unit economics of building software. The developers who notice this first, and who learn to organize their work around a workflow that can sustain many parallel agents inside that subscription, are going to ship things that look impossible by the standards of API-priced inference. We just did.

What it deliberately wasn’t

A few things didn’t happen during this build. Given the public news cycle around autonomous coding agents, they’re worth naming.

No agent had production credentials. Each agent ran inside an isolated dev container with a scoped service account that could reach the container’s own emulators and service instances — Firebase, Pub/Sub, the agent runtime, the MCP servers — and nothing else. There was no path from an agent process to a production GCP project, a production Firestore instance, or a tenant’s data. The credentials simply weren’t on the box.

No agent action was anonymous. Every commit on a feature branch carried the identity pw-dev-bot. Every tool call carried an agent_id in the audit log, linked to a specific running container, with sanitized arguments and a result summary. A reviewer auditing the trail of any operation always lands on a specific agent process attributable to a specific PR.

No plan got rewritten to match what shipped. The 22-week Implementation Sequence in docs/05-epic-breakdown.md is byte-identical between its first commit and the last January edit. If we had silently adjusted it to make the comparison look better, the diff would show it.

These properties aren’t unique to our build — they’re the kind of structural choices any team running autonomous agents at scale will eventually have to make. What’s worth noting is that they made the speed of the previous sections possible. Eleven agents working in parallel only stays safe if none of them can reach anything they shouldn’t.

Where the platform is now

This case study is a snapshot of a 27-day window in the middle of a story still in motion. The repository is now about six months old and continues to evolve. The platform itself is heading into production within 45 days, as the operational system for a joint venture with a US personal injury law firm group.

Worth grounding one detail: nobody set out to build this as a customer demo. We built it because we needed it ourselves. Painworth was the original user, with no external customer in view in January. The JV materialized later: a different team that, it turned out, needed almost exactly the system we’d built for ourselves. Which is what dogfooding is supposed to produce.

What this was built with

The workflow engine described above — the labels, the phased lifecycle, the draft-PR-with-checklist pattern, the isolated dev containers, the credential isolation, the per-agent attribution — is what we’ve productized as Generacy. The workflow engine, dev containers, and plugins are open source; a cloud UI and managed components sit on top.

If you’re building production software with AI agents — especially across multiple services or repositories — Generacy is the tooling that made the speed and safety of this build possible. See it at generacy.ai, or read the docs to set up your own project.

Methodology

The source repository is Painworth/ai-lawfirm, private. All numbers in this post come from the closed window 2026-01-02 to 2026-01-31, extracted in May 2026 via the GitHub CLI (gh) and git; the extraction scripts are available on request.

Commit-author classification uses committer identity (pw-dev-bot vs. human authors), not commit-message prefix. The feat(agent): convention exists on feature branches but is lost when those branches are squash-merged and deleted; author identity is the durable signal.

PR cycle time is createdAt → mergedAt, computed across the 136 merged PRs only. It includes the agent’s work on the PR after the workflow engine opened the draft.

Concurrent in-flight PR count, per hour bucket: any PR whose [createdAt, end) interval overlaps the bucket, where end = mergedAt if merged or closedAt otherwise.

The planned-vs-actual timeline sources the planned schedule verbatim from docs/05-epic-breakdown.md at commit 7089e7e6 (2026-01-02T18:04:24Z). The actual schedule is anchored on repo creation (Jan 2 17:57:47Z) and the internal omnichannel demo (Jan 29).

Anonymized in this post: specific customer matters, claim contents, and production credentials. The commit counts, PR counts, cycle-time statistics, repo timestamps, and architectural descriptions are all unmodified from the source data.