← Back to blog
Engineering note2026-06-07 16:00 UTC

Pressure-testing Ota on twenty: one contract for monorepo CI, local dev, integration, and self-host

How twenty pressure-testing turned a real monorepo’s contributor, CI, service-backed integration, and Docker self-host paths into one explicit readiness contract.

Overview

Twenty was a good pressure repo because it looks like the kind of monorepo that contributors and agents routinely misread.

It has frontend, backend, and worker surfaces. It has CI-style validation. It has service-backed integration work. It has a Docker Compose self-host path. It also has platform truth that should not be blurred away just because a repo happens to be large and modern.

That makes it a strong test for a basic claim:

can one contract carry local development, CI verification, integration, and self-host runtime without collapsing into vague “run some scripts” advice?

Before Ota

Before the contract was modeled, the repo already had real execution paths. The problem was not absence. The problem was scattering.

The operating truth lived across:

  • workspace scripts
  • env templates
  • service assumptions
  • Nx task surfaces
  • Docker Compose files
  • contributor knowledge about which path was for what

That is workable for a maintainer. It is weaker for everyone else.

A new contributor or agent should not have to infer all of this by reading commands and guessing intent:

  • which env prep is for native dev
  • which env prep is for Docker Compose self-host
  • which tasks are CI-style verification
  • which paths depend on Postgres and Redis
  • which host platforms the repo is actually claiming

What we modeled

The final contract keeps those paths separate on purpose.

It models:

  • a CI-style verify workflow
  • a native-dev workflow for the contributor loop
  • an integration workflow for the service-backed backend lane
  • a docker-selfhost workflow for the shipped Compose path

It also keeps the runtime surfaces explicit:

  • api
  • web
  • postgres
  • redis

That matters because Twenty is not a single-surface app. It is a monorepo whose readiness changes depending on whether you mean local UI work, backend integration, or packaged self-host startup.

The contract decision that mattered most

The most important part of the Twenty contract was not adding more tasks. It was refusing to flatten unlike paths into one generic “app” story.

That is where readiness tools usually get weak.

If CI validation, native contributor development, service-backed integration, and Docker self-host all get collapsed into one label, then the contract stops being operational. It becomes a prettier README.

The Twenty contract stays explicit instead:

  • verify means lint, typecheck, and core unit-test validation
  • native-dev means contributor startup with the expected local services and env files
  • integration means backend integration work with service setup and database reset
  • docker-selfhost means the repo’s own Compose-based packaged runtime path

That is the correct design. A readiness contract should not erase operating distinctions just to look simpler.

The task layer was also tightened to avoid hiding orchestration inside shell chaining. Aggregate checks now use explicit follow-up tasks through after_success instead of embedding && sequences in one command body. That keeps the contract closer to Ota's own execution model and makes the task graph more legible to both humans and agents.

What Twenty proved

Twenty did not surface a dramatic new Ota bug in the final cleared pass. That is useful in its own right.

What it proved is that Ota’s current model can already carry a serious monorepo shape without cheating:

  • env prep can stay explicit
  • service-backed setup can stay explicit
  • CI and runtime lanes can stay separate
  • container and native verification can both be claimed where the repo actually supports them
  • unsupported host truth can still be expressed honestly instead of being hand-waved away

That last point matters. The contract explicitly scopes the native host context to Linux and macOS. That means Windows is not treated as “probably fine.” It is treated truthfully.

That is the standard repo-readiness tooling should meet more often.

What the matrix now proves

The Twenty matrix is doing the right kind of proof, not just syntax checking.

It covers:

  • ota validate
  • ota doctor
  • ota tasks --use
  • ota tasks --safe --use
  • native and container dry-runs
  • representative native execution
  • representative container execution
  • cross-OS unsupported-host handling where the contract says the host is not supported

That gives a much better signal than a generic “green CI” badge.

It proves that the contract is not only parseable, but operationally legible across the lanes it claims.

Why this matters

Twenty is the kind of repo where execution governance starts to matter more than setup prose.

Large monorepos usually already have commands. What they lack is a single contract that says:

  • this is the CI surface
  • this is the contributor dev surface
  • this is the integration surface
  • this is the Docker self-host surface
  • these are the services and env files each one depends on

That is the gap Ota closes here.

After this pressure pass, the repo does not ask humans, CI, and agents to reconstruct the same operating model from different files. The contract already carries that shape.

That is the point of repo readiness when it is done seriously.

Links: