Business Systems
Your agents should work on your business, not on a copy of it.
One model. Human screens derived from it. Agent tools that write into it — through the same constraints, inside the same transactions, under the same permission check.
The problem, without caricature
The usual architecture has two halves that never quite meet. On one side, a database, a backend, an API and a front end. On the other, documents, embeddings and an agent that reads about the business without ever touching it.
The second half is safe because it is powerless. The moment you let it write, you discover that your business rules live in the first half — in forms, in controllers, in the screens themselves.
What we build
We build the business model first, as a graph where types, instances, attributes and relations share one formalism. From there:
The screens are derived from the model. Not generated once and then maintained by hand — derived, so a change to the model reaches the interface.
Agents call tools against the same model. They discover the schema, resolve canonical references, traverse relations, and write inside transactions.
They can rehearse before they commit. A mutation can be run as a dry run: the agent sees exactly what would happen, and nothing happens.
Constraints belong to the model, not to the form. Cardinality, uniqueness, required values and referential rules are enforced where the write lands, so they hold for a human, an agent, an import or a scheduled job alike.
The refusal
One invoice — FACT-2026-000002, a draft — and one user: a manager whose role
creates and edits invoices but cannot delete them. The delete, from two sides: the manager's
own screen, and an agent badged as the same manager. This is gs-invoice on
live data. Nothing here is a test.
authenticate
{ "login": "gestionnaire", "password": "••••••••" }
→ { "login": "gestionnaire", "principal": "gestionnaire", "badged": true }
remove_instance_by_ts
{ "instanceTs": "1787312345772497468", "dryRun": true }
→ Write refused: the principal gestionnaire may not DELETE
class org.genericsystem.invoice.model.Organisation$FacturesEmises.
authenticate tool, it calls remove_instance_by_ts on the same
invoice, as a dry run. The refusal is the rights check at the write boundary — it fires
before the write, dry run or not.
One permission, enforced at the write boundary — the deletion refused whether the gesture comes from a human (an action not offered) or from an agent (an attempt refused). The invoice is still there.
The proof
gs-invoice is a production invoicing application on this architecture — organisations, clients, products, taxes, invoices, lines, payments, issuance, PDF, credit notes, users, roles, and data separated per organisation. Its scope is real and bounded: bank reconciliation, accounting-software export, reminders, recurring billing and multi-currency are out of scope, as the Why now case states.
gs-postal and gs-importer show the entry path most companies actually need: an existing operational dataset — spreadsheets, exports, a legacy extract — becomes a structured model that people and agents can both operate, with the ETL sharing the very same model as the application.
How it starts
We start as a project, not as a platform sale. A first system, scoped to something you can put in production, with your data and your rules. You are reserving a production capacity against a measured objective — defined and tracked, not guaranteed; the generalisation follows from real use, not ahead of it.
Bring a process you would never let an agent near.
That is the one worth walking through together.