Obligations

An obligation is one falsifiable statement your model makes about runtime behaviour. You never write one: they are derived from what you have already declared, which is what makes the list complete rather than aspirational.

They are the denominator everything else is scored against.

What gets derived, and from what

Obligation kindDerived fromThe statement it makes
Guarda guard on a commanda caller failing this predicate is refused
Policya policy, inline or referencedthis operation is reachable only by callers the policy admits
Speca spec on a fielda value violating this constraint is rejected
Invariantan invariant on an aggregatethis must hold after every mutation
Uniquenessa unique indextwo rows cannot share these values
Emissiona command's emitsperforming this operation publishes this event
RouteContracta route and its errorsthis endpoint answers on this path, with these statuses
Isolationisolation_axes on an entitya caller on one axis value cannot read another's rows

A real application produces more of these than people expect. The booking example in these docs — a services business with a catalog, a diary, reviews and payments — derives 278, most of them route contracts and emissions.

That number is useful on its own, before any test exists. It is the size of the testing problem your model has already committed you to.

Every obligation has an identity

This is the load-bearing detail.

An obligation carries a stable id derived from what it came from. When a test run reports its outcomes, assurance folds them in by that id — never by matching a test's name against a model element.

The alternative is worse than it sounds. Name matching appears to work, degrades silently as names drift, and the failure mode is an assurance report claiming coverage that does not exist. A report that is confidently wrong is worse than no report, because people stop checking.

Generating the cases

You can write the tests by hand, and their outcomes fold in like any others. But the framework will also generate one case per obligation, each carrying its obligation id, by declaring a testkit slot in your project manifest:

project.vishwakarma
testkit go {
  tiers [domain]
  scope [booking, catalog, scheduling, payments, reviews]
}
  • tiers names the substrate. domain is hermetic: the application assembled with in-memory adapters and driven through the real handler chain, so caller resolution, tenancy, permission and policy middleware all run.
  • scope names which bounded contexts to generate cases for. Leaving one out is a decision, not an oversight — an append-only audit trail with no client-facing write surface would pad the suite without testing anything you claim.

Running that suite produces the results file; assure reads it.

Verified, unverified, unverifiable

Three states, and the third is the interesting one.

  • Verified — a passing case discharged it.
  • Unverified — nothing demonstrated it. This is the default and it is not an accusation; it is the starting point.
  • Unverifiable in this run — a passing case exists, but the substrate it ran on cannot establish the claim. An isolation obligation cannot be discharged by in-memory repositories, because they have no row-level security to enforce.

That third state is what stops the report from lying in the most tempting direction. A hermetic run that counted isolation obligations as passed would report a security guarantee that had never been exercised — and it would do so on every project, by default, which is precisely how a control becomes theatre.

The report records the reason instead of the tick, and the isolation dimension stays capped and annotated until the run happens against a real deployed stack.

Waivers

Some obligations will not be discharged, and pretending otherwise helps nobody. A waiver accepts one explicitly:

vishwakarma assure --waivers waivers.yaml

A waiver names the obligation by id, states a reason, and expires. The expiry is the whole design: a permanent exception is indistinguishable from a forgotten one, and a list of accepted risks nobody revisits is how an exception becomes a policy by accident.

Read next: The dimensions — how obligations roll up into what the report actually scores.