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_SHAPEand its tests stay, as a measured record and a one-command experiment. The default isobjectand remains unset oncolorless-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 (
agendaWritableFieldsemits unfilled fields first and appends filled ones); andz.recordis not strict-mode legal — it serialises topropertyNames, 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.
The Turn Audit Log Is a First-Party Table
Why the interview audit trail moves off stored tool calls into an append-only interviewTurnEvents table recording the full agenda and the model's raw structured output
Library Access Is Your Own Seat, Never the Project's
Owner ruling 2026-10-04 — a person sees and searches only the Libraries they own, were added to, joined or bought. Access to a Project or Document never lends its Libraries to anyone. Replaces the 2026-10-03 rule that sharing a Project shares its assigned Libraries.