Case study · real project, real artifacts

EuroFundos: a dormant SaaS taken from audit to live deployment by an agent pipeline

A grant-discovery SaaS for Portuguese SMEs had been untouched since April 2026. In one working day, the AgentSprint engine audited it, fixed what was broken, shipped two epics of product work through review gates, and deployed it to production. This page links every claim to its artifact.

The day in numbers

Everything below is verifiable in the client's repository — commit history, PR threads, CI runs.

7Pull requests merged (all CI-green)
13Acceptance criteria delivered across 2 epics
+146Net test growth (203 → 349+), incl. a golden calibration gate
€0Model spend (free-tier routing throughout)
1Working day, human time limited to approvals + sign-off

What actually happened

The pipeline's spec→stories→gates structure, with the artifacts it produced.

09:00–10:30
Ground-truth audit

Codebase read end-to-end before any changes: routes, schemas, queue processors, deploy scripts. Output: a fallacy report separating what the docs claimed from what the code did — including three dead pages and an unwired token-budget guard that docs described as active.

10:30–13:00
SPEC-001 → EPIC-001: matching engine truth

Spec written from the audit, decomposed into stories, each implemented TDD-first behind its own commit. Result: wildcard CAE matches eliminated, region vocabulary canonicalized, one scoring path shared by API/batch/refresh, unknown criteria fail closed, and a 35-case golden calibration gate that caught a real macro-lookup bug on first run. → PR #189

13:00–15:00
SPEC-002 → EPIC-002: funnel integrity

Free tier actually gated server-side; eligibility reports moved to double opt-in with a new confirmation surface; stored reports became server-computed (client payloads rejected); scraper put on a weekly cron with automatic deadline sweeps; seeder future-proofed so demos never show expired grants. → PR #190

15:00–17:00
SPEC-003 → EPIC-003: product core

Real SAF-T (PT) XML parser replacing a stub; AI Evaluator v1 on a live route with structured outputs and confidence bands; stale claude-3-haiku defaults replaced with current models; the long-dead token-budget guard finally wired into every AI path. → PR #191

17:00–19:30
Deploy recovery + dependency hardening

VPS recovered from disk exhaustion, app redeployed and verified live; zod major split (v3/v4 across packages) closed; OpenAI SDK 4→6; dependabot backlog triaged with explanations on every closed PR. Docs updated to match reality. Then the site itself was used like a customer would use it — which found more.

Day+1
Self-test audit → three more fixes shipped

Created a throwaway account on the live app and drove it end-to-end. Found and fixed: a BullMQ job-id bug 500-ing every report request; a login loop that bounced every registered user out of the dashboard (container couldn't reach its own public URL); and missing signup provisioning that left new users with a permanently broken workspace. Each through its own reviewed PR: #193, #194, #195.

Sample findings, with the receipts

Three representative bugs from the audit and self-test phases. The pattern is the point: the pipeline verifies claims against behavior, not vibes.

Found by tests The calibration gate caught a live bug

The 35 independently-derived golden cases weren't decorative — on their first CI run they failed against a macro-region lookup error that manual review had missed. Fixed before merge.

Found by self-test Every report email was failing in prod

BullMQ rejects custom job ids containing ':'. Every eligibility-report enqueue threw at runtime while all unit tests passed. Live POST returned 502; after the fix it returns {sent:true} and the verification email lands.

Found by self-test No user could ever reach the dashboard

The session middleware fetched the auth check from a URL the container couldn't resolve — infinite redirect to /login for everyone. Fixed with container-to-container routing; verified by driving seven authenticated pages with a real session.

The discipline, in one diff

Every fix lands with the reasoning attached — this is what "review-gated" means in practice:

# fix(api): BullMQ job ids cannot contain ':' — join with '-'
# (broke report-request enqueue on prod)

-  return `${orgPart}:${userPart}:${data.type}:${channel}:${fingerprint}`
+  // BullMQ forbids ':' in custom job ids.
+  return [orgPart, userPart, data.type, channel, fingerprint].join('-')

# Tests: 151 passed (151) — then deployed and re-verified
# against the live service: {"sent":true,"verifyRequired":true}

What we'll tell you when it's not done

Same project, same day — things the pipeline flagged as still open rather than claiming victory:

OpenReal Stripe price IDs

The build requires non-empty price IDs; placeholders are running until the client creates real prices in Stripe. Stated plainly in the handover, not hidden.

OpenProduction DNS/TLS

App is live on its tailnet funnel URL; pointing eurofundos.eu needs registrar access only the owner has.

OpenEmail domain verification

Transactional email sends via Resend but from an unverified sending domain — deliverability work queued.

Your backlog has a EuroFundos in it.

A dormant repo, a half-built feature, a migration nobody wants to touch — same pipeline, same receipts.

Book intro call Back to overview