Design doc: attribute constants, discriminator on refinements, enum refinements - #1643
Design doc: attribute constants, discriminator on refinements, enum refinements#1643lmolkova wants to merge 5 commits into
Conversation
Pull request dashboard statusWaiting on the author · refreshed 2026-08-08 18:42 UTC Respond to 1 review item (e.g. link a commit, explain why not, ask a follow-up):
Status above doesn't look right?
|
There was a problem hiding this comment.
Pull request overview
Adds a new Weaver design/spec document describing how to model attribute value constraints directly in schema definitions, instead of relying on repeated prose in semantic convention registries.
Changes:
- Introduces a proposal for pinning referenced attribute values via
constant(phase 1). - Proposes refinement identification via
discriminator(phase 2). - Proposes locally constraining/defining known values via
type.memberson references (phase 3).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1643 +/- ##
=====================================
Coverage 81.5% 81.5%
=====================================
Files 130 130
Lines 11616 11616
=====================================
Hits 9475 9475
Misses 2141 2141 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| ### Proposal: `type.members` on a reference (phase 3) | ||
|
|
||
| A reference may restate the attribute's members. Two kinds of entry cover every case in the table above: | ||
|
|
||
| - `- ref: <member>` selects a member the definition already has. It inherits the member's properties and may | ||
| update the ones a reference is allowed to update. | ||
| - `- id: <value>` adds a member the definition does not have, and must carry `brief` and `stability` like any | ||
| other public definition. | ||
|
|
||
| A reference may do this even when the definition is a plain string — it refines the string into a locally | ||
| documented enum. That is safe because a string enum carries string values: the wire type never changes, only the | ||
| documentation and what code generation can emit. It is also the honest model for `messaging.operation.name`, | ||
| which is a string globally because no global vocabulary exists, and a small known set everywhere it is used: | ||
|
|
||
| ```yaml | ||
| attributes: | ||
| - key: messaging.operation.name | ||
| type: string | ||
| stability: development | ||
| brief: The system-specific name of the messaging operation. | ||
|
|
||
| span_refinements: | ||
| - id: azure.servicebus.producer.create | ||
| ref: messaging.producer.create | ||
| attributes: | ||
| - ref: messaging.operation.name | ||
| brief: Azure Service Bus operation name. | ||
| type: | ||
| members: | ||
| - id: send | ||
| brief: Sends a message to a queue or topic. | ||
| stability: development | ||
| - id: schedule | ||
| brief: Schedules a message for future delivery. | ||
| stability: development | ||
| ``` | ||
|
|
||
| When the values already exist in the definition, the entries carry nothing but refs — here the GenAI inference | ||
| span narrows the definition to a subset of its members: | ||
|
|
||
| ```yaml | ||
| attributes: | ||
| - key: gen_ai.operation.name | ||
| stability: development | ||
| brief: The name of the operation being performed. | ||
| type: | ||
| members: | ||
| - id: chat | ||
| value: chat | ||
| brief: Chat completion operation such as OpenAI Chat API. | ||
| stability: development | ||
| - id: embeddings | ||
| value: embeddings | ||
| brief: Embeddings operation such as OpenAI Create embeddings API. | ||
| stability: development | ||
| # ... 14 more | ||
|
|
||
| spans: | ||
| - type: gen_ai.inference.client | ||
| kind: client | ||
| attributes: | ||
| - ref: gen_ai.operation.name | ||
| type: | ||
| members: | ||
| - ref: chat | ||
| - ref: generate_content | ||
| - ref: text_completion | ||
| ``` | ||
|
|
||
| `error.type` needs the same field for the opposite reason — none of its values exist in the definition. Here is | ||
| the .NET socket span from the introduction, with each code described instead of listed in prose: | ||
|
|
||
| ```yaml | ||
| attributes: | ||
| - key: error.type | ||
| stability: stable | ||
| brief: Describes a class of error the operation ended with. | ||
| type: | ||
| members: | ||
| - id: other | ||
| value: _OTHER | ||
| brief: A fallback error value used when the instrumentation has no custom value. | ||
| stability: stable | ||
|
|
||
| spans: | ||
| - type: socket.connect | ||
| attributes: | ||
| - ref: error.type | ||
| brief: Socket error code. | ||
| type: | ||
| members: | ||
| - ref: other | ||
| - id: network_down | ||
| brief: The network subsystem is unavailable. | ||
| stability: development | ||
| - id: connection_refused | ||
| brief: The remote host actively refused the connection. | ||
| stability: development | ||
| # ... 14 more | ||
| ``` | ||
|
|
||
| There is no "exclude these members" form: `type.members` on a reference replaces the members of the definition. | ||
| A new member added to `gen_ai.operation.name` must **not** silently become part of a refinement's set — someone | ||
| has to decide which span types it belongs to. |
There was a problem hiding this comment.
It would be good to understand how hw.state would be modelled using this design. As That is probs one of the most complex.
There was a problem hiding this comment.
like this
spans:
- type: gen_ai.inference.client
kind: client
discriminator: [gen_ai.provider.name]
# ...
span_refinements:
- id: openai.inference.client
ref: gen_ai.inference.client
attributes:
- ref: gen_ai.provider.name
constant: openai # can be used independently of `discriminator` on base span definition
| - **Turning enums into standalone named types** shared by several attributes. Unrelated to this proposal; the | ||
| fields above work either way. |
There was a problem hiding this comment.
Would standalone types not change the solution for 3?
For instance we could just overwrite the type on the ref to a new type which has the same value type. That new type can either be a refinement (additional members) or a new type.
Related issues #1617, #803, #479, #520, #1590, #892, #878, #1146, #1623, #329
Examples:
Pinning attribute value to constant when referencing attribute and discriminators
Specifying a set of known values on a enum
Specifying a set of known values on a string attribute