---
title: "Kalceo: Building a B2B SaaS with AI Agents"
description: "A concrete retrospective on building Kalceo, a quotes and invoicing SaaS for the construction industry, from the first ticket to production using a fleet of Claude agents."
created: 2026-05-05T10:00:00+02:00
updated: 2026-05-13T12:00:00+02:00
tags: ["AI", "SaaS", "KittyClaw", "Agents", "Construction", "Retex"]
image: /images/blog/kalceo-mvp-saas-b2b-agents-cover.webp
card: /images/blog/kalceo-mvp-saas-b2b-agents-card.webp
---

## The Problem We Chose

The construction industry still runs on emails and Excel spreadsheets. Craftsmen and small construction companies spend hours writing quotes by hand, chasing clients, and tracking payments. Tools exist — but they're either too complex or designed for structured mid-sized companies, not the solo tiler or plasterer.

Kalceo starts from a simple observation: a quote should take 10 minutes, not two hours.

The MVP scope is deliberately narrow — no accounting module, no bank integration, no e-signature in the first version. Just: create a quote, convert it to an invoice, PDF it, email it to the client. The bare minimum to be useful from day one.

---

## From Ticket to Production

Kalceo was built with the same infrastructure as Ekioo.com itself: **KittyClaw** as the orchestrating kanban, and a fleet of specialized Claude agents taking tickets one by one.

Scoping the MVP started with a raw feature list, then refined into atomic tickets. Each ticket has a precise scope: "Create the quote input form", "Generate PDF from HTML template", "Send the quote by email via Brevo". Nothing vague, nothing cross-cutting. Agents work well with clear boundaries.

The workflow for each feature looks like this:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 260" style="width:100%;max-width:720px;display:block;margin:2rem auto;font-family:monospace">
  <defs>
    <filter id="kce-glow-g" x="-30%" y="-30%" width="160%" height="160%">
      <feGaussianBlur stdDeviation="3" result="blur"/>
      <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
    </filter>
    <filter id="kce-glow-p" x="-30%" y="-30%" width="160%" height="160%">
      <feGaussianBlur stdDeviation="3" result="blur"/>
      <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
    </filter>
    <filter id="kce-glow-b" x="-30%" y="-30%" width="160%" height="160%">
      <feGaussianBlur stdDeviation="3" result="blur"/>
      <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
    </filter>
    <pattern id="kce-dots" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
      <circle cx="1" cy="1" r="1" fill="#ffffff08"/>
    </pattern>
  </defs>
  <rect width="720" height="260" rx="12" fill="#0d1117"/>
  <rect width="720" height="260" rx="12" fill="url(#kce-dots)"/>
  <!-- Ticket box -->
  <rect x="20" y="90" width="120" height="80" rx="8" fill="#0d2818" stroke="#22c55e" stroke-width="1.5" filter="url(#kce-glow-g)"/>
  <rect x="20" y="90" width="120" height="4" rx="4" fill="#22c55e"/>
  <text x="80" y="120" text-anchor="middle" fill="#22c55e" font-size="11" font-weight="bold">KittyClaw</text>
  <text x="80" y="138" text-anchor="middle" fill="#94a3b8" font-size="10">Ticket Todo</text>
  <text x="80" y="155" text-anchor="middle" fill="#94a3b8" font-size="10">→ assignee</text>
  <!-- Arrow 1 -->
  <line x1="140" y1="130" x2="188" y2="130" stroke="#475569" stroke-width="1.5"/>
  <polygon points="188,125 196,130 188,135" fill="#475569"/>
  <!-- Dispatcher box -->
  <rect x="196" y="90" width="120" height="80" rx="8" fill="#1a0d2e" stroke="#a855f7" stroke-width="1.5" filter="url(#kce-glow-p)"/>
  <rect x="196" y="90" width="120" height="4" rx="4" fill="#a855f7"/>
  <text x="256" y="120" text-anchor="middle" fill="#a855f7" font-size="11" font-weight="bold">Dispatch</text>
  <text x="256" y="138" text-anchor="middle" fill="#94a3b8" font-size="10">Lain / Automation</text>
  <text x="256" y="155" text-anchor="middle" fill="#94a3b8" font-size="10">→ launch agent</text>
  <!-- Arrow 2 -->
  <line x1="316" y1="130" x2="364" y2="130" stroke="#475569" stroke-width="1.5"/>
  <polygon points="364,125 372,130 364,135" fill="#475569"/>
  <!-- Agent box -->
  <rect x="372" y="90" width="130" height="80" rx="8" fill="#0d1a2e" stroke="#3b82f6" stroke-width="1.5" filter="url(#kce-glow-b)"/>
  <rect x="372" y="90" width="130" height="4" rx="4" fill="#3b82f6"/>
  <text x="437" y="116" text-anchor="middle" fill="#3b82f6" font-size="11" font-weight="bold">Claude Agent</text>
  <text x="437" y="132" text-anchor="middle" fill="#94a3b8" font-size="10">programmer</text>
  <text x="437" y="147" text-anchor="middle" fill="#94a3b8" font-size="10">qa-tester</text>
  <text x="437" y="162" text-anchor="middle" fill="#94a3b8" font-size="10">content-writer</text>
  <!-- Arrow 3 -->
  <line x1="502" y1="130" x2="550" y2="130" stroke="#475569" stroke-width="1.5"/>
  <polygon points="550,125 558,130 550,135" fill="#475569"/>
  <!-- PR / Review box -->
  <rect x="558" y="90" width="130" height="80" rx="8" fill="#0d2818" stroke="#22c55e" stroke-width="1.5" filter="url(#kce-glow-g)"/>
  <rect x="558" y="90" width="130" height="4" rx="4" fill="#22c55e"/>
  <text x="623" y="120" text-anchor="middle" fill="#22c55e" font-size="11" font-weight="bold">PR → Review</text>
  <text x="623" y="138" text-anchor="middle" fill="#94a3b8" font-size="10">Owner merge</text>
  <text x="623" y="155" text-anchor="middle" fill="#94a3b8" font-size="10">→ Production</text>
  <text x="360" y="230" text-anchor="middle" fill="#475569" font-size="10">Kalceo MVP — per-ticket workflow</text>
</svg>

Each agent works in its own isolated **git worktree**. It creates a branch, codes the feature, commits, opens a PR. The owner (me) reviews and merges. When the agent finishes, the ticket moves to Review in KittyClaw.

---

## What the Agents Built

Over a few weeks, the fleet produced the core of the MVP:

- **Authentication** — sign-up, login, account management
- **Service catalog** — each company creates their own reusable quote line items
- **Quote creation** — adding line items, calculating totals (pre-tax/VAT/all-in), statuses (draft, finalized, sent, accepted, declined)
- **PDF export** via browser print (server-generated print-ready HTML page)
- **DOCX export** for editing in Word

The `programmer` agent handled most of the backend features (JavaScript, Cloudflare Workers + D1). The `qa-tester` wrote integration tests and caught several bugs in total calculations — particularly rounding issues on lines with decimal quantities.

---

## What Worked, What We Had to Fix

**What worked well:**

Agents are excellent on well-bounded tasks. Give a ticket a clear scope ("generate a PDF from the Quote Razor component"), and it's often a single round-trip. The agent reads the existing code, proposes an implementation consistent with what's already there, and produces something testable.

The isolated worktree structure also simplified parallelization: two or three features could advance simultaneously without interference.

**What we had to fix:**

Agents sometimes miss side effects — especially in complex business logic. The totals calculation is the clearest example: the first pass handled rounding correctly for whole-number quantities, but there was a subtle bug on decimal quantities (e.g. 1.5 units × unit price) that produced an incorrect pre-tax total by a few cents. The `qa-tester` caught it, the `programmer` fixed it — but it illustrates why human review remains essential.

There were also UI regressions between features. One agent adds a component, another modifies the layout, and CSS breaks in a corner neither tested. We introduced a simple rule: every PR must include a screenshot of the affected page before going into Review.

---

## Where Kalceo Stands Today

The MVP is **live** at [kalceo.fr](https://kalceo.fr). A Facebook campaign has been running for a few weeks, targeting craftsmen and micro-businesses in the French construction industry.

Early feedback confirms the problem: users who sign up are indeed solo or micro-enterprises in construction who still manage their quotes in Excel or Word. The main friction they report is importing their existing catalog — a clearly identified improvement for V2.

The rest of the backlog centers on two axes: **automatic client follow-up** (sending a reminder email X days after sending a quote with no response) and **accounting export** (export in formats compatible with popular French accounting software).

---

## What This Changes

Kalceo wouldn't have existed as a solo project — not in this timeframe. Not because the features are impossible to code, but because the sum of background tasks (auth, data models, PDF generation, emails, tests, deployment) would have taken several months part-time.

With the fleet, it works differently: you spend time scoping tickets, reviewing PRs, deciding what's in the MVP and what isn't. The background work is delegated. That doesn't mean "error-free" — but it means you can iterate fast, fix fast, and stay focused on the product rather than the code.

That's the real shift: **the acceleration doesn't come from agents coding faster, but from you deciding faster** because execution friction has disappeared.
