Repository navigation
docs(emitters): layer runtime policy and read embedded policy - #1332
Open
OmarAlJarrah wants to merge 7 commits into
Open
OmarAlJarrah wants to merge 7 commits into
OmarAlJarrah wants to merge 7 commits into
Conversation
Runtime policy was one tree per project, so an operation needing its own timeout or retry had nowhere to say so, and a document's own SDK configuration, kept verbatim as vendor extensions, would have been parsed by each emitter its own way. Policy and hints are now layers, each holding a document-wide override and overrides by operation: what a document embeds, the project's, the emitter's. A setting takes the highest layer that sets it, an operation's value over its layer's document-wide one, and a declared IR fact beats every layer. A new emitters/embed reader turns vendor extensions a configuration maps into the lowest layer of each, decoding each value strictly and reporting what it cannot read. The vocabulary gains connection-error retries, Retry-After, a throttling backoff and redirects. Whether an operation is safe or idempotent is a plan fact, OpPlan.Effects: declared, else from its HTTP method. An enum a client reads keeps a value it does not list, as a union does, and a oneOf value two variants admit decodes as plan.Policy.ExclusiveMatch says. A service hint makes a parameter a client setting; nothing is hoisted by inference, since a shared parameter stops being shared as an API grows, and the plan only reports candidates.
This was referenced Nov 2, 2026
EmitterInput.Policy became policy.Resolved when policy gained layers and per-operation overrides, but the refiner context still held a single policy.Policy, so a step had no way to read an operation's own settings. Ctx.Policy now holds the resolved policy the emitter is given.
The rule saying what a oneOf decodes a value two variants admit to landed at the end of the section on a variant's null, which it has nothing to do with. It belongs with how a variant is recognized, which is also where T-35 links it.
This was referenced Nov 2, 2026
…ata' into docs/emitters-policy-layers
…ata' into docs/emitters-policy-layers
…ata' into docs/emitters-policy-layers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Five gaps in what configures a generated SDK, resolved as one contract: who may set a behavior, at which scope, and from where.
Layers and per-operation policy (#710). Every source of runtime policy is now a
policy.Layer, holding a document-wide override and overrides keyed byOpID. The layers, lowest first: the policy a document embeds, the project's, the emitter's.policy.ResolvegivesEmitterInput.Policyas apolicy.Resolvedper operation.OpIDmatching nothing is reported, and so is an override of an embedded value, as an info.Embedded policy and hints (#712) (
docs/emitters/contract/embedded-policy.md). A newemitters/embedreader turns the vendor extensions a mapping names into the lowest layer of runtime policy and of hints.Unmodeled, as a promotion leaves it.vendor_extensionentries, whose keys are the source's own, which is what Unmodeled key names are an unversioned contract that consumers are currently forced to read #429 asks the IR to state as stable. A mapping naming a key of another reason is refused.ShapeHint.AlsoMountanswers an operation exposed under several sub-clients, which oneRemountcould not. Parameters were already ID-keyed (ir: parameters are bound by name, so same-named parameters in different locations are ambiguous #596).Missing behaviors (#711).
RetrygainsConnection: a request that got no response is retried only for an idempotent operation by default. It also gainsRetryAfter, honored by default and capped atBackoff.Max, andThrottled, the throttling backofffailuresalready named.Redirectsfollows RFC 9110 §15.4 and never forwards credentials to another origin.plan.PolicygainsOpenEnums(an enum a client reads keeps a value it does not list, asOpenUnionsdoes for variants) andExclusiveMatch(firstoronlyfor aoneOfvalue two variants admit). Unknown members follow each model's declaredAdditional, with no setting.OpenEnumsdoes not override the declaredEnum.Closed.Closedstates which members the document lists, and the SDK sends only those. It says nothing of what a later API version lists, which is what a client must survive. All 8,314 golden enums are closed.Effects from the method (#696).
OpPlan.Effects{Safe, Idempotent}comes from the declaredOperation.Idempotency(its token kind counts as idempotent), else from the method underplan.Policy.MethodSemantics, as RFC 9110 §9.2 defines it, markedInferred. "Destructive" is stated as not derivable. The retry rule above readsEffects, and an idempotency key the runtime generates counts only where the IR declares its token. On the golden spec no operation declares idempotency, 1,841 use a safe method and 2,693 an idempotent one.Client settings (#719) (
docs/emitters/operations/client.md#client-settings).ShapeHint.ClientParamsnames it by location and wire name, never by inference. Whether every operation of a scope shares a parameter changes as an API grows, and an inferred setting would move the parameter in or out of every method's signature, a break caused by an additive spec change.account_idis a path parameter of 2,660 operations, all strings, under 16 distinct sets of constraints.Measured at spec @319dc756, morphic @b206dc99.
Closes #696
Closes #710
Closes #711
Closes #712
Closes #719
Test plan
go vet.Stacked on #1331 (itself on #1329), because all three edit
plan/policy.md,plan/types.md,refine/steps.md,docs-and-mock.mdand the end oftesting/mandatory-tests.md. Merge in order. After each lands, "Update branch" on the next is a merge ofmainthat conflicts nowhere.