IAB Tech Lab's milestone post on ARTF v1.0 says the Agentic Real Time Framework is final and running in production on real bidstreams. That is the moment hosts start trusting container exit codes and gRPC OK as proof the auction object is still valid OpenRTB.
It is not. A publisher-side agent can return a mutation set where every field is spelled like the reference .proto, the RPC completes, and the orchestrator still cannot apply a single deal activation.
Intent and payload are two halves of one proposal
ARTF v1.0 models each Mutation with an Intent (why the change exists), an Operation (add, replace, remove), a semantic path (which auction entity), and a value oneof: exactly one of ids, adjust_deal, adjust_bid, metrics, or content_data.
The intent tells the orchestrator which member of that oneof must be set. ACTIVATE_DEALS carries deal identifiers in ids. BID_SHADE and related bid-response intents carry adjust_bid. ADD_METRICS carries metrics. They are not interchangeable labels on the same JSON blob.
When an agent builder copies a mutation template from a bid-shading path and forgets to swap the payload constructor, you get something that still looks like ARTF:
{
"intent": "ACTIVATE_DEALS",
"op": "OPERATION_ADD",
"path": "/imp/imp-1",
"adjust_bid": { "price": 4.0 }
}
Protobuf JSON parsing succeeds. The host's gRPC stack returns success. Pass one on the envelope is green. Pass two on the mutation set is where the contract breaks: artf.mutation.payload_intent_mismatch because ACTIVATE_DEALS carries ids, not adjust_bid.
The orchestrator was asked to turn on PMP deals on imp-1. The payload proposes a bid price with no deal id list. There is no defined way to merge that into OpenRTB's pmp.deals array.
The sibling failure: two oneof members at once
Code generators and hand-written JSON sometimes set both ids and adjust_bid on one mutation, especially when a model emits "everything that might matter" in one object:
{
"intent": "ACTIVATE_DEALS",
"op": "OPERATION_ADD",
"path": "/imp/imp-1",
"ids": { "id": ["deal-2"] },
"adjust_bid": { "price": 4.0 }
}
On the wire, a protobuf oneof holds one member. Serializers pick a winner by ordering. The JSON you logged is not the mutation the orchestrator can reason about. That is artf.mutation.payload_ambiguous. ARTF evaluates mutations independently, so splitting deal activation and bid shading into two entries is free and is the fix the spec assumes.
Envelope shape errors hide behind the same OK
The same class of "parsed but wrong" shows up above the mutation list. An RTBResponse whose root is a JSON array, or an RTBRequest wrapped in an extra layer, fails artf.payload.root_not_object before you ever reach intent matching. LLM outputs love extra brackets; transport layers still deliver bytes.
Individual mutations must be objects too. A string entry inside mutations[] triggers artf.mutation.not_object. Typed field mistakes on the envelope (string where the proto expects an enum name) surface under artf.field.type_mismatch. None of that replaces gRPC health checks; it is the document contract ARTF hosts forward to exchanges.
How you catch it before the forward path
I run three passes on every ARTF hop: envelope and carried OpenRTB as protobuf JSON, mutation set against the request's applicable_intents and semantic paths, then apply accepted mutations and revalidate OpenRTB so field damage shows up on the object buyers will see.
The ARTF simulator runs the same engine in a browser: paste an RTBRequest, paste mutations, watch apply-then-revalidate. The longer walkthrough of lifecycle, paths, and independent acceptability is in ARTF mutations and bid request validation.
CLI:
$ rtblint validate --type artf-response --request rtb-request.json rtb-response.json
FAILED (OpenRTB 2.6-202606 ARTF mutation set): …
mutations[3].adjust_bid: Intent ACTIVATE_DEALS carries ids, but the mutation sets adjust_bid.
(artf.mutation.payload_intent_mismatch)
MCP tools validate_artf_request and validate_artf_response (with apply when you need post-patch OpenRTB) exist so an agent implementing AdCP, ARTF, or AAMP-style patches can call the check on its own output. The RPC can still return OK; the wire contract for OpenRTB inside the envelope does not move. rtblint (cargo install rtblint, npm install rtblint-core, MCP rtblint-mcp) is the deterministic check on that object and mutation set. It is independent of IAB Tech Lab and of AAO; the spec does not require it, and nothing in v1.0 replaces pass two before you forward.
Where to go next
If you host agents, validate mutations against the request you actually sent, not a cached envelope from an earlier auction. If you ship agents, treat intent-specific payload builders as separate types: copying adjust_bid construction into an ACTIVATE_DEALS path is the bug that survives unit tests on gRPC stubs.
For rule-level detail on oneof matching, see the payload intent mismatch and ambiguous payload references. For envelope roots, root not object and mutation not object cover the failures that happen before intent logic runs.
Production ARTF means production mutation bugs. gRPC success is transport; OpenRTB validity is still pass three.
Top comments (0)