In the 48 hours after OpenAI’s 7/9 dual launch (GPT-5.6 and ChatGPT Work on the same day), the agent ecosystem started realigning along three visible axes. OpenAI temporarily lifted the 5-hour rate limit on Plus / Business / Pro. Codex weekly active users crossed 6 million. And one production agent — Ploy, who builds real marketing websites with an agent that plans across apps, reads and writes code, takes its own screenshots, and decides for itself when the build is done — migrated its core model from Claude Opus 4.8 to GPT-5.6 Sol and posted first-impression numbers that beat Opus 4.8 across the board: 2.17x faster wall-clock, 27% lower cost, 0.970 vs 0.936 on the visual score.
Each of those is news on its own. Together, they read as a signal: model-level differences are being replaced by engineering-level differences. Any builder trying to migrate a production agent to GPT-5.6 today is going to hit the same wall — the visible ledger looks right, but the stack underneath isn’t ready.
What actually matters: a production agent that swapped models in public
Ploy’s migration writeup by Lorenzo Gentile, posted on 7/9, is one of the few public accounts of a real production agent actually swapping models, with line-item numbers:
- Per-task cost: Opus 4.8 $3.06 → GPT-5.6 $2.22 (down 27%)
- End-to-end build time: 8m00s → 3m42s (2.17x)
- Input tokens: 2.60M → 1.70M
- Output tokens: 33.0K → 17.1K
- Visual score (against generated website screenshots): 0.936 → 0.970
What makes the numbers stand out isn’t the 27%, it’s that they exist at all. For the past 18 months every “model X vs model Y” comparison has been a benchmark bake-off; Ploy is the first case I’m aware of where a real production agent — actually building full marketing websites for paying customers — swapped models in public and showed the books.
The actually interesting section of the post, though, is the three hidden costs that follow the table. Ploy writes: “we thought we were running an A/B test. Three days in, more than half of our tool calls were returning wrong file contents. After we finished migration, we realized our entire engineering stack had quietly specialized around Opus 4.8.”
The three hidden costs Ploy describes
Each of these isn’t “GPT-5.6 can’t” — it’s “GPT-5.6 does things in a way your schema wasn’t expecting.”
Tool-argument overreach: GPT-5.6 fills in everything
Ploy’s code tool schema has 25 top-level parameters: 1 required and 24 optional. In the three days after the migration, their production trace shows:
- GPT-5.6 made 6,635
code(read)calls, all 6,635 came with all 25 properties filled in (100%) - Claude Opus 4.8 made 2,898 calls, only 4 came with all 25 (0.1%)
- Claude Sonnet 5 made 1,933 calls, zero came with all 25
GPT-5.6, when handed an optional field with a schema default, will fill it in. It invents plausible values for every optional property: offset: 0, limit: 100, timeout: 120000, siteId: "00000000-0000-0000-0000-000000000000". Ploy’s code(read) had a “this is a real read, scoped to one site” semantic — passing a placeholder UUID dropped the call into the “list everything across sites” code path. The result: 52%–64% of file reads came back empty, the agent kept retrying because the file looked missing, and tool calls per task rose by 30%.
Ploy’s fix was at the schema layer. Every optional property got rewritten to required-but-nullable, expressed as anyOf[T, null] — meaning “this field can be null, and if the model doesn’t fill it, that’s null.” Then at the provider boundary they stripped out the nulls before the request hit the backend.
// before (GPT-5.6 will fill defaults in)
{
"type": "object",
"properties": {
"offset": { "type": "integer", "default": 0 },
"limit": { "type": "integer", "default": 100 },
"timeout": { "type": "integer", "default": 120000 },
"siteId": { "type": "string" }
},
"required": ["siteId"]
}
// after (only the fields the model actually filled survive)
{
"type": "object",
"properties": {
"offset": { "anyOf": [{ "type": "integer" }, { "type": "null" }] },
"limit": { "anyOf": [{ "type": "integer" }, { "type": "null" }] },
"timeout": { "anyOf": [{ "type": "integer" }, { "type": "null" }] },
"siteId": { "anyOf": [{ "type": "string" }, { "type": "null" }] }
},
"required": ["siteId", "offset", "limit", "timeout"]
}
After this: empty reads went from 52% to 0%, tool calls per task dropped 30%. Opus 4.8’s behavior was “leave it empty if not needed.” GPT-5.6’s behavior is “if the schema has a default, use the default.” Your stack has to explicitly opt into the second model’s contract.
Prompt-cache design cliff: partial-prefix implicit caching was removed
Opus 4.8’s prompt cache was an org-scoped partial-prefix cache. A 29K-token static prefix (system prompt + tool descriptions + team style conventions) hit 92%–96% of the time, by default, no work required. Switching to GPT-5.6 without changes dropped the hit rate to 0%.
GPT-5.6 dropped partial-prefix implicit caching in favor of explicit prompt_cache_key and prompt_cache_breakpoint markers. No markers, no cache, and every cold write costs about $0.18. Ploy’s fix was to split the static prefix into three layers — system prompt (stable) → breakpoint → task template (stable) → breakpoint → task instance (variable) — and put an explicit prompt_cache_breakpoint on each layer. They keyed by workspace, not by conversation, because a single workspace has many conversations sharing the same stable prefix. First-call hit rate went from 0% to 83.7%; total uncached input tokens dropped 28%.
But there’s a hidden ceiling on top of that: OpenAI’s same-cache-key throughput is roughly 15 rpm — past that the request fans out to cold nodes. So the key topology matters:
- Global key: hits the 15 rpm ceiling, every request spills to cold, ends up more expensive
- Per-conversation key: always cold, every new conversation’s first request is a cold write
- Per-workspace key: the sweet spot — every conversation within a workspace shares the same stable prefix, high hit rate, no quota collision
What Ploy settled on decouples the stable prefix from the conversation lifecycle and binds it to the workspace lifecycle. Sounds like a config change, but it’s actually a different philosophy of prompt design. In the Opus era you wrote the prompt and forgot it. In the GPT-5.6 era you have to think about how many times this prompt is going to be read under which key.
Reasoning replay: server-side item references break under multi-worker append-only input
The third cost is the hardest to spot in advance. GPT-5.6’s Responses API defaults to a server-side item-reference model: you call Responses, get back an rs_xxx handle, and on the next turn you pass previous_response_id: rs_xxx to extend the reasoning chain. Clean in theory. In Ploy’s production trace after the migration, a new class of error appeared: Item 'rs_xxx' not found.
It’s not that the server deleted data; it’s that the server state and the client’s append-only input drifted apart. Ploy’s agent streams tool outputs from workers — each worker pushes new tool output into the same rs_xxx reference. With multiple workers pushing in parallel, the order in which the server receives those pushes diverges from the order of the main session timeline. The next time the main session reads rs_xxx, the server’s internal index doesn’t match what the client thought it was looking at, and it returns 404.
Ploy’s fix was to set store: false explicitly. That switches Responses to a different mode: no server-side item reference is stored, the full reasoning content is embedded as an encrypted blob in the response body itself, and on the next turn you pass that blob back as input. That puts the API back into an append-only-local-replay model. The cost is per-request payload size; the gain is deterministic client session model — append-only, server state not involved, multi-worker safe.
The Responses API effectively has two modes: reference mode and replay mode. The Opus era had only replay mode (Anthropic’s multi-turn is client-side message arrays). GPT-5.6 defaults to reference mode. Under multi-worker append-only load, replay is the only mode that doesn’t drop.
Other signals from the same week
Beyond Ploy, three other signals from this week are worth pulling together:
- Codex at 6M weekly active users. My 7/10 ChatGPT Work post had this at 5M; it’s added another million in the last few days, still measured in “tasks actually completed” rather than tokens or API calls.
- The Plus / Business / Pro 5-hour rate limit got temporarily lifted. OpenAI is using distribution-side levers to tell the market “GPT-5.6 supply is fine.” Reads as part of the same package as the 7/9 dual launch.
- Tibo posted a 5-minute X walkthrough on routing Claude Code through CLIProxyAPI to a GPT-5.6 Sol backend. Builder-side signal: Ploy isn’t the only one migrating, and at least one KOL thinks a public tutorial is worth writing.
All three point the same direction: GPT-5.6 supply is healthy, demand is migrating off Opus fast.
What this means for builders: a three-step migration order
If you’re considering migrating a production agent from Opus 4.8 to GPT-5.6, Ploy’s three lessons collapse into a single checklist:
- Rewrite tool schema, add a null-strip boundary at the provider edge. Every optional becomes
required-but-nullable(anyOf[T, null]); nulls get stripped before the request lands at the backend. This single step is the schema-level “GPT-5.6 adapter” — it alone is what took tool calls per task down 30%. - Re-architect the prompt for cache topology. Three explicit
breakpointlayers, key on per-workspace rather than per-conversation. Then re-run the trace and watch the cold-write monthly bill, Ploy’s number was total uncached input tokens down 28%. - Pick your reasoning replay mode explicitly. Under multi-worker concurrency, set
store: falseand go back to append-only local replay. Single-worker serial sessions can keep server-side item references for the token savings.
None of these three steps is “change business logic.” They are stack-level changes. Ploy’s stack had quietly specialized for Opus 4.8 for a year; they didn’t realize it until migration, at which point the specialization became visible as breakage.
The underlying point: when the visible ledger says the new model is faster and cheaper, what you’re actually doing with “swap the model” is “swap the model’s default contract.” The visible costs (token spend, latency, quality) move; the hidden costs (schema defaults, cache topology, replay semantics) only show up after you’ve already committed.
Where this differs from 6/27 and 7/10
I’ve written about GPT-5.6 twice before on this blog. The 6/27 ID-check post covered the user-facing change: identity verification at login, the Washington Post reporting, “whitelist distribution” becoming part of the product definition. The 7/10 ChatGPT Work post covered the market-facing change: OpenAI shipping model + agent on the same day, Codex at 5M WAU, Musk publicly crediting Anthropic, agent task-completion becoming the new competitive axis. Neither post got to the engineering ledger.
This post is specifically the engineering ledger for builders. Ploy put a real production agent through Opus 4.8 → GPT-5.6 in public and surfaced three costs (schema overreach, cache cliff, replay mismatch) that are all the same shape: “we thought we were comparing models, we were actually comparing how quietly our stack had specialized around the incumbent.” The fixes aren’t about business logic, they’re about schema / prompt topology / replay mode — stack-layer changes.
Read 6/27 and you can answer “what changes for me as a user.” Read 7/10 and you can answer “what is OpenAI betting on now.” Read this and you can answer “I run an agent and I want to switch models — what’s the order of operations.” Three pieces together, the full picture of GPT-5.6’s first week.
References
- Ploy: Migrating a production AI agent to GPT-5.6 (Lorenzo Gentile, 2026-07-09)
- OpenAI: GPT-5.6 introduction page
- OpenAI Responses API docs: prompt_cache_key and prompt_cache_breakpoint
- OpenAI Responses API docs: the
storeparameter and state management - Ploy’s schema migration discussion thread on r/LocalLLaMA