Problem
TypeSpec.HttpClient.@experimental uses options.emitterScope to control which emitters receive its lifecycle metadata:
@experimental(#{ emitterScope: "@typespec/http-client-csharp" })
op test(): void;
This differs from the established client-generator decorator convention, which uses a top-level optional argument named scope:
extern dec clientName(
target: unknown,
rename: valueof string,
scope?: valueof string
);
@experimental and ClientDecoratorOptions.emitterScope were introduced together in #11094 with a package-specific scope parser.
Impact
TCGC's generic decorator handling automatically filters decorators by decoratorInfo.arguments.scope. It cannot recognize the nested decoratorInfo.arguments.options.emitterScope value used by @experimental.
As a result, emitters consuming @experimental through TCGC's generic decorator metadata must implement a second scope parser and filtering path. This creates duplicated behavior and makes the two scoping conventions easier to interpret differently over time.
Proposed compatibility path
Align @experimental with the standard top-level scope convention without immediately breaking existing TypeSpec:
- Add an optional top-level
scope argument to @experimental.
- Continue accepting
options.emitterScope as a deprecated compatibility alias.
- Use
scope when provided; otherwise fall back to options.emitterScope.
- Report a diagnostic if both values are supplied.
- Update examples and documentation to use
scope.
- Remove
emitterScope only in a future breaking release.
Possible shape:
@experimental(
#{ diagnosticId: "C", dependsOn: #["A", "B"] },
"@typespec/http-client-csharp"
)
op test(): void;
Acceptance criteria
Related work
Problem
TypeSpec.HttpClient.@experimentalusesoptions.emitterScopeto control which emitters receive its lifecycle metadata:This differs from the established client-generator decorator convention, which uses a top-level optional argument named
scope:@experimentalandClientDecoratorOptions.emitterScopewere introduced together in #11094 with a package-specific scope parser.Impact
TCGC's generic decorator handling automatically filters decorators by
decoratorInfo.arguments.scope. It cannot recognize the nesteddecoratorInfo.arguments.options.emitterScopevalue used by@experimental.As a result, emitters consuming
@experimentalthrough TCGC's generic decorator metadata must implement a second scope parser and filtering path. This creates duplicated behavior and makes the two scoping conventions easier to interpret differently over time.Proposed compatibility path
Align
@experimentalwith the standard top-levelscopeconvention without immediately breaking existing TypeSpec:scopeargument to@experimental.options.emitterScopeas a deprecated compatibility alias.scopewhen provided; otherwise fall back tooptions.emitterScope.scope.emitterScopeonly in a future breaking release.Possible shape:
Acceptance criteria
scopeconvention.options.emitterScopeusage continues to compile during the compatibility period.Related work