Goldie Blocks

Interview Writes Stay Object-Encoded

Why the array-shaped writes encoding is not adopted, why the key-discriminated union that would fix its value corruption is strict-legal after all, and why it is rejected on cost rather than on impossibility

Decision

The interview turn's writes field stays object-encoded: a z.strictObject with one nullable key per writable field, built from the agenda. The array-shaped alternative measured behind INTERVIEW_WRITE_SHAPE (plan R9) is not adopted, and the env flag's default stays object.

The record exists mainly to fix a false claim, because the false version is the one that will otherwise be reasoned from:

A key-discriminated union over the array's entries is legal in OpenAI strict mode. It is rejected on measured cost, not on impossibility.

Context

R9 encoded writes two ways behind one env var so both could run against the same eval-results/ baselines. The array shape emits only actual writes, making the schema byte-identical for a section regardless of which fields are filled — the prerequisite for explicit cache breakpoints.

Six eval runs each. The savings were real: output 113.1 → 84.4 tokens/turn (−25.4%), prompt 7,728 → 7,004 (−9.4%), 6/6 pass in both modes, and no invented writes in either — the relocation of the discipline text out of per-field .describe() into the agenda tail held.

But array mode silently corrupted values. 19/19 YesNo writes arrived as JSON true instead of the authored "Yes"/"No" literal that flow conditions compare against with strict equality, and every child's age was dropped from children_collection. The eval gate caught none of it, because it asserts which keys landed rather than what landed in them. A single-call control run with the bare {key, value} shape reproduced the corruption, confirming it is grammar-determined and not run-to-run noise.

The claim that was wrong

The first write-up explained the corruption as structural: strict mode "has no key-discriminated value schema", so value must be a union over every shape the section can hold, and per-field typing has nowhere to live.

The first half is true and remains the explanation for the flat {key, value} shape. The generalisation to strict mode is false.

A live probe against gpt-5.6-luna established that z.discriminatedUnion("key", …) — one member per field, each carrying its own correctly-typed value — is accepted by OpenAI strict mode and ran 7/7 correct: YesNo as "Yes"/"No" strings, collection item age intact.

The gotcha that most likely produced the "impossible" conclusion is much narrower than it looked: a bare const member without a sibling "type" key is rejected. At a glance that reads like the discriminated-union form itself being refused.

Why it is still rejected

Cost. The discriminated union measured ~24% more prompt-expensive than a lean object encoding — 529 vs 427 schema tokens — because the per-member boilerplate outweighs the saving.

The prize was never the wire format. R9's −25% output saving came from stripping per-field discipline text, not from the array shape. The object encoding can claim that same saving without changing the wire format at all.

The risk is asymmetric. A turn is dominated by input tokens (~7k prompt vs ~113 completion), so the remaining upside is small against the data-integrity risk of migrating an encoding that has already demonstrated silent value corruption.

Consequences

  • INTERVIEW_WRITE_SHAPE and its tests stay, as a measured record and a one-command experiment. The default is object and remains unset on colorless-hare-654.
  • If anyone revisits the array encoding, two constraints are load-bearing and were found by test rather than review: the key enum must be sorted, or the schema still churns as a section fills (agendaWritableFields emits unfilled fields first and appends filled ones); and z.record is not strict-mode legal — it serialises to propertyNames, which OpenAI rejects outright.
  • The caching upside once claimed for schema-stabilisation work does not hold up: steady state was measured at 75–100% cache reads (66.8% across 24 calls), not the 0% an earlier cold-cache reading suggested. Do not justify that work on caching alone.
  • The related data-integrity rule is that YesNo is string-valued, not boolean — see the corpus DSL spec and Folios Schema.

Revisit when

The SDK stack unlocks explicit cache breakpoints (blocked today: @convex-dev/agent peer-depends on ai@^6, incompatible with @ai-sdk/openai@4.x), and a prompt-side measurement shows stable-schema caching worth more than the ~102 extra schema tokens the discriminated union costs. Adopting it would also require the eval gate to assert values, not just keys.

On this page