Your model makes claims. An entity says which callers may read its rows. A command says what must be true before it runs and which errors it can return. A route says who may call it. A capability says it is beta.
Assurance is the machinery that checks whether those claims are backed by anything.
It is not a test framework and not a linter. It reads the model, derives every falsifiable statement the model makes about runtime behaviour, and then reports how many of those statements are actually demonstrated — turning the architecture description from documentation into a control.
The one idea
Everything here follows from a single move:
A declaration is a promise. A promise nobody checked is a liability.
When you write guard "caller.role == 'OPERATOR'", you have asserted that a caller without that role is refused. That assertion is either true of the running system or it is not, and which one it is should not depend on whether somebody remembered to write a test.
So the compiler writes the list for you. Every guard, every constraint, every uniqueness rule, every declared error contract, every event a command says it emits — each becomes an obligation: a statement with an identity, that a test run can discharge.
Then the report answers the only question that matters: how many are discharged, and which ones are not?
Why it is not a score
The obvious design is a percentage. Assurance deliberately does not lead with one.
A percentage answers "how much?", which invites gaming and hides shape: 78% tells a reader nothing about which 22% is missing, and whether the gap is cosmetic or is the authorization check on the money-moving endpoint.
Levels answer "what is now true?" instead. Each is a set of enumerable predicates, so a claim of "Level 2" decomposes into specific statements a sceptic can audit one by one. That is a claim that survives an enterprise security review; a number is not.
Levels are computed per bounded context — a mature payments context should not be dragged down by an experimental one sitting beside it.
What it catches that nothing else does
- An overstated maturity. A capability marked
gawhose underwriting obligations are unverified is reported as a falsified claim, not as a coverage gap. This is the sharpest output in the report, and the one an auditor asks for. - An unguarded mutation. A command that changes data and declares no authorization is a specification hole visible before a single test runs.
- Isolation that exists only on paper. An entity in a tenant-scoped context that declares no isolation axis has rows that carry a tenant and nothing that enforces it.
- A promise no longer kept. Comparing today's report against an earlier one catches a guarantee that quietly regressed, which is the failure mode a passing test suite is worst at detecting.
What it does not do
Assurance is honest about its own boundaries, which is the point of it:
- It executes nothing on its own. Run it against a model and every obligation is reported unverified. That is not a failure — the count alone sizes the testing problem the model poses, which is useful before any test exists.
- Evidence has to be folded in. A test run produces outcomes; assurance matches them to obligations by identity, never by matching a test's name against a model element. Name matching mis-attributes silently, and in an assurance report a silent mis-attribution means claiming coverage that does not exist.
- Some obligations need real infrastructure. An isolation claim cannot be discharged by an in-memory substrate that has no row-level security. Those are marked as needing a real deployed stack rather than counted as passed — and the isolation dimension's score is capped and annotated until the run is against one.
Where to see it
- In the Workshop, the Assurance lens — "what your model promises, and how much of it is specified, honest and verified."
- From the CLI,
vishwakarma assure, which writes a readable report, a machine-readable one, and an HTML version.
Read next: Obligations — what the compiler derives, and from what.