Case study · production system · Romania

Three hundred pages of tender documentation, read into one decision a contractor can defend

Corthea ingests public procurement documentation, specifications, draft contracts, bills of quantities and qualification forms, and turns it into a structured answer: what the procedure requires, what the company is missing, and what the contract does to its cash flow before the first invoice is paid. Built by one person, with AI assistance, over seven months.

TypeScript · Next.js · PostgreSQL Multi-tenant, isolation enforced in the database Fail-closed cost governance GDPR export and deletion 2FA with revocable sessions
118,590
lines of TypeScript across 473 files
187
API endpoints, each one authorised server side
59
database tables, 52 under row-level security
242
commits between February and September 2026
7
months, one person, no engineering team
The problem

The expensive mistakes are never on the first page

A Romanian construction firm deciding whether to bid reads several hundred pages under a two week deadline: eligibility criteria, evidence requirements, penalty clauses, payment schedules, guarantee obligations, technical specifications. The work falls to the most senior people in the company, it is done by hand, and the failure modes are ordinary rather than exotic. A qualification document nobody noticed was required. A payment term that quietly makes the project cash flow negative from month one. A penalty clause that turns a two week delay into a loss.

82% of Romanian public contracts are awarded on lowest price, against a European average of 54%. Every incentive pushes the offer down; the risk sits in signing something the company cannot fund. Corthea exists to make that risk visible before submission instead of after.

What it does

Six workspaces, one thread from the documentation to the decision

The platform is not a chat window over a pile of PDFs. It is a sequence a bidding team can actually follow, where each step produces something the next step consumes, and every conclusion stays attached to the paragraph that produced it.

01

Summary

One verdict for the procedure, with the blockers ranked by severity underneath and a readiness score labelled as indicative, not as a promise.

02

Requirements

Every requirement in the documentation, linked to the evidence the company can produce and to the document being prepared for the bid file.

03

Contract money

Working capital, guarantees, the month the balance turns negative, penalties, financing cost. Deterministic engines, not generated numbers.

04

Compliance

Coverage, gaps and open questions, with detected status and human confirmed status stored separately.

05

File and DUAE

Submission checklist and a European Single Procurement Document skeleton, parsed from the authority's own request file.

06

Clarifications

Draft questions to the contracting authority, each one carrying the source paragraph that made it necessary.

The screens

From the running system, unretouched

The interface is in Romanian because the users are Romanian contractors and the source documents are Romanian public procurement files. What matters in each screenshot is not the layout, it is the discipline visible in the numbers.

Corthea decision summary showing a readiness score of 51 and the reasons the procedure is blocked
The decision screen. One verdict, here blocked pending clarification, with the reasons ranked by severity beneath it and each one traceable to the clause that produced it. The score is labelled indicative. The argument is the product; the number is only a handle for it.
Corthea requirement register showing 18 requirements, 6 critical, 0 confirmed
The requirement register. Eighteen requirements found, six critical, and zero confirmed. That last figure is the point: the system refuses to report coverage it has not been shown evidence for, so an empty register reads as empty rather than as ready.
Corthea cash flow simulator showing working capital, first negative month and capital locked in guarantees
The financial simulator. Working capital required, the first month with a negative balance, capital locked in guarantees, and the net result of the scenario. Every input carries its provenance, either read from the documentation or working assumption, needs confirmation, so a default never gets mistaken for a fact.
Corthea compliance matrix with coverage percentage and open critical items
The compliance matrix. The pre-submission checklist, exportable to CSV for the meeting where the decision is actually taken. Detected status and manually confirmed status live in separate columns and separate database fields, because the machine proposes and a named person confirms.
Under the hood

The half a client never sees, which is the half that decides whether they can use it

Anyone can put a language model behind an upload button. What takes seven months is everything that has to be true before a company will let that thing near a procurement file: isolation that holds, money that cannot run away, an answer to the question of who did what, and a way back when something breaks.

Tenant isolation lib/db

Isolation is enforced by PostgreSQL, not by remembering to add a filter in application code. Every request runs inside a tenant context; a query that forgets its tenant returns nothing rather than someone else's documents.

  • 59 tables, 52 of them carrying row-level security policies
  • Tenant context set per connection, not per query
  • Seat management and invitations scoped to the tenant
  • A break-glass path for support access, which writes an audit record when used

Entitlements three layers, server side

What a plan allows is decided on the server every time, in three cooperating layers, so a limit cannot be lifted by editing anything the browser can reach.

  • entitlements for the product surface a tenant has bought
  • rotix-server-entitlements for platform level ceilings
  • offeriq-server-entitlements for module specific gates
  • Trial gates and plan boundaries evaluated in the same path as production access

Document pipeline lib/documents, lib/indexer

Ingestion handles what real companies actually send: scans, legacy Office formats, archives, mixed quality PDFs. Everything is validated before it is opened.

  • MIME validation and path safety on every upload, no trusting file names
  • OCR for scanned pages, with scan quality detected rather than assumed
  • Legacy Office and archive expansion, so a ZIP of DOCs is not a dead end
  • Indexing into a knowledge layer with tiers, pointers and citation cards
  • PDF font and embedded image handling for documents that resist extraction

Deterministic engines lib/contract-money

Every number that touches money is computed in ordinary code, reproducibly, and can be recalculated by hand. No financial figure in this product is generated by a language model.

  • Monthly cash flow with advance, retention and payment term as inputs
  • Bid bond and performance guarantee, with locked capital tracked separately
  • A penalty and interest engine driven by the clauses read from the contract
  • The financial stress point: the month the project first requires outside funding
  • Assumptions marked as assumptions, everywhere, in the data model itself

Cost governance lib/cost-caps

Model spend is metered per tenant and checked before the call is made, not reconciled afterwards. At the ceiling the system refuses work and says so.

  • Per tenant metering with a monthly ceiling, fail closed by design
  • Usage accounting kept separately from billing state
  • Rate limiting in two layers, one static and one runtime aware
  • Search fails closed when authorisation is uncertain, covered by its own test

Security lib/auth, lib/totp

Access control was built at the start rather than added before launch, which is the only time it is cheap.

  • Two factor authentication with TOTP, and sessions that can be revoked individually
  • Secrets encrypted at rest with a dedicated crypto path
  • Invite only registration, with email verification and password reset flows
  • An append-only audit trail across authentication, documents and money

Compliance lib/account-deletion

The obligations a European customer will ask about in the first call, implemented rather than promised.

  • GDPR data export on demand, per account
  • Account deletion that actually removes the tenant's data
  • Public share links that are scoped and revocable
  • Sanitisation on anything leaving the tenant boundary

Operations lib/migrations, backups

The part that decides whether a system survives its first bad day.

  • Backup and restore exercised for real on 19 July 2026, not assumed
  • Versioned migrations across 17 SQL files, applied in order
  • A recovery dossier: written procedure for getting the system back
  • Resilience paths so one failing subsystem does not take the analysis down
Specification

Counted, not estimated

MeasureValueHow it was counted
Application code118,590 linesTypeScript and TSX in app/ and lib/
Source files473Same two directories
API endpoints187Route handlers under app/api/
Pages and components98TSX files outside the API layer
Database tables59Schema, with 52 carrying row-level security policies
Schema migrations17 filesVersioned SQL, applied in order
Commits242February to September 2026
Build time7 monthsOne person, part time, with AI assistance
What I do not claim

The part most portfolios leave out

This section exists because it is the part a serious buyer actually needs, and because a vendor who cannot tell you where their system is weak has not looked.

  • Corthea has no paying customers. It is a production grade system without a commercial launch behind it.
  • Its extraction accuracy has not been benchmarked on a scored set of real Romanian tender files. The infrastructure is finished; that measurement is not.
  • No figure exists for money or time saved, because none was measured. You will not find a percentage on this page.

Which is exactly why every project I deliver includes a scored evaluation on the client's own documents, before launch, instead of a promise of accuracy afterwards.

What it means for you

Corthea is not for sale. It is the reference for what gets built into your product.

If you are a software company, an agency or a product team with a document problem, the relevant fact is not that this platform exists. It is that one person took an unstructured, high stakes document domain and delivered a working system with real access control, cost governance and an audit trail, then wrote down honestly what it does not prove.

The reusable half of it, ingestion, indexing, page level citations, the human review screen, per tenant metering, is the library every client project starts from. That is why a document pipeline from me takes weeks rather than months, and why the boring parts are already solved when we start.

Figures are counted from the repository and the database schema on 10 September 2026, not estimated. Screenshots come from the running system and are unretouched. The 82% and 54% procurement figures are European Commission Single Market Scoreboard data for 2024.

Rotix AI Systems. Document intelligence, web applications and internal tools, built in TypeScript and delivered with tests, documentation and a handover call.