HotChocolate Specialist
Deep HotChocolate v15 API expertise. Complements the backend-developer skill (project-specific patterns) with framework-level knowledge and self-learning from official documentation.
Scope boundary: Project-specific patterns (ObjectType layout, TypeExtension conventions, DataLoader naming, middleware testing, MassTransit, MongoDB) live in the backend-developer skill. This skill covers HotChocolate framework APIs, advanced features, and official best practices.
Self-Learning Workflow
Before answering any non-trivial HotChocolate question, fetch the latest official documentation to ensure accuracy. HotChocolate evolves rapidly — never rely solely on training data.
Step 1: Identify the Topic
Map the user's question to one or more documentation areas from the Documentation Index.
Step 2: Fetch Latest Documentation
Use fetch_webpage to retrieve the relevant page(s):
Primary: https://chillicream.com/docs/hotchocolate/v15/{topic-path}
Fallback: https://github.com/ChilliCream/graphql-platform/blob/main/website/src/docs/hotchocolate/v15/{topic-path}/index.md
For bleeding-edge features or source code questions, search the GitHub repository:
https://github.com/ChilliCream/graphql-platform/tree/main/src/HotChocolate/{module}
Step 3: Cross-Reference with Project Patterns
After fetching official docs, verify alignment with project conventions from backend-developer skill and general.instructions.md. Prefer project conventions when they intentionally diverge from defaults.
Step 4: Apply and Cite
Implement using the latest API. Always cite the documentation URL when introducing patterns the user may not have seen before.
Topic Quick Reference
| Area |
Key APIs / Attributes |
Doc Path |
| Schema Design |
ObjectType<T>, InterfaceType<T>, UnionType, EnumType, InputObjectType<T>, DirectiveType<T> |
defining-a-schema/ |
| Scalars |
Built-in + custom ScalarType<TRuntimeType, TLiteral> |
defining-a-schema/scalars |
| Enums |
EnumType<T>, [GraphQLName], [GraphQLDescription] |
defining-a-schema/enums |
| Interfaces |
InterfaceType<T>, [InterfaceObject], implements |
defining-a-schema/interfaces |
| Unions |
UnionType, UnionType<T>, annotation-based |
defining-a-schema/unions |
| Directives |
DirectiveType<T>, [Directive], executable/type-system |
defining-a-schema/directives |
| Relay |
[Node], [ID], Global Object Identification, INodeResolver |
defining-a-schema/relay |
| OneOf |
[OneOf] input types |
defining-a-schema/input-object-types |
| Dynamic Schemas |
Runtime type building, ITypeModule |
defining-a-schema/dynamic-schemas |
| Versioning |
@deprecated, schema evolution strategies |
defining-a-schema/versioning |
| Resolvers |
Pure resolvers, resolver pipeline, [Parent], [Service], [ScopedService] |
fetching-data/resolvers |
| DataLoader |
BatchDataLoader, GroupedDataLoader, CacheDataLoader, source-generated |
fetching-data/dataloader |
| Pagination |
[UsePaging], [UseOffsetPaging], cursor vs offset vs keyset |
fetching-data/pagination |
| Filtering |
[UseFiltering], IFilterConvention, custom filter fields |
fetching-data/filtering |
| Sorting |
[UseSorting], ISortConvention, custom sort fields |
fetching-data/sorting |
| Projections |
[UseProjection], IProjectionConvention, first-class MongoDB support |
fetching-data/projections |
| Subscriptions |
[Subscribe], [Topic], in-memory, Redis provider |
defining-a-schema/subscriptions |
| Mutations |
Mutation conventions, MutationConventionOptions, [Error], [UseMutationConvention] |
defining-a-schema/mutations |
| Error Handling |
IErrorFilter, mutation error conventions, IError, ErrorBuilder |
defining-a-schema/mutations |
| Server Config |
Endpoints, warmup, command-line |
server/ |
| Interceptors |
IHttpRequestInterceptor, ISocketSessionInterceptor |
server/interceptors |
| DI |
[Service], RegisterService, scoped services in resolvers |
server/dependency-injection |
| Global State |
IResolverContext.ContextData, SetGlobalState, GetGlobalState |
server/global-state |
| Introspection |
Enable/disable, AllowIntrospection |
server/introspection |
| File Upload |
IFile, Upload scalar |
server/files |
| Batching |
Request batching, variable batching |
server/batching |
| Instrumentation |
IExecutionDiagnosticEvents, OpenTelemetry integration |
server/instrumentation |
| Auth |
[Authorize], policies, roles, @authorize directive |
security/ |
| Persisted Ops |
Automatic Persisted Operations (APQ), file/blob/Redis stores |
performance/ |
| Cost Analysis |
Query cost/complexity limits, [Cost] |
Related to CostAnalysis module |
| Source Generators |
[Module], AddTypes(), automatic type registration |
defining-a-schema/#automatic-type-registration |
| Migration |
v13→v14→v15 breaking changes and migration paths |
migrating/ |
Advanced Patterns
Custom Scalars
public class UrlType : ScalarType<Uri, StringValueNode>
{
public UrlType() : base("Url") { }
protected override Uri ParseLiteral(StringValueNode valueSyntax)
=> new(valueSyntax.Value);
protected override StringValueNode ParseValue(Uri runtimeValue)
=> new(runtimeValue.AbsoluteUri);
public override IValueNode ParseResult(object? resultValue)
=> ParseValue((Uri)resultValue!);
}
Register: .AddType<UrlType>()
Filtering — Custom Convention
public class CustomFilterConvention : FilterConvention
{
protected override void Configure(IFilterConventionDescriptor descriptor)
{
descriptor.AddDefaults();
descriptor.Provider(new QueryableFilterProvider(p =>
p.AddDefaultFieldHandlers()));
}
}
// Registration
services.AddGraphQLServer()
.AddFiltering<CustomFilterConvention>();
Sorting — Custom Convention
public class CustomSortConvention : SortConvention
{
protected override void Configure(ISortConventionDescriptor descriptor)
{
descriptor.AddDefaults();
descriptor.Provider(new QueryableSortProvider(p =>
p.AddDefaultFieldHandlers()));
}
}
Pagination — Cursor with MongoDB
[UsePaging(IncludeTotalCount = true, MaxPageSize = 100, DefaultPageSize = 25)]
[UseProjection]
[UseSorting]
[UseFiltering]
public IExecutable<MyEntity> GetEntities([Service] IMongoCollection<MyEntity> collection)
=> collection.AsExecutable();
Order matters: [UsePaging] → [UseProjection] → [UseSorting] → [UseFiltering] (outermost to innermost).
Subscriptions
// In mutation resolver
[UseMutationConvention]
public async Task<MyEntity> CreateEntity(
CreateEntityInput input,
[Service] ITopicEventSender sender,
CancellationToken ct)
{
MyEntity entity = /* create */;
await sender.SendAsync(nameof(OnEntityCreated), entity, ct);
return entity;
}
// Subscription
[Subscribe]
[Topic(nameof(OnEntityCreated))]
public MyEntity OnEntityCreated([EventMessage] MyEntity entity) => entity;
Error Handling — Mutation Conventions
// Define domain errors as classes
public class EntityNotFoundError
{
public EntityNotFoundError(string id) => Id = id;
public string Id { get; }
public string Message => $"Entity '{Id}' not found.";
}
// Annotate mutation with expected errors
[Error<EntityNotFoundError>]
[Error<ValidationError>]
[UseMutationConvention]
public async Task<MyEntity> UpdateEntity(UpdateEntityInput input, ...)
{
// throw or return error types
}
Authorization — Field-Level
[Authorize(Policy = "AdminOnly")]
public class AdminQueries
{
[Authorize(Roles = ["superadmin"])]
public async Task<SensitiveData> GetSensitiveData(...)
=> /* ... */;
}
Interceptors
public class CustomHttpRequestInterceptor : DefaultHttpRequestInterceptor
{
public override ValueTask OnCreateAsync(
HttpContext context,
IRequestExecutor requestExecutor,
OperationRequestBuilder requestBuilder,
CancellationToken ct)
{
// Add custom context data from HTTP headers
string? tenantId = context.Request.Headers["X-Tenant-Id"];
requestBuilder.SetGlobalState("TenantId", tenantId);
return base.OnCreateAsync(context, requestExecutor, requestBuilder, ct);
}
}
// Registration
services.AddGraphQLServer()
.AddHttpRequestInterceptor<CustomHttpRequestInterceptor>();
Source Generators & Registration
HotChocolate 12.7+ source generator auto-discovers types decorated with [QueryType], [MutationType], [SubscriptionType], [ObjectType], [ExtendObjectType], etc.
// ModuleInfo.cs — triggers source generation
[assembly: Module("MyDomain")]
// Startup — register all discovered types
services.AddGraphQLServer()
.AddTypes(); // Registers all source-generated types from the module
Persisted Operations
services.AddGraphQLServer()
.UseAutomaticPersistedOperationPipeline()
.AddFileSystemOperationDocumentStorage("./persisted-operations");
Alternative stores: Redis (AddRedisOperationDocumentStorage), Azure Blob Storage.
Troubleshooting Checklist
| Symptom |
Likely Cause |
Action |
Type 'X' is not registered |
Missing AddTypes() or manual registration |
Check [Module] attribute + startup config |
| Filtering/sorting not applied |
Missing [UseFiltering]/[UseSorting] or wrong attribute order |
Verify attribute stacking order |
| N+1 queries in resolver |
Missing DataLoader |
Implement BatchDataLoader per backend-developer conventions |
| Subscription not firing |
ITopicEventSender not called or topic mismatch |
Verify SendAsync topic matches [Topic] |
Auth returning AUTH_NOT_AUTHENTICATED |
Missing AddAuthorization() or JWT config |
Verify auth pipeline in startup |
Pagination returns no totalCount |
IncludeTotalCount = false (default) |
Set [UsePaging(IncludeTotalCount = true)] |
| Mutation error not in union |
Missing [Error<T>] attribute |
Add error type annotation to mutation |
| Schema export changed unexpectedly |
Source generator picked up new types |
Review [Module] scope and type visibility |
Anti-Patterns
| Anti-Pattern |
Why It's Wrong |
Fix |
| Filtering/sorting on non-indexed fields |
Kills DB performance at scale |
Ensure MongoDB indexes match filterable/sortable fields |
[UseProjection] without DB support |
Projection only works with supported providers |
Verify MongoDB/EF Core provider integration |
| Manual pagination implementation |
Reinvents cursor/offset logic HC handles |
Use [UsePaging] or [UseOffsetPaging] |
| Catching exceptions in resolvers to return errors |
Bypasses HC error pipeline |
Use IErrorFilter or mutation error conventions |
Hardcoded MaxPageSize > 500 |
Opens DoS vector via large queries |
Keep MaxPageSize ≤ 100 in production |
| Introspection enabled in production |
Exposes full schema to attackers |
Disable via AllowIntrospection.Never or per-policy |
Ignoring [Authorize] on mutations |
All mutations default to anonymous |
Add authorization explicitly |
Using DateTime scalar for UTC timestamps |
Ambiguous timezone semantics |
Use DateTimeOffset or custom UTC scalar |
Important Rules
- Always fetch latest documentation before implementing advanced HC features
- Attribute stacking order matters:
[UsePaging] → [UseProjection] → [UseSorting] → [UseFiltering]
- Never duplicate patterns already defined in
backend-developer skill — reference it instead
- Cite documentation URLs when introducing new patterns to the team
- Prefer implementation-first (annotation-based) over schema-first approach
- For project-specific conventions (naming, testing), defer to
backend-developer skill and tests.instructions.md
1---2name: hotchocolate-specialist3description: Deep HotChocolate v15 GraphQL server expertise — schema design, resolvers, filtering/sorting/projections, pagination, subscriptions, error handling, authorization, performance, persisted operations, source generators, and migration. Self-learns from official docs. Triggers on: HotChocolate, GraphQL filtering/sorting/projections, pagination, subscriptions, mutation conventions, persisted queries, cost analysis, custom scalars, GraphQL authorization, interceptors, source generator, AddTypes, HotChocolate migration, Relay node, UseProjection, UsePaging, UseFiltering, UseSorting.4---5
6# HotChocolate Specialist
7
8Deep HotChocolate v15 API expertise. Complements the `backend-developer` skill (project-specific patterns) with framework-level knowledge and self-learning from official documentation.
9
10> **Scope boundary**: Project-specific patterns (ObjectType layout, TypeExtension conventions, DataLoader naming, middleware testing, MassTransit, MongoDB) live in the `backend-developer` skill. This skill covers HotChocolate framework APIs, advanced features, and official best practices.
11
12## Self-Learning Workflow
13
14**Before answering any non-trivial HotChocolate question**, fetch the latest official documentation to ensure accuracy. HotChocolate evolves rapidly — never rely solely on training data.
15
16### Step 1: Identify the Topic
17
18Map the user's question to one or more documentation areas from the [Documentation Index](references/doc-index.md).
19
20### Step 2: Fetch Latest Documentation
21
22Use `fetch_webpage` to retrieve the relevant page(s):
23
24```
25Primary: https://chillicream.com/docs/hotchocolate/v15/{topic-path}
26Fallback: https://github.com/ChilliCream/graphql-platform/blob/main/website/src/docs/hotchocolate/v15/{topic-path}/index.md
27```
28
29For bleeding-edge features or source code questions, search the GitHub repository:
30```
31https://github.com/ChilliCream/graphql-platform/tree/main/src/HotChocolate/{module}
32```
33
34### Step 3: Cross-Reference with Project Patterns
35
36After fetching official docs, verify alignment with project conventions from `backend-developer` skill and `general.instructions.md`. Prefer project conventions when they intentionally diverge from defaults.
37
38### Step 4: Apply and Cite
39
40Implement using the latest API. Always cite the documentation URL when introducing patterns the user may not have seen before.
41
42## Topic Quick Reference
43
44| Area | Key APIs / Attributes | Doc Path |
45|---|---|---|
46| **Schema Design** | `ObjectType<T>`, `InterfaceType<T>`, `UnionType`, `EnumType`, `InputObjectType<T>`, `DirectiveType<T>` | `defining-a-schema/` |
47| **Scalars** | Built-in + custom `ScalarType<TRuntimeType, TLiteral>` | `defining-a-schema/scalars` |
48| **Enums** | `EnumType<T>`, `[GraphQLName]`, `[GraphQLDescription]` | `defining-a-schema/enums` |
49| **Interfaces** | `InterfaceType<T>`, `[InterfaceObject]`, `implements` | `defining-a-schema/interfaces` |
50| **Unions** | `UnionType`, `UnionType<T>`, annotation-based | `defining-a-schema/unions` |
51| **Directives** | `DirectiveType<T>`, `[Directive]`, executable/type-system | `defining-a-schema/directives` |
52| **Relay** | `[Node]`, `[ID]`, Global Object Identification, `INodeResolver` | `defining-a-schema/relay` |
53| **OneOf** | `[OneOf]` input types | `defining-a-schema/input-object-types` |
54| **Dynamic Schemas** | Runtime type building, `ITypeModule` | `defining-a-schema/dynamic-schemas` |
55| **Versioning** | `@deprecated`, schema evolution strategies | `defining-a-schema/versioning` |
56| **Resolvers** | Pure resolvers, resolver pipeline, `[Parent]`, `[Service]`, `[ScopedService]` | `fetching-data/resolvers` |
57| **DataLoader** | `BatchDataLoader`, `GroupedDataLoader`, `CacheDataLoader`, source-generated | `fetching-data/dataloader` |
58| **Pagination** | `[UsePaging]`, `[UseOffsetPaging]`, cursor vs offset vs keyset | `fetching-data/pagination` |
59| **Filtering** | `[UseFiltering]`, `IFilterConvention`, custom filter fields | `fetching-data/filtering` |
60| **Sorting** | `[UseSorting]`, `ISortConvention`, custom sort fields | `fetching-data/sorting` |
61| **Projections** | `[UseProjection]`, `IProjectionConvention`, first-class MongoDB support | `fetching-data/projections` |
62| **Subscriptions** | `[Subscribe]`, `[Topic]`, in-memory, Redis provider | `defining-a-schema/subscriptions` |
63| **Mutations** | Mutation conventions, `MutationConventionOptions`, `[Error]`, `[UseMutationConvention]` | `defining-a-schema/mutations` |
64| **Error Handling** | `IErrorFilter`, mutation error conventions, `IError`, `ErrorBuilder` | `defining-a-schema/mutations` |
65| **Server Config** | Endpoints, warmup, command-line | `server/` |
66| **Interceptors** | `IHttpRequestInterceptor`, `ISocketSessionInterceptor` | `server/interceptors` |
67| **DI** | `[Service]`, `RegisterService`, scoped services in resolvers | `server/dependency-injection` |
68| **Global State** | `IResolverContext.ContextData`, `SetGlobalState`, `GetGlobalState` | `server/global-state` |
69| **Introspection** | Enable/disable, `AllowIntrospection` | `server/introspection` |
70| **File Upload** | `IFile`, `Upload` scalar | `server/files` |
71| **Batching** | Request batching, variable batching | `server/batching` |
72| **Instrumentation** | `IExecutionDiagnosticEvents`, OpenTelemetry integration | `server/instrumentation` |
73| **Auth** | `[Authorize]`, policies, roles, `@authorize` directive | `security/` |
74| **Persisted Ops** | Automatic Persisted Operations (APQ), file/blob/Redis stores | `performance/` |
75| **Cost Analysis** | Query cost/complexity limits, `[Cost]` | Related to CostAnalysis module |
76| **Source Generators** | `[Module]`, `AddTypes()`, automatic type registration | `defining-a-schema/#automatic-type-registration` |
77| **Migration** | v13→v14→v15 breaking changes and migration paths | `migrating/` |
78
79## Advanced Patterns
80
81### Custom Scalars
82
83```csharp
84public class UrlType : ScalarType<Uri, StringValueNode>
85{
86 public UrlType() : base("Url") { }
87
88 protected override Uri ParseLiteral(StringValueNode valueSyntax)
89 => new(valueSyntax.Value);
90
91 protected override StringValueNode ParseValue(Uri runtimeValue)
92 => new(runtimeValue.AbsoluteUri);
93
94 public override IValueNode ParseResult(object? resultValue)
95 => ParseValue((Uri)resultValue!);
96}
97```
98
99Register: `.AddType<UrlType>()`
100
101### Filtering — Custom Convention
102
103```csharp
104public class CustomFilterConvention : FilterConvention
105{
106 protected override void Configure(IFilterConventionDescriptor descriptor)
107 {
108 descriptor.AddDefaults();
109 descriptor.Provider(new QueryableFilterProvider(p =>
110 p.AddDefaultFieldHandlers()));
111 }
112}
113
114// Registration
115services.AddGraphQLServer()
116 .AddFiltering<CustomFilterConvention>();
117```
118
119### Sorting — Custom Convention
120
121```csharp
122public class CustomSortConvention : SortConvention
123{
124 protected override void Configure(ISortConventionDescriptor descriptor)
125 {
126 descriptor.AddDefaults();
127 descriptor.Provider(new QueryableSortProvider(p =>
128 p.AddDefaultFieldHandlers()));
129 }
130}
131```
132
133### Pagination — Cursor with MongoDB
134
135```csharp
136[UsePaging(IncludeTotalCount = true, MaxPageSize = 100, DefaultPageSize = 25)]
137[UseProjection]
138[UseSorting]
139[UseFiltering]
140public IExecutable<MyEntity> GetEntities([Service] IMongoCollection<MyEntity> collection)
141 => collection.AsExecutable();
142```
143
144Order matters: `[UsePaging]` → `[UseProjection]` → `[UseSorting]` → `[UseFiltering]` (outermost to innermost).
145
146### Subscriptions
147
148```csharp
149// In mutation resolver
150[UseMutationConvention]
151public async Task<MyEntity> CreateEntity(
152 CreateEntityInput input,
153 [Service] ITopicEventSender sender,
154 CancellationToken ct)
155{
156 MyEntity entity = /* create */;
157 await sender.SendAsync(nameof(OnEntityCreated), entity, ct);
158 return entity;
159}
160
161// Subscription
162[Subscribe]
163[Topic(nameof(OnEntityCreated))]
164public MyEntity OnEntityCreated([EventMessage] MyEntity entity) => entity;
165```
166
167### Error Handling — Mutation Conventions
168
169```csharp
170// Define domain errors as classes
171public class EntityNotFoundError
172{
173 public EntityNotFoundError(string id) => Id = id;
174 public string Id { get; }
175 public string Message => $"Entity '{Id}' not found.";
176}
177
178// Annotate mutation with expected errors
179[Error<EntityNotFoundError>]
180[Error<ValidationError>]
181[UseMutationConvention]
182public async Task<MyEntity> UpdateEntity(UpdateEntityInput input, ...)
183{
184 // throw or return error types
185}
186```
187
188### Authorization — Field-Level
189
190```csharp
191[Authorize(Policy = "AdminOnly")]
192public class AdminQueries
193{
194 [Authorize(Roles = ["superadmin"])]
195 public async Task<SensitiveData> GetSensitiveData(...)
196 => /* ... */;
197}
198```
199
200### Interceptors
201
202```csharp
203public class CustomHttpRequestInterceptor : DefaultHttpRequestInterceptor
204{
205 public override ValueTask OnCreateAsync(
206 HttpContext context,
207 IRequestExecutor requestExecutor,
208 OperationRequestBuilder requestBuilder,
209 CancellationToken ct)
210 {
211 // Add custom context data from HTTP headers
212 string? tenantId = context.Request.Headers["X-Tenant-Id"];
213 requestBuilder.SetGlobalState("TenantId", tenantId);
214 return base.OnCreateAsync(context, requestExecutor, requestBuilder, ct);
215 }
216}
217
218// Registration
219services.AddGraphQLServer()
220 .AddHttpRequestInterceptor<CustomHttpRequestInterceptor>();
221```
222
223### Source Generators & Registration
224
225HotChocolate 12.7+ source generator auto-discovers types decorated with `[QueryType]`, `[MutationType]`, `[SubscriptionType]`, `[ObjectType]`, `[ExtendObjectType]`, etc.
226
227```csharp
228// ModuleInfo.cs — triggers source generation
229[assembly: Module("MyDomain")]
230
231// Startup — register all discovered types
232services.AddGraphQLServer()
233 .AddTypes(); // Registers all source-generated types from the module
234```
235
236### Persisted Operations
237
238```csharp
239services.AddGraphQLServer()
240 .UseAutomaticPersistedOperationPipeline()
241 .AddFileSystemOperationDocumentStorage("./persisted-operations");
242```
243
244Alternative stores: Redis (`AddRedisOperationDocumentStorage`), Azure Blob Storage.
245
246## Troubleshooting Checklist
247
248| Symptom | Likely Cause | Action |
249|---|---|---|
250| `Type 'X' is not registered` | Missing `AddTypes()` or manual registration | Check `[Module]` attribute + startup config |
251| Filtering/sorting not applied | Missing `[UseFiltering]`/`[UseSorting]` or wrong attribute order | Verify attribute stacking order |
252| N+1 queries in resolver | Missing DataLoader | Implement `BatchDataLoader` per `backend-developer` conventions |
253| Subscription not firing | `ITopicEventSender` not called or topic mismatch | Verify `SendAsync` topic matches `[Topic]` |
254| Auth returning `AUTH_NOT_AUTHENTICATED` | Missing `AddAuthorization()` or JWT config | Verify auth pipeline in startup |
255| Pagination returns no `totalCount` | `IncludeTotalCount = false` (default) | Set `[UsePaging(IncludeTotalCount = true)]` |
256| Mutation error not in union | Missing `[Error<T>]` attribute | Add error type annotation to mutation |
257| Schema export changed unexpectedly | Source generator picked up new types | Review `[Module]` scope and type visibility |
258
259## Anti-Patterns
260
261| Anti-Pattern | Why It's Wrong | Fix |
262|---|---|---|
263| Filtering/sorting on non-indexed fields | Kills DB performance at scale | Ensure MongoDB indexes match filterable/sortable fields |
264| `[UseProjection]` without DB support | Projection only works with supported providers | Verify MongoDB/EF Core provider integration |
265| Manual pagination implementation | Reinvents cursor/offset logic HC handles | Use `[UsePaging]` or `[UseOffsetPaging]` |
266| Catching exceptions in resolvers to return errors | Bypasses HC error pipeline | Use `IErrorFilter` or mutation error conventions |
267| Hardcoded `MaxPageSize` > 500 | Opens DoS vector via large queries | Keep `MaxPageSize` ≤ 100 in production |
268| Introspection enabled in production | Exposes full schema to attackers | Disable via `AllowIntrospection.Never` or per-policy |
269| Ignoring `[Authorize]` on mutations | All mutations default to anonymous | Add authorization explicitly |
270| Using `DateTime` scalar for UTC timestamps | Ambiguous timezone semantics | Use `DateTimeOffset` or custom UTC scalar |
271
272## Important Rules
273
274- Always fetch latest documentation before implementing advanced HC features
275- Attribute stacking order matters: `[UsePaging]` → `[UseProjection]` → `[UseSorting]` → `[UseFiltering]`
276- Never duplicate patterns already defined in `backend-developer` skill — reference it instead
277- Cite documentation URLs when introducing new patterns to the team
278- Prefer implementation-first (annotation-based) over schema-first approach
279- For project-specific conventions (naming, testing), defer to `backend-developer` skill and `tests.instructions.md`