← Wiki

Validate an AI-Proposed API Contract Change

This tutorial shows how to separate an AI proposal from an API contract decision and from execution evidence. You will inspect a tested Mule baseline, read the proposal recorded before any code change, compare it with the engineer’s decision, and run the parameterised MUnit cases. The companion article explores what that decision says about AI-assisted engineering.

Evidence boundary: The baseline, AI proposal, contract decision and parameterised MUnit run were recorded on 27 September 2026. The public lab repository holds the artefacts. An end-to-end HTTP check of APIKit routing remains outstanding; this tutorial verifies the contract and handler.

Prepare the lab

You need Java 17 and Maven, with Mule dependencies either cached locally or available from your configured repositories. MUnit needs permission to open local dynamic ports. The fixture uses synthetic customer data and needs no backend, credentials or deployment environment.

git clone https://github.com/brunosouzas/ai-assisted-engineering-lab.git
cd ai-assisted-engineering-lab
JAVA_HOME=/path/to/jdk-17 mvn -version

The baseline is commit 82eb3332c889d31e75351df58c69948d4d50fcc1. The first contract implementation is 394490d1df171e75f7a0851e3c5c09cc1b3f6f64; later commits converted the tests to parameterised suites. Compare the contract and code changes:

git diff 82eb3332c889d31e75351df58c69948d4d50fcc1 394490d1df171e75f7a0851e3c5c09cc1b3f6f64 -- src/main src/test pom.xml

Run the baseline first, then return to the current version and run its tests:

git switch --detach 82eb3332c889d31e75351df58c69948d4d50fcc1
JAVA_HOME=/path/to/jdk-17 mvn -o clean package
git switch main
JAVA_HOME=/path/to/jdk-17 mvn -o clean package

Remove -o if the dependencies are not cached. Check BUILD SUCCESS and the test, failure and error counts. In the recorded run, the baseline passed six tests with 60% application coverage. Seven fixed-value tests passed after the contract change; they were then consolidated into six parameterised executions, which also passed, with 42.86% coverage. Do not compare those coverage percentages in isolation: the tests invoke the handler directly, while the change added routing and error-handling components outside the suite. The first local baseline build used Java 25 and failed; selecting Java 17 resolved that tooling mismatch.

Read the contract before proposing code

Open src/main/resources/api/customer-lookup-api.raml, src/main/mule/customer-lookup-api.xml and the src/test/munit/customer-lookup-*-parameterized-suite.xml files on main. For the original tests, inspect the baseline commit. Ask three questions:

  1. Which responses does the RAML declare for GET /customers/{customerId}?
  2. Which branch of the Mule flow produces each response?
  3. Which MUnit assertion would fail if that behaviour changed?

Initially, CUST-001 returned 200, a malformed ID returned 400, a well-formed but unknown ID returned 404, and CUST-500 simulated a sanitised 500. The contract decision later changed the unknown-customer response to 400, reserving 404 in this lab for a missing endpoint.

Compare the AI proposal with the decision

The proposed change was to add CUST-002 with status INACTIVE. Read the exact prompt and read-only response in the proposal record. The assistant suggested 200 in the RAML’s existing response shape. It also identified the missing assumption: the schema allowed INACTIVE, but the contract did not say whether callers should see that customer.

Read Bruno’s decision in the experiment record before inspecting the final code. In this lab, business errors use 400, missing endpoints use 404, and system failures use 500. Errors share the { "error": { "code": number, "reason": string, "message": string } } shape, and the correlation ID remains in the response header. Compare that decision with the proposal and with the inactive-customer case in the MUnit YAML.

Run and interpret the final tests

On main, compare the RAML, customer-lookup-process, the two parameterised suites and their cases in src/test/resources/munit/. Verify that CUST-002 returns business error 400; CUST-999, previously 404, also returns 400; and the errors use the shared shape. The APIKit configuration assigns 404 to an unmatched route and 405, 406 and 415 to other protocol errors. Check the MUnit output for both success/correlation cases and all four error cases.

When reporting the exercise, separate what the AI proposed, what the engineer decided, what the code implements and what MUnit executed. The six passing MUnit cases cover handler behaviour; they do not prove the HTTP response for an unmatched route. mvn mule:run is not a goal of the Mule Maven Plugin 4.9.1 used here, so that local attempt did not start an application for an HTTP check. The result does not measure productivity or predict whether a profession will be replaced.