Skip to content

feat(client): typed render builders for preview_creative discriminator#772

Merged
bokelley merged 2 commits into
mainfrom
typed-render-builders
Apr 22, 2026
Merged

feat(client): typed render builders for preview_creative discriminator#772
bokelley merged 2 commits into
mainfrom
typed-render-builders

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Adds urlRender / htmlRender / bothRender factory helpers + a grouped Render namespace — directly targets the preview_creative drift class matrix v13 surfaced (2 hits on /previews/0/renders/0/preview_url: must have required property 'preview_url').

Mirrors the asset-builder pattern shipped in #771. PreviewRender is a oneOf on output_format with three variants (url / html / both); the discriminator decides which sibling becomes required. Matrix runs kept catching renders that dropped either the discriminator or the sibling it makes required.

// Before — easy to forget output_format or preview_url
renders: [{ render_id: 'r1', role: 'primary', preview_url: 'https://preview/r1' }]
// ↑ fails validation: missing `output_format`

// After — helper injects the discriminator; input type requires preview_url
renders: [urlRender({ render_id: 'r1', role: 'primary', preview_url: 'https://preview/r1' })]

Design

  • Three helpers, one per variant. Can't call urlRender({...}) without preview_url because its input type requires it.
  • Return type Omit<Variant, 'output_format'> & { output_format: <literal> } — regen-stable whether or not the generated interface declares the discriminator in the future.
  • Discriminator spread last so a runtime cast that slips output_format: 'wrong' into the input still produces the correct tag.
  • Render namespace (Render.url(...)) as convenience for renders[] arrays that mix variants.

Skill updates

build-creative-agent and build-generative-seller-agent cross-cutting pitfall callouts now recommend the render helpers alongside the asset helpers, completing the set:

  • imageAsset / videoAsset / htmlAsset / urlAsset for each asset
  • urlRender / htmlRender / bothRender for each render

Both classes of drift the matrix has been catching now have typed construction paths Claude can lean on.

Test plan

  • npm run typecheck clean
  • 5 subtests under render builders — discriminator injection per variant, field forwarding, spread-order invariant, namespace equivalence
  • npm test — no regressions
  • Matrix v14 (post-merge) measures whether the preview_creative drift class clears

Minor version bump — new public API, no breaking change.

Adds urlRender / htmlRender / bothRender factory helpers + a grouped
Render namespace. Each helper takes the render payload without
output_format and returns the tagged object — urlRender({ render_id,
preview_url, role }) can't be called without preview_url because the
input type omits output_format and requires the sibling field the url
variant needs.

Mirrors the asset-builder pattern (#771). PreviewRender is a oneOf on
output_format with three variants; matrix runs kept catching renders
that dropped the discriminator or its required sibling. These helpers
make the wrong shape syntactically harder to express.

Return type is Omit<Variant, 'output_format'> & { output_format: <literal> }
for regen-stability. Discriminator spread last so a runtime cast can't
overwrite the canonical tag.

Skill pitfall callouts in build-creative-agent and
build-generative-seller-agent now recommend the render helpers alongside
the asset helpers.
@bokelley bokelley merged commit 6a2c2c5 into main Apr 22, 2026
9 checks passed
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.

1 participant