Functional Conformance
Security conformance proves a feature protects what it was specified to protect. Functional conformance proves a feature does what it was specified to do — and it runs on the same assertion and CI verification engine.
Every asset or capability a feature exposes carries two obligations: deliver its value (functional) and protect its value (security). Both are derived from one model of intent, expressed as testable statements, and verified continuously in CI. The model detail page shows the two side by side — Functional conformance X% · Security posture Y% — one model, two lenses.
Conformance is to intent, not to code. Most AI test generation works from the implementation and locks in current behaviour, so it can tell you behaviour changed but not that the code is wrong. Mipiti works from the spec it already holds, so a functional objective can catch a feature that runs fine yet diverges from what it was specified to do.
Capabilities × Conditions
The security methodology is Security Properties × Capability Attackers. Functional conformance is the same shape with a different set of forces — operational instead of adversarial:
| Security | Functional | |
|---|---|---|
| Unit of value | Asset | Capability (intended behaviour) |
| Forces | Capability attackers | Operating conditions |
| Obligation | A property (C/I/A/U) survives | A behaviour/contract holds |
| Output | Control Objective | Functional Objective |
| Satisfied by | Control | Functional Test |
| Verified by | Assertion → CI | Assertion → CI (same) |
Capabilities are the behaviours the feature must deliver — the functional analogue of an asset. Each is derived from the feature description and bound to the component(s) that implement it.
Conditions are a principled taxonomy of operating forces, grounded in established testing theory (this is what makes the cross-product rigorous rather than "ask the model for edge cases"):
| Condition | What it stresses | Basis |
|---|---|---|
| Nominal | valid input, normal state → correct outcome | requirements-based testing |
| Boundary | limits and edge values | boundary-value analysis |
| Equivalence | representative input partitions | equivalence partitioning |
| Invalid input | malformed / out-of-domain → graceful rejection | negative testing |
| State / precondition | wrong order, unmet precondition, invariants | design-by-contract |
| Dependency failure | a downstream dependency fails → degradation | fault injection / resilience |
| Concurrency | retries, duplicates, races | property-based invariants |
The high-value objectives are the non-nominal ones — boundary, state, and dependency-failure — exactly the cases ad-hoc testing misses and a code-derived generator cannot invent, because they are not in the code yet.
Functional objectives
A functional objective is one cell of the Capability × Condition cross-product, written as a Given-When-Then acceptance criterion and bound to component(s):
Given the payment processor is unreachable, when a checkout is submitted, the system SHALL queue the order and surface a retryable error rather than charging the card. — capability: Checkout · condition: dependency failure
The cross-product gives the same exhaustiveness the security methodology has: every capability, against every applicable condition, yields a non-arbitrary set of testable obligations.
Two tabs: intent and assurance
Functional conformance is split across two tabs, mirroring how security separates Overview (what should hold) from Assurance (how it's proven):
- Functional — the intent: the coverage-posture summary, the Capabilities × Conditions matrix, and the objectives (what the feature should do).
- Functional Assurance — the proof: the open gaps (implement + verify, or waive), the reversible waived list, and the specified tests to implement.
Selecting a matrix cell on the Functional tab filters the objectives in place; that selection carries over to Functional Assurance, where it filters the tests for the same cell — so a single click drills from "what should behave here" to "what proves it."
The coverage matrix
The Functional tab shows a Capabilities (rows) × Conditions (columns) grid. Each cell is one of:
- Verified — a satisfying test has a CI-attested passing assertion.
- Covered — a satisfying test passes, but the result is not yet CI-attested.
- Failing — a satisfying test's assertion is failing. A promise the feature is specified to keep is being broken — the signal to act on first.
- Untested — an objective exists but nothing proves it yet.
- No test yet — the condition applies to this capability but no objective has been authored. A visible gap, not a blank.
- Not applicable — the model's own structure proves the condition cannot apply here (for example, concurrency for a capability that touches no mutable shared state). Excluded from the obligation count, exactly as an unreachable objective is excluded from security posture.
The applicability check is deterministic and conservative: a condition is applicable unless the structure proves otherwise, so a real obligation is never hidden, and any objective you author yourself always takes precedence over the check.
The scorecard rolls these up into a percent-verified headline plus the obligation-cell accounting (applicable / missing-objective / not-applicable / waived), and lists the top gaps to work on next.
Handling a gap
A gap — a Failing, Untested, or No test yet cell — has four ways to resolve, and each has a path:
- Prove it. Implement the specified test and submit its assertions; CI verifies them and the cell turns Covered/Verified. This is the primary path — usually the coding agent, via
get_functional_scan_prompt→ implement →submit_functional_tests. - Fill it. For a No test yet cell, either regenerate (which authors the objective + test for applicable conditions) or add one by hand: select the cell in the matrix and use + Add objective in the objectives section (capability and condition pre-fill from the selected cell). To author a test yourself, use + Add test on the Functional Assurance tab and pick the objectives it proves — with a cell selected, those objectives are pre-checked. Hand-authored objectives and tests are kept across regeneration.
- Fix the objective. If an objective is wrong or spurious, edit or delete it.
- Mark it not applicable. If a condition genuinely doesn't apply to a capability — beyond what the structural check could prove — waive the cell with a reason from the Functional Assurance tab. A waiver removes the cell from the gap list and the obligation denominator (so coverage isn't dragged down by an obligation you've deemed inapplicable), records who waived it and why, is shown distinctly from a structural Not applicable, and is reversible. A waiver is refused if the cell has a live objective — so it can never hide a real obligation; delete the objective first if it truly doesn't apply.
The waiver is the functional counterpart of a security non-applicability assumption: a human-judgment exclusion that is justified, audited, and reversible, complementing the deterministic structural check.
Functional tests and evidence
Generation doesn't stop at objectives — Mipiti also specifies the functional tests to implement, one implementable brief (arrange / act / assert) per objective. This is the functional analogue of security control generation: just as the security pipeline derives concrete controls from control objectives, the functional pipeline derives concrete test specifications from functional objectives. The coding agent's job is to implement each specified test, not to decide what to test.
A functional test satisfies one or more objectives and starts as not implemented. Once implemented, evidence assertions (test_exists, test_passes, and the rest of the assertion types) attach to it and are verified by the same two-tier CI runner as control evidence — Tier 1 mechanical, Tier 2 semantic. There is no separate verification pipeline: functional posture is computed from verified evidence the same way security posture is.
The coding-agent loop
Functional conformance is most useful inside the coding agent, over MCP. The loop mirrors the control loop, for behaviour instead of defence:
generate_functional_objectives— derive capabilities, objectives, and the concrete tests to implement from the feature description.get_functional_scan_prompt— the agent brief: for each not-yet-verified test, its implementation brief and the objectives it proves.- Implement the specified test, then
submit_functional_tests(itstest_exists/test_passesassertions). - CI verifies the assertions.
get_functional_coverage/check_functional_gaps— see the updated posture and what remains.
Generation infers capabilities, conditions, objectives, and tests from the spec, but bindings must reference real entities — inferred references that don't ground to the model are dropped. Regenerating replaces the generated capabilities, objectives, and tests while preserving anything you authored by hand.