Angel Campa
Colour theme

WorkThe .NET year

CapVeri

Worked out how much of a commercial building's running costs each tenant owed, and showed the arithmetic behind every figure.

Ran on
Railway and Vercel until June 2026, then Cloudflare Workers
Ran
December 2025 to July 2026
Domain
Commercial real estate
Source
cam-reconciliation-saas-capveri-snapshot

Built with

  • Cloudflare Workers
  • Hono
  • React 19
  • TanStack
  • Next.js
  • OpenNext
  • Supabase Postgres
  • Python

Hosted services

  • Railway
  • Vercel
  • Cloudflare Workers
  • Queues
  • Hyperdrive
  • R2
  • Supabase Postgres
CapVeri's Calculation Breakdown drawer, open over the Downtown Tower 2024 reconciliation. Four numbered steps each print their own arithmetic: Step 1 fetches $150,000.00 of operating expenses, Step 2 grosses up with the literal expression 150000.00 * 1.0752688 = 161290.32, Step 3 takes the tenant's 5% share as 161290.32 * 0.05 = 8064.52, and Step 4 deducts the base year as 8064.52 - 7800.00 = 264.52. Behind the drawer, a per-tenant table shows Design Studio at $8,064.52 and FinanceGroup LLC at $4,838.71.
Showing its work, literally. Every number a tenant could dispute expands into the arithmetic that produced it, one step at a time, with the operands visible. Captured from the local stack against seeded data.

The decision

CapVeri’s reconciliation engine was ported from Python to TypeScript, and the Python became a differential oracle: kept in the repository, under continuous test, deliberately never deployed, and checked against every change to the shipped TypeScript engine. Twenty-one property-based Hypothesis suites generated inputs across the money math and asserted that the two engines agreed on a tenant’s bill to the penny.

The written mandate governing the practice set a strict standard for what agreement could prove: “Matching the Python oracle is NOT automatically correct, AND diverging from it is NOT automatically a bug.” Every disagreement between the two engines was checked against lease language and accounting practice before it counted as a bug, with the ruling recorded at the call site.

That scrutiny is what makes the next fact matter. Five places were found where the TypeScript engine deliberately disagreed with the Python reference, each adjudicated and recorded. The sixth is the one the discipline was built to catch: a Stripe subscription-status mapper defaulted unrecognized statuses to "active" access in both engines, so the two engines agreed and the reconciliation was still wrong. A bug two independent implementations get wrong the same way is exactly the class differential testing is supposed to be blind to, and it surfaced anyway.

A separate hardening pass through the same money-parsing code found a second bug from the same family: a Unicode minus sign (U+2212), used in place of an ASCII hyphen in some source figures, was dropped by the amount parser instead of read as a negative sign, turning a credit into a charge. The fix widened the parser’s negative-sign handling to cover the Unicode minus alongside a bare - and parenthesis notation.

The same idea outlived the product. CAMAudit v2 is a second engine for the same problem, started afterwards in a new repository from an empty tree rather than forked from this one, for the reason the Python oracle existed here: two independent implementations that agree are evidence a single implementation cannot produce on its own.

Architecture

Four independently deployed Cloudflare Workers ran in front of a single Supabase Postgres database, with no origin server and no containers anywhere in the request path. capveri-app served a React 19 SPA and reverse-proxied /api/* to capveri-api, a Hono Worker holding 43 route modules, a queue producer and consumer, and two Durable Objects.

capveri-marketing was a separate Next.js site built with OpenNext, 275 MDX pages deep. Reconciliation and lease extraction ran as jobs on five typed Cloudflare Queues, two of them backed by dead-letter queues, rather than inside a request/response cycle.

By the numbers

Each figure carries where it came from and when it was measured. Open one to see the command behind it, where the repository recorded a command.

Application source
364,654 lines across 1,463 files
Provenance

Source portfolio/METRICS.md § Source vs test vs generated

Measured

Test and end-to-end harness
534,150 lines across 1,381 files
Provenance

Source portfolio/METRICS.md § Source vs test vs generated

Measured

Tests passing
16,758 across 1,115 files
Provenance

Source portfolio/METRICS.md § Measured runs

Measured

Python coverage
95.51% total
Provenance

Source portfolio/METRICS.md § Coverage measured in the same runs

Measured

Database
142 migrations, 63 tables, 394 RLS policy statements, 231 indexes, 66 functions
Provenance

Source portfolio/METRICS.md § Database

Measured

Oracle parity suites
21 property-based Hypothesis suites, asserting the TypeScript engine against the Python reference to the penny
Provenance

Source portfolio/ORACLE.md § How parity is enforced

Measured

Production stress program
104 scenario scripts driving the live API, with penny-exact expectations computed offline
Provenance

Source README.md § What's worth your time here

Measured

Python test gate runtime
cut from roughly 38 minutes to 379.06s (6m19s) on pytest's own timer, about 6x
Provenance

Source portfolio/METRICS.md § Measured runs

pytest -n 12 --dist loadscope

Measured

Commit history
5,242 commits over 125 active days, 2025-12-24 to 2026-07-03
Provenance

Source portfolio/METRICS.md § Git history

Measured

Screens

CapVeri dashboard headed "See what needs review and what to do next", with a statement total to check of $19,464 above a Review drafts button, and three cards reading 4 Properties, 4 Need Attention, and $42,966 finalized billing exposure
The landing screen is a work queue: the one number at the top is the money that has not been checked yet.
Downtown Tower 2024 reconciliation stopped at step 3 of 4, with an amber banner reading "1 expense pool has no GL account mappings configured" above a per-tenant table of tenant share, admin fee and final amount
The four steps gate each other: finalize is unreachable until review happens. The amber banner is a real check firing on the seeded data: one expense pool has no GL mappings, so its expenses would reach no tenant. The product says so before the numbers are trusted rather than after.
Extraction review for downtown-tower-suite-500-lease-2024.pdf. The Approve and Commit button is greyed out, with "Load the source PDF before you approve." printed beneath it. The source pane failed to load. The right pane lists extracted lease terms with per-field confidence: Base Year 2024 at 95%, Pro-Rata Share 4.2% at 87%, a Base Year Amount reading "Not extracted" with the note "The AI didn't find a value", and Verification Progress at 0 of 7
The commit gate refused an unverifiable extraction: the source PDF would not load, so Approve & Commit is disabled and the reason is printed underneath it. Model output landed in a holding column and reached the lease record only after a human confirmed each field.
Portfolio overview: bill difference $27,249.16, final tenant total and NOI lift both $42,966, asset value lift $613,805 at a 7.0% cap rate, with a slider spanning 2% to 12%
The same recovery difference in the two numbers a landlord's principal acts on: additional annual NOI, and the implied change in building value. The cap rate is a slider because it is an assumption, and assumptions belong to the user.
Read the repositorycam-reconciliation-saas-capveri-snapshot

Its portfolio/ directory holds the architecture, metrics and testing documents these figures were taken from.