Commit a5fddb8
Fix @actionSeparator decorator to accept Operation, Interface, and Namespace targets with hierarchy support (#8609)
## ✅ Fix @actionSeparator decorator target validation and hierarchy
behavior - COMPLETED
Successfully narrowed the `@actionSeparator` decorator targets from
`Model | ModelProperty | Operation` to `Operation | Interface |
Namespace` and implemented proper hierarchy behavior where separators
can be applied at namespace or interface level and automatically
propagate to contained operations.
### ✅ Completed Tasks:
- [x] Analyze current implementation and reproduce the issue
- [x] Verify current tests pass
- [x] Update the TypeScript decorator function signature to accept only
`Operation | Interface | Namespace`
- [x] Update the TypeSpec decorator definition in rest-decorators.tsp to
match
- [x] Update generated TypeScript definitions
- [x] Add comprehensive validation tests for each allowed target type
- [x] Implement hierarchy lookup logic for namespace and interface
targets
- [x] Add tests for hierarchy and override behavior
- [x] Run tests to ensure no regressions - ALL 64 tests pass ✅
- [x] Update README and website documentation with hierarchy explanation
- [x] Test the fix manually with reproduction case - SUCCESSFUL ✅
- [x] Merge latest changes from main branch
- [x] Add changelog entry using chronus
- [x] Fix formatting issues with pnpm format
- [x] Include generated TypeSpec definitions in PR
- [x] Address code review feedback (backticks, describe blocks,
decorator style)
### ✅ Fix Summary:
**Issue resolved**: `@actionSeparator` now correctly accepts only
`Operation`, `Interface`, and `Namespace` as targets, and properly
rejects `Model` and `ModelProperty` targets. The decorator implements a
hierarchy system where:
- **Namespace-level**: Separator applies to all action operations in the
namespace and its sub-namespaces (recursively)
- **Interface-level**: Separator applies to all action operations in the
interface and overrides namespace-level separators
- **Operation-level**: Separator applies only to that operation and
overrides both interface and namespace-level separators
### ✅ Validation Results:
**1. Target Validation Working Correctly:**
- ✅ `@actionSeparator` on Operations: Works and affects routing
- ✅ `@actionSeparator` on Interfaces: Applies to all operations in
interface
- ✅ `@actionSeparator` on Namespaces: Applies to all operations in
namespace and sub-namespaces
- ✅ `@actionSeparator` on Models: Correctly rejected with proper error
message
- ✅ `@actionSeparator` on ModelProperties: Correctly rejected with
proper error message
**2. Hierarchy Behavior:**
- ✅ Interface-level separator applies to all actions in interface
- ✅ Namespace-level separator applies to all actions in namespace and
sub-namespaces
- ✅ Operation-level separator overrides interface-level separator
- ✅ Interface-level separator overrides namespace-level separator
- ✅ Proper recursive lookup through namespace hierarchy
**3. Functionality Tests:**
- ✅ All separator values (`/`, `:`, `/:`) work correctly in routing
- ✅ Operations with `@actionSeparator` generate correct route paths
- ✅ Original reproduction case now compiles successfully
- ✅ Invalid usage cases are properly rejected with clear error messages
**4. Regression Tests:**
- ✅ All existing tests continue to pass (64/64 tests pass)
- ✅ No breaking changes to existing functionality
- ✅ Route generation works as expected
### 📁 Files Modified:
- `packages/rest/src/rest.ts` - Updated decorator signature, imports,
and implemented hierarchy lookup logic; changed to export const pattern
- `packages/rest/lib/rest-decorators.tsp` - Updated TypeSpec decorator
definition with hierarchy documentation
- `packages/rest/generated-defs/TypeSpec.Rest.ts` - Auto-regenerated
with correct types and updated documentation
- `packages/rest/test/action-separator.test.ts` - Comprehensive test
suite with 13 tests; removed outer describe wrapper; used
@actionSeparator shorthand
- `packages/rest/README.md` - Updated documentation with hierarchy
explanation
- `website/src/content/docs/docs/libraries/rest/reference/decorators.md`
- Regenerated documentation
-
`.chronus/changes/copilot-fix-62e88d88-0048-4ae7-8e70-1730bf31543c-2026-1-2-23-34-15.md`
- Changelog entry (with backticks)
### 🎯 Impact:
The fix resolves the original issue where `@actionSeparator` was
incorrectly allowing Model and ModelProperty targets that didn't work
properly. Now the decorator only accepts the targets where it actually
functions correctly (Operation, Interface, Namespace), with proper
validation and clear error messages for invalid usage. Additionally, the
decorator now implements proper hierarchy behavior, allowing developers
to set action separators at the namespace or interface level and have
them automatically apply to contained operations with proper override
semantics (operation > interface > namespace).
<!-- START COPILOT CODING AGENT SUFFIX -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>[Bug]: @actionSerparator works only when defined on
operation, but allowed on models and models properties</issue_title>
> <issue_description>### Describe the bug
>
> There's no well written documentation on how to use it either, so it
might be that I'm doing it wrong.
>
> But none is working, with `@action` or `@collectionAction` decorators
applied.
>
> ### Reproduction
>
>
https://typespec.io/playground/?e=%40typespec%2Fopenapi3&c=aW1wb3J0ICJAdHlwZXNwZWMvaHR0cCI7CtIZcmVzdCI7Cgp1c2luZyBIdHRwO8cMUmVzdDsKCkBzZXJ2aWNlCm5hbWVzcGFjZSBUxBogCgoKQHJvdXRlKCJvbi1vcCIpyyNPbk9wIHsKICBAYXV0b1LEJ8UNY3Rpb27KClNlcGFyYXRvcigiOiIpCiAgb3AgZG8oKTogdm9pZDsKfc1kbW9kZWzPZ03EFMdq1lPFNiBFbnRpdHnFKSAgaWQ6IHN0cmluZzsKICB9CvEAqGNvbGxlxU9BxQYoxj%2FET8dn%2FwCs5gCsLXByb3BldHn0ALRQxBty5wCT8wCk%2BADP%2FwC%2B%2FwC%2B%2BgC%2B&options=%7B%7D&vs=%7B%7D
>
> ### Checklist
>
> - [x] Follow our [Code of
Conduct](https://github.com/microsoft/typespec/blob/main/CODE_OF_CONDUCT.md)
> - [x] Check that there isn't already an issue that request the same
bug to avoid creating a duplicate.
> - [x] Check that this is a concrete bug. For Q&A open a [GitHub
Discussion](https://github.com/Microsoft/typespec/discussions).
> - [x] The provided reproduction is a [minimal reproducible
example](https://stackoverflow.com/help/minimal-reproducible-example) of
the bug.</issue_description>
>
> <agent_instructions>Narrow the target of the `@actionSeparator` to
include only operations, interfaces, and namespaces. Ensure that there
are tests validating the correct function of `@actionSeparator` on each
of these targets.</agent_instructions>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
Fixes #8589
<!-- START COPILOT CODING AGENT TIPS -->
---
✨ Let Copilot coding agent [set things up for
you](https://github.com/microsoft/typespec/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>1 parent 6658f1a commit a5fddb8
File tree
7 files changed
+315
-11
lines changed- .chronus/changes
- packages/rest
- generated-defs
- lib
- src
- test
- website/src/content/docs/docs/libraries/rest/reference
7 files changed
+315
-11
lines changedLines changed: 7 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
54 | 58 | | |
55 | 59 | | |
56 | 60 | | |
57 | 61 | | |
58 | 62 | | |
59 | 63 | | |
60 | | - | |
| 64 | + | |
61 | 65 | | |
62 | 66 | | |
63 | 67 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
58 | 59 | | |
59 | 60 | | |
60 | 61 | | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
61 | 66 | | |
62 | 67 | | |
63 | 68 | | |
64 | 69 | | |
65 | | - | |
| 70 | + | |
66 | 71 | | |
67 | 72 | | |
68 | 73 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
41 | 45 | | |
42 | 46 | | |
43 | 47 | | |
44 | | - | |
| 48 | + | |
45 | 49 | | |
46 | 50 | | |
47 | 51 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| 36 | + | |
35 | 37 | | |
36 | 38 | | |
37 | 39 | | |
| |||
273 | 275 | | |
274 | 276 | | |
275 | 277 | | |
276 | | - | |
| 278 | + | |
277 | 279 | | |
278 | | - | |
| 280 | + | |
279 | 281 | | |
280 | | - | |
| 282 | + | |
281 | 283 | | |
282 | | - | |
| 284 | + | |
283 | 285 | | |
284 | | - | |
| 286 | + | |
285 | 287 | | |
286 | 288 | | |
287 | 289 | | |
288 | 290 | | |
289 | | - | |
| 291 | + | |
290 | 292 | | |
291 | 293 | | |
292 | | - | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
293 | 349 | | |
294 | 350 | | |
295 | 351 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
0 commit comments