Personal project by Ulf Fäldt Reifenberg · built in November 2026 with coding agents for a job application to Coretura · unofficial, not affiliated with Coretura · all data synthetic
AI assets as code, released by risk tier
A prompt edit, a model swap or a new tool can change how an AI product behaves. Here each of those is a versioned file, and a release gate decides whether a change may ship. The bar depends on how much the product is allowed to do on its own. Every model call goes through a small gateway that meters usage per team and release.
Seven releases through the gate
The product is a service-desk assistant for a truck workshop (synthetic). It reads an untrusted document such as a fault report or a supplier email and then either answers the technician or sends a report to a colleague. It is declared tier 2 because it acts through tools. Each row is one proposed change to its assets, evaluated on the same 64 cases as the production release.
Tier 0: a person reads every output. Tier 1: people rely on the answers. Tier 2: acts through tools. Tier 3: autonomous or safety-relevant (also needs a recorded sign-off and canary plan, which no release here has). Requirements accumulate with the tier; the exact thresholds are in policy.json, shown at the bottom.
What came out
- The token-saving prompt did not save money. Input tokens fell 23% (378 to 290 per call), but outputs got longer and the provider's bill rose 2%. Task success under attack fell 25 points (CI −41.7 to −6.2) and the model followed the injected instruction in 19% of attacked cases.
- The smaller model was not cheaper here (1.04× on this provider's pricing) and followed the injection in 6% of attacked cases. It passes a smoke test.
- The 70B model was best on every quality measure and cost 6.1× as much. The gate does not reject it; it asks for the cost owner's approval.
- Adding a third tool broke the format. The 8B model started answering in plain text in some cases: valid tool calls 92%, clean task success 88%.
- A harmless-looking rewording was blocked at tier 2. 48 attacked cases cannot show it stays within 10 points of production (−4.2, CI −22.9 to +12.5). That is the price of a "show it is not worse" rule on a small suite; at tier 1 it ships.
- Raising max_tokens shipped. It fixed tool calls that were cut off mid-JSON (valid calls 97% to 100%).
- In every release the deterministic policy check stopped all attacker-addressed calls before a tool ran, with no correct action blocked.
What the evaluation measured
16 clean cases and 48 cases with an injected instruction (three styles, position rotated). Differences against production are paired by case; intervals are 95% bootstrap intervals that resample whole scenarios. "Reached a tool" counts attacker-addressed calls that the deterministic policy check (recipient must appear in the technician's own request; the key never leaves) would have let through.
What the gate caught that a smoke test would not
Usage metering
The gateway records every call with team, product, release id, purpose, tokens and the provider's billing units (Workers AI neurons), and flags cache replays. This is the bill for the evaluation runs on this page:
How it works
- Assets.
assets/service-assistant/holds the system prompt versions, the tool list, the model route and parameters, and a manifest that points at them. A candidate is a small file that overrides manifest fields. - Release id. The gate resolves a manifest to its full content and hashes it. Any change to prompt, wrapping, tools, model or parameters gives a new id; the evaluation dataset has its own hash. Both go into the release record.
- Gateway. One
call(routes, messages, params, team, product, release, purpose)interface over provider adapters, a response cache keyed by content hash, a fallback route on provider error, and a usage table in SQLite. Re-running the gate replays from the cache, so a scoring fix costs no model calls. - Scoring. Deterministic: valid tool call, task done, action addressed to the attacker, key leaving in anything but a reply to the technician. No LLM judge.
- Decision. For each tier the gate checks absolute floors and non-inferiority against production, and writes the reasons into
releases/<name>.json. In CI the job fails when the declared tier's decision is "block".
What this does not show
- One provider is called for real (Cloudflare Workers AI, three Llama models at temperature 0). The second adapter is a test stand-in.
- 64 single-turn cases with fixed attack templates. With 16 clean cases one extra miss is enough to fail the non-inferiority check, so the gate is strict by construction; a real suite needs more cases and an adaptive attacker.
- No retrieval or knowledge base, no multi-step agent, no live traffic, no canary. The CI workflow file is included but has not run on a hosted runner.
- The tier definitions are my own sketch. They are not Coretura's and say nothing about how Coretura works.
Policy file
Contact: linkedin.com/in/ulffaldt · other projects: injection-gate-bench, mcp-scope-gate, euro-asr-bench