Problem
The Go SDK generator is now a structural risk. The current adcp/schemas/generate.py handles simple structs/enums, but the AdCP schemas rely heavily on JSON Schema features that force hand-written Go mirrors today:
oneOf / anyOf response variants and polymorphic core shapes
allOf composition and flat merged rows
- inline nested objects, especially response item rows
- absolute
$id / $ref paths like /schemas/3.0.12/core/context.json
- optional-vs-zero semantics (
*bool, *float64)
- schema drift checks for hand-written inline subshapes
The latest media-buy follow-up needed hand-written CreateMediaBuyResult, MediaBuyData, PackageStatus, and custom marshal logic. That got the wire correct, but it is not a sustainable SDK maintenance model.
Current repo signal
Quick count from the current schema bundle:
- 587 schema files
- 329
oneOf
- 247
anyOf
- 271
allOf
- 4,367 inline object schemas
- 1,094 array item inline object schemas
- 3,248
$ref
- 737
additionalProperties: false
Current Go type ownership is already inverted:
adcp/types.go: 90 hand-written structs
adcp/inputs.go: 14 hand-written structs
adcp/governance_types.go: 12 hand-written structs
adcp/types_gen.go: 77 generated structs, 142 aliases
Initial candidate smoke tests
github.com/atombender/go-jsonschema
Pros:
- Real JSON Schema generator, not OpenAPI-only.
- Claims support for
allOf, anyOf, oneOf, if/then/else, refs, enums, and generated validation.
Smoke-test results:
go run github.com/atombender/go-jsonschema@latest -p adcp adcp/schemas/media-buy/create-media-buy-response.json
Generated only:
type CreateMediaBuyResponseJson map[string]interface{}
That is not acceptable for the main pain point: typed create-media-buy variants.
It also failed to resolve AdCP absolute refs in normal repo layout:
go run github.com/atombender/go-jsonschema@latest --only-models -p adcp \
adcp/schemas/media-buy/get-media-buys-response.json \
adcp/schemas/core/context.json \
adcp/schemas/core/account.json
Failure:
could not follow $ref "/schemas/3.0.12/core/context.json" ... cannot resolve schema
quicktype
Pros:
- Mature multi-language codegen; supports JSON Schema input and Go output.
Smoke-test results:
npx --yes quicktype -s schema -l go --package adcp adcp/schemas/media-buy/create-media-buy-response.json
Failed resolving AdCP absolute refs:
Error: Could not fetch schema , referred to from ...#/oneOf/0/properties/account.
A self-contained oneOf smoke test generated a merged bag-of-fields struct, not a typed union/interface:
type CreateMediaBuyResponse struct {
Status *Status `json:"status,omitempty"`
TaskID *string `json:"task_id,omitempty"`
MediaBuyID *string `json:"media_buy_id,omitempty"`
}
This is better than any, but still not the Go SDK shape we want.
OpenAPI generators (ogen, oapi-codegen, OpenAPI Generator)
Worth a deeper spike because ogen advertises generated sum types for oneOf, but these are OpenAPI generators, not direct AdCP JSON Schema generators. We would need a reliable JSON Schema -> OpenAPI components normalization layer first. That may be as much work as building our own generator unless the layer also solves refs, inline extraction, and allOf flattening.
Recommendation
Do a short spike, but bias toward building an AdCP-specific generator unless one candidate clears the acceptance tests below. The generator should be deterministic, repo-local, and generate protocol data shapes only. Response builders and SDK convenience APIs can remain hand-written.
Acceptance criteria
A replacement generator must:
- resolve AdCP
$id / $ref paths without filesystem hacks
- emit named structs for inline object schemas using stable, predictable names
- emit typed Go representations for
oneOf response variants, preferably sealed interfaces plus variant structs for public SDK APIs
- merge or represent
allOf without dropping fields or creating nested shapes that do not match the wire
- preserve nil-vs-zero semantics for optional booleans/numbers
- generate enums with stable names and values
- preserve
additionalProperties through Extra map[string]any where appropriate
- support schema pointers in drift lint, e.g.
get-media-buys-response.json#/properties/media_buys/items
- produce stable gofmt'd output with low diff churn
- add golden tests for the known hard cases: create media buy response, get media buys response/package rows, delivery response/package delivery, sync creatives assignments, pricing option/deployment/format oneOfs, governance plan inline arrays
- avoid adding runtime dependencies to the root module; generator-only dependencies are acceptable if pinned and isolated under
adcp/schemas tooling
First implementation path if we build
- Add a schema loader that indexes every schema by
$id, relative path, and local file path.
- Build an intermediate representation with resolved refs and explicit schema pointers.
- Extract inline object schemas into named Go types using path-based names plus an override table.
- Implement
allOf object merge for AdCP's common composition patterns.
- Implement
oneOf strategies:
- discriminated const/status branches -> sealed interface + variants
- structural object variants -> generated wrapper with custom marshal/unmarshal, or explicit override
- ambiguous/primitive variants -> documented
any fallback with lint warning
- Replace
KNOWN_TYPES / EXEMPT with a typed override config that records why a type is hand-written and which schema pointer it must drift-check against.
- Port one area first: media-buy response/request types.
Related follow-up from PR #142 expert pass
MIGRATING.md should explicitly document that Config.GetMediaBuys now returns *GetMediaBuysResponse instead of []MediaBuyData.
Problem
The Go SDK generator is now a structural risk. The current
adcp/schemas/generate.pyhandles simple structs/enums, but the AdCP schemas rely heavily on JSON Schema features that force hand-written Go mirrors today:oneOf/anyOfresponse variants and polymorphic core shapesallOfcomposition and flat merged rows$id/$refpaths like/schemas/3.0.12/core/context.json*bool,*float64)The latest media-buy follow-up needed hand-written
CreateMediaBuyResult,MediaBuyData,PackageStatus, and custom marshal logic. That got the wire correct, but it is not a sustainable SDK maintenance model.Current repo signal
Quick count from the current schema bundle:
oneOfanyOfallOf$refadditionalProperties: falseCurrent Go type ownership is already inverted:
adcp/types.go: 90 hand-written structsadcp/inputs.go: 14 hand-written structsadcp/governance_types.go: 12 hand-written structsadcp/types_gen.go: 77 generated structs, 142 aliasesInitial candidate smoke tests
github.com/atombender/go-jsonschemaPros:
allOf,anyOf,oneOf,if/then/else, refs, enums, and generated validation.Smoke-test results:
Generated only:
That is not acceptable for the main pain point: typed create-media-buy variants.
It also failed to resolve AdCP absolute refs in normal repo layout:
Failure:
quicktypePros:
Smoke-test results:
Failed resolving AdCP absolute refs:
A self-contained
oneOfsmoke test generated a merged bag-of-fields struct, not a typed union/interface:This is better than
any, but still not the Go SDK shape we want.OpenAPI generators (
ogen,oapi-codegen, OpenAPI Generator)Worth a deeper spike because
ogenadvertises generated sum types foroneOf, but these are OpenAPI generators, not direct AdCP JSON Schema generators. We would need a reliable JSON Schema -> OpenAPI components normalization layer first. That may be as much work as building our own generator unless the layer also solves refs, inline extraction, andallOfflattening.Recommendation
Do a short spike, but bias toward building an AdCP-specific generator unless one candidate clears the acceptance tests below. The generator should be deterministic, repo-local, and generate protocol data shapes only. Response builders and SDK convenience APIs can remain hand-written.
Acceptance criteria
A replacement generator must:
$id/$refpaths without filesystem hacksoneOfresponse variants, preferably sealed interfaces plus variant structs for public SDK APIsallOfwithout dropping fields or creating nested shapes that do not match the wireadditionalPropertiesthroughExtra map[string]anywhere appropriateget-media-buys-response.json#/properties/media_buys/itemsadcp/schemastoolingFirst implementation path if we build
$id, relative path, and local file path.allOfobject merge for AdCP's common composition patterns.oneOfstrategies:anyfallback with lint warningKNOWN_TYPES/EXEMPTwith a typed override config that records why a type is hand-written and which schema pointer it must drift-check against.Related follow-up from PR #142 expert pass
MIGRATING.mdshould explicitly document thatConfig.GetMediaBuysnow returns*GetMediaBuysResponseinstead of[]MediaBuyData.