Skip to content

fix(translate): satisfy OpenAI strict-mode object/array requirements on makeNullable's typeless-branch fallback - #832

Merged
steventohme merged 3 commits into
mainfrom
fix/openai-strict-mode-union-branch
Jul 24, 2026
Merged

fix(translate): satisfy OpenAI strict-mode object/array requirements on makeNullable's typeless-branch fallback#832
steventohme merged 3 commits into
mainfrom
fix/openai-strict-mode-union-branch

Conversation

@steventohme

Copy link
Copy Markdown
Collaborator

What

#829 fixed makeNullable's fallback for a typeless optional property (no "type"/"anyOf"/"enum") by stamping an explicit value-type union (["string","number","boolean","object","array","null"]) on the synthetic anyOf branch. Necessary, but not sufficient — OpenAI's strict-mode validator has two more requirements for any node whose type can be "object" or "array", with no exception for a union that merely includes those types alongside primitives:

  • additionalProperties:false (+ empty properties/required) on every object-capable node — 400: "'additionalProperties' is required to be supplied and to be false"
  • an "items" sub-schema on every array-capable node — 400: "array schema missing items"

Fix: the synthetic branch now carries both. items bottoms out at primitives only (no recursion) — deliberate, since this branch already means "no type information at all."

Why

Both gaps were found live, back to back, on the same real-OpenAI smoke run (#828): a request to gpt-5.4-nano with a Workflow-tool-shaped typeless args param 400'd on additionalProperties; after fixing that, the very next request 400'd on the array/items gap. Split out of #828 per review — that PR is the test harness; this is the standalone production fix it caught.

Testing

  • go test ./internal/translate/... — strengthened TestStrictify_TypelessOptionalGetsExplicitValueType with assertions on all four fields (additionalProperties, properties, required, items); their absence is exactly what let both gaps ship.
  • go build ./..., go vet ./..., gofmt -l . all clean.

…on the makeNullable typeless-branch fallback

#829 fixed makeNullable's fallback for a typeless optional property (no
"type"/"anyOf"/"enum") by stamping an explicit value-type union
(["string","number","boolean","object","array","null"]) on the synthetic
anyOf branch so OpenAI strict mode would accept a typeless node. That fix
was necessary but not sufficient: OpenAI's strict-mode validator has two
more requirements that apply to any node whose type CAN be "object" or
"array", with no exception for a type union that merely includes those
alongside other primitives:

  - additionalProperties:false (+ empty properties/required) on every node
    whose type can be "object" — 400: "'additionalProperties' is required
    to be supplied and to be false"
  - an "items" sub-schema on every node whose type can be "array" — 400:
    "array schema missing items"

Both gaps were found live, back to back, on the same real-OpenAI smoke
run (#828): the first request to gpt-5.4-nano with a
Workflow-tool-shaped typeless `args` param 400'd on additionalProperties;
after fixing that, the very next request 400'd on the array/items gap.

Fix: the synthetic branch now also carries additionalProperties:false, an
empty properties map, an empty required list, and an items sub-schema.
items bottoms out at primitives only (string/number/boolean/null) rather
than recursing into the same object/array requirements one level down —
deliberate and bounded, since this whole branch already means "no type
information at all" and a precise recursive value contract isn't the
point.

Strengthened TestStrictify_TypelessOptionalGetsExplicitValueType with
assertions on all four fields (additionalProperties, properties, required,
items) — their absence is exactly what let both gaps ship, since the
existing test only checked the type union itself.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown

Claude finished @steventohme's task —— View job


  • Run git diff and analyze added comments
  • Post review with comment-length suggestions

Posted 3 advisory nits (won't block merge):

  1. strictify_openai.go lines 210–215 — 6-line block; parenthetical error messages restate what the code already shows; WHY fits in 4 lines.
  2. strictify_openai_test.go lines 258–260 — 3-line block restating what the assertion messages below already say; collapses to 1 line.
  3. strictify_openai_test.go lines 268–270 — same pattern; collapses to 1 line.

@workweave-bot workweave-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Advisory only — comment-length nits. Won't block merge.

Comment thread internal/translate/strictify_openai.go
Comment thread internal/translate/strictify_openai_test.go Outdated
Comment thread internal/translate/strictify_openai_test.go Outdated
@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown

T-Rex T-Rex Logs

What T-Rex did

  • Ran the focused translation test for TypelessOptionalGetsExplicitValueType and observed a PASS with EXIT_CODE: 0.
  • Ran the translation suite and confirmed the translate packages reported ok, with EXIT_CODE: 0.
  • Ran gofmt; no files needed formatting and EXIT_CODE: 0.
  • Ran go vet; the vet step completed with EXIT_CODE: 0.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "fix(translate): satisfy OpenAI strict-mo..." | Re-trigger Greptile

steventohme and others added 2 commits July 23, 2026 21:59
Co-authored-by: workweave-bot <bot@workweave.ai>
Co-authored-by: workweave-bot <bot@workweave.ai>
@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown

Claude finished @steventohme's task —— View job


Claude finished @steventohme's task —— View job


  • Run git diff and analyze added comments
  • Post review with comment-length suggestions

Posted 1 advisory nit (won't block merge):

  1. strictify_openai.go lines 211–216 — 6-line block; parenthetical 400-message quotes restate what the code already shows; the WHY fits in 4 lines.

@steventohme
steventohme enabled auto-merge (squash) July 24, 2026 02:00
@steventohme
steventohme merged commit 258427e into main Jul 24, 2026
11 checks passed

@workweave-bot workweave-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Advisory only — comment-length nits. Won't block merge.

Comment on lines +211 to +216
//
// OpenAI strict mode requires, on every node whose type can be "object": additionalProperties:false
// + empty properties/required (400: "'additionalProperties' is required to be supplied and to be false").
// On every node whose type can be "array": an "items" sub-schema (400: "array schema missing items").
// items bottoms out at primitives — this branch means "no type info at all", so a shallow
// terminating schema is deliberate.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//
// OpenAI strict mode requires, on every node whose type can be "object": additionalProperties:false
// + empty properties/required (400: "'additionalProperties' is required to be supplied and to be false").
// On every node whose type can be "array": an "items" sub-schema (400: "array schema missing items").
// items bottoms out at primitives — this branch means "no type info at all", so a shallow
// terminating schema is deliberate.
//
// OpenAI strict mode: object-capable nodes need additionalProperties:false + empty
// properties/required; array-capable nodes need an items sub-schema. items bottoms out
// at primitives — deliberate, this branch means "no type info at all".

Was 6 lines; parenthetical 400-message quotes restate what the code already shows. The WHY fits in 4.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 53bfe70. Configure here.

"properties": map[string]any{},
"required": []any{},
"items": map[string]any{"type": []any{"string", "number", "boolean", "null"}},
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strict defaults clobbered by merge

Low Severity

The synthetic branch sets OpenAI-required additionalProperties, properties, required, and items, then copies every key from the original node on top. A typeless node that already carries additionalProperties (or a conflicting required/items) overwrites those defaults and can reintroduce the same strict-mode 400s this change is meant to close.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 53bfe70. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants