The contract
A contract is the seal between Plan and Build. It defines “done” as a list of assertions with matchers — mechanical, testable, unambiguous. Once sealed, it can’t be changed. Both Build and Verify work against the frozen version.
What a contract is
A YAML file with three parts: header fields (version, sealed_by, feature), an assertions array, and a file_changes list declaring which files will be created, modified, or deleted. Plan writes it. The CLI validates the structure and seals it with a content hash.
Each assertion has required fields: id (A001, A002...), says (human-readable description), block (test block name), target (what's checked), and matcher (how it's checked). value is required for most matchers.
Matcher types
| Matcher | Checks | Example |
|---|---|---|
equals | Exact match | Regex pattern is exactly /^[a-z0-9]...$/ |
contains | Substring present | Error message contains "Invalid slug" |
not_contains | Substring absent | Test output does not contain "pwned" |
not_equals | Not equal | Exit code is not 0 |
exists | Exists or is present | Test for early-validation exists |
greater | Numeric comparison | Test count is greater than 10 |
truthy | Value is truthy | Validation result is truthy |
The sealing moment
When ana artifact save contract runs, the CLI validates the YAML structure, checks that every assertion has the required fields, and computes a SHA-256 hash. This hash is stored in .saves.json. From that point forward, any modification is detectable — ana verify pre-check compares the current hash against the sealed version before verification begins.
A contract is what makes a verifier possible. Without it, "done" is opinion.
How assertions become tests
Plan writes assertion A001. Build writes a test tagged // @ana A001 whose describe/it text matches the assertion's block field. Verify checks that each assertion ID has a matching test tag and that the test passes independently. The block field is the bridge — it tells Build what the test should be named, and Verify uses it to locate the test.
Acceptance-criteria coverage
A contract must account for every acceptance criterion in the scope. An assertion claims one with an ac field — ac: "AC1" or ac: ["AC1", "AC2"]. An AC that no assertion can mechanically pin is excused with a coverage_waivers entry: ac, kind, and reason, where kind is judgment (untestable by nature) or retired (deliberately dropped). reason is required for both.
At ana artifact save, a pre-seal coverage gate blocks the seal when a scope AC has neither a covering assertion nor a waiver. It runs only for contract version 1.1 or higher, on a scope whose criteria it can parse; legacy 1.0 contracts and unparseable scopes no-op, warning without blocking. ana plan coverage <slug> is the read-only, plan-time preview of the same join — run it while writing the contract.
Coverage is a structural guarantee: a link exists. It does not prove the linked test actually exercises the AC — that remains a judgment for Verify.
Writing good assertions
Good assertions are specific, testable, and mechanical. "The code is well-structured" is not an assertion. "Slugs with shell injection characters are rejected before any operation" is. The says field is for humans; the matcher, target, and value are for machines. The block names the test.