The tempting architecture is two lines long: send the report to the model and do whatever comes back. It works in a demo. In production it fails in all the ways that matter. Personal data goes where it does not need to go, a confident wrong answer becomes an action, and when the service is slow the report quietly disappears.
The architecture that survives production has explicit stages, and only one of them involves the model. Everything else belongs to the application, can be tested without calling the model, and keeps working when the model does not.
This part walks through those stages for Kloofvale's Fix-It routing. Kloofvale Local Municipality is fictional, and its systems and figures are illustrative. This is an independent CloudNala analysis; product details are as documented on 13 October 2026.
Layer 1: collect and prepare state
The application gathers only the facts the decision needs. For Kloofvale that is the report text, the channel it came through, the ward if the resident chose one, and a flag saying whether a location pin was shared. It is not the resident's name, phone number or account history, even though the case system holds all three and the API would happily accept them.
Good state is:
- relevant to the question being asked;
- current, not a stale copy from last night's batch;
- minimised, holding the fewest fields that give a good answer;
- valid against a schema, so malformed input is caught before it costs anything;
- traceable, so every field can be followed back to its source;
- clean of secrets and of personal information the decision does not need.
Minimisation is not only a privacy measure. Every field you add is something the model can be distracted or manipulated by, and something you must now include in your evaluation set. TypeSafe's notes on Jev 1.13 also warn about "context rot" when state grows long. Less state is usually a better decision as well as a safer one.
Layer 2: ask the bounded question
The question defines the contract, so version it like code. Changing one word in an option's description can change behaviour even though the JSON shape is identical. Kloofvale's adapter stores, for every call:
- the question identifier and version, such as
route-v3; - the criteria or rubric version;
- the model identifier;
- the timestamp;
- a correlation identifier linking the call to the Fix-It case.
Layer 3: interpret the signal
The answer is evidence for the next step, not the next step itself. Kloofvale's pilot policy reads:
IF route is not "other"
AND probability of the chosen route >= 0.85
AND needs_human < 0.20
AND a location is present
AND the state passed schema validation
THEN place the report in that team's queue
ELSE place it in the triage review queue
That policy belongs in deterministic code or a governed rules engine. It must be testable with recorded answers and no live model call at all. When a reviewer later asks why a report went to the water depot, the answer should be a rule and a version number, not "the AI decided".
Layer 4: act through authorised services
The action layer is the one that already exists. It checks who is acting, what they are allowed to do, the current state of the case and whether this action has already happened. A high-probability answer never bypasses any of that. At Kloofvale, the routing service writes to team queues through the same internal API the call-centre staff use, with the same permissions and the same audit log.
Idempotency matters here more than people expect. If the adapter retries a call after a timeout and both attempts eventually succeed, the report must still land in one queue once, not twice.
Layer 5: record the outcome
The system needs two records, not one:
- What the model answered and why the application took the path it took.
- What actually happened afterwards: did the water team accept the report, reroute it, or close it as a duplicate?
Without the second record, the team can measure how often the model agrees with reviewers, but not whether the decision layer is improving anything residents care about. Kloofvale's case system already records when a team closes a case, so linking that back through the correlation identifier was a small change with a large payoff.
Test the boundary without the model
Because every layer except the second belongs to Kloofvale, almost all of the testing can happen without a live call. The team keeps a folder of recorded responses: clear cases, close calls, a water answer at 0.84 that should fall just short of the threshold, a missing needs_human field, an empty body, a 529. The policy and the adapter are tested against those recordings on every build, quickly and for free.
That split also clarifies what each kind of test proves. The recorded tests prove that the application does the right thing with any answer it might receive, including bad ones. They say nothing about whether the model's answers are any good. That is a separate question, answered by evaluation against labelled reports, which Part 7 covers. Teams that blur the two end up with either a well-tested application around an unmeasured model or a well-measured model inside an untested application.
A minimal server-side example
The example below shows the shape of Kloofvale's adapter call using the Jev AI endpoint. It is illustrative, not production code. The key stays on the server, the call has a hard timeout, and the response is treated as a signal that policy then interprets.
type Route = "roads" | "water" | "electricity" | "other";
type Outcome =
| { action: "route"; queue: Exclude<Route, "other"> }
| { action: "review"; reason: string };
async function decideRoute(state: object, hasLocation: boolean): Promise<Outcome> {
const response = await fetch("https://thejevai.com/v1/systemone", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.JEV_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "typesafe/jev-1.13",
state,
questions: {
route: {
type: "choice",
instructions: "Which municipal team should handle this service report?",
criteria: {
roads: "Potholes, road markings, damaged or obstructed road surfaces",
water: "Leaks, burst pipes, water interruptions or sewage overflows",
electricity: "Power interruptions, meters, streetlights or exposed cables",
other: "Refuse, parks or anything no approved team covers",
},
},
needs_human: {
type: "noul",
instructions: "Does this report need a person to triage it?",
},
},
}),
signal: AbortSignal.timeout(3000),
}).catch(() => null);
if (!response?.ok) return { action: "review", reason: "decision_unavailable" };
const body = await response.json();
const answers = body.result?.answers ?? body.answers;
if (!answers?.route || !answers?.needs_human) {
return { action: "review", reason: "invalid_response" };
}
const route = answers.route;
const needsHuman: number = answers.needs_human.noul;
if (
hasLocation &&
route.choice !== "other" &&
route.probabilities[route.choice] >= 0.85 &&
needsHuman < 0.2
) {
return { action: "route", queue: route.choice };
}
return { action: "review", reason: "uncertain" };
}
Two notes on that code. First, the line body.result?.answers ?? body.answers is deliberate. On 13 October 2026, Jev AI's documentation described answers as sitting inside a result object, while its own examples, and all of TypeSafe's, showed answers at the top level. Verify the actual response contract before you build, and replace the fallback with proper schema validation. Second, TypeSafe's own API uses a different endpoint (https://api.typesafe.ai/v1/systemone) and model name (jev-latest). The two are not interchangeable without a configuration change, which is one reason to keep the call inside a single adapter.
Production code still needs full response-schema validation, secret management and rotation, redaction, retries, circuit breaking, idempotency, auditing and tests. The example is the shape, not the finished thing.
Failure handling
Both Jev AI and TypeSafe document the same error responses: 401 for an unauthorised request, 422 for a validation error, 429 when you hit a rate limit and 529 when the service is overloaded. Each needs a different response, and only two of them should ever be retried.
Kloofvale's adapter applies:
- a timeout on every call;
- exponential backoff with jitter for 429 and 529 only, within a retry budget;
- no retry for 422, because sending the same invalid request again will fail again, and an alert so someone fixes it;
- an immediate alert for 401, because it means a key or configuration problem;
- a circuit breaker, so that a struggling service is not hammered with retries;
- a safe fallback that puts the report in the triage queue;
- duplicate protection at the action layer;
- alerts based on business impact, such as "triage queue growing faster than staff can clear it", not only on HTTP status.
The principle is simple. If the decision service is unavailable, a burst main must not disappear, a payment must not run without its control, and a resident's report must not be silently discarded. When everything fails, Kloofvale is back where it started: two people reading reports. That is a slower system, not a broken one.
Key points
Only one layer calls the model. Preparation, policy, action and evidence all belong to your application.
Minimised state is safer and often more accurate. Send what the decision needs, nothing more.
Version questions like code. A wording change is a behaviour change.
Policy turns signals into actions. Keep it deterministic and testable without the model.
Every failure has a destination. Retry only what is temporary, and make sure nothing is lost.
Previous: Part 2, Choice, Score and Noul. Next: Part 4, Jev is not an agent. All parts are on the series page.
How CloudNala can help
We design and review the boundary around AI decisions: the state builder, the adapter, the policy layer, the failure paths and the evidence record. The aim is an architecture where the model can be wrong, slow or unavailable without the business process failing, and where every routed case can be traced back to a rule and a version.
Work with CloudNala
CloudNala helps organisations move from technology ambition to practical execution across cloud, AI, data, platform engineering and digital services.
Whether you are exploring AI, modernising your cloud environment, building a public-sector digital service, or turning an idea into a working MVP, we can help you shape the roadmap and deliver the next step.
Book an AI Decision Review or write to us at consult@cloudnala.co.za
