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
verifyworkflow - a
native-devworkflow for the contributor loop - an
integrationworkflow for the service-backed backend lane - a
docker-selfhostworkflow for the shipped Compose path
It also keeps the runtime surfaces explicit:
apiwebpostgresredis
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:
verifymeans lint, typecheck, and core unit-test validationnative-devmeans contributor startup with the expected local services and env filesintegrationmeans backend integration work with service setup and database resetdocker-selfhostmeans 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 validateota doctorota tasks --useota 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:
- Contract: twenty
ota.yaml - Matrix workflow: test-ota-contract-matrix.yml
- Green matrix run: #27097038819
- Closed PR: twentyhq/twenty#21289
Take action