Change work item widgets queries to use `workItem.features`
To simplify the frontend code and allow deprecation / removal of the `workItem.widgets` field, we should change the frontend queries to use the new `workItem.features` field.
To prevent N+1s on queries where we fetch multiple work items, we also need to change `WorkItems::LookAheadPreloads` to handle the new field.
## Tracking
```mermaid
gantt
title Epic 20609 - Change work item widgets queries to use workItem.features
dateFormat YYYY-MM-DD
section Epic
Epic 20609 Fixed Timeline :epic1, 2026-02-10, 2026-03-20
section Phase 1 Backend
Cache utils CLOSED :done, p1a, 2026-02-10, 2026-02-13
LookAheadPreloads CLOSED :done, p1b, 2026-02-10, 2026-02-20
section Phase 2 Query Implementation
List query (587964) :done, p2a, 2026-02-10, 2026-02-09
Get by ID query (587965) :active, p2b, 2026-02-10, 2026-02-26
section Phase 3 Create Cache
Create work item cache (587966) :crit, active, p3cache, 2026-02-24, 2026-02-28
section Phase 4 Widget Migration
Widget migration parent (587970) :crit, p4parent, 2026-03-03, 2026-03-10
Assignees widget :p4a, 2026-03-12, 1d
Award emoji widget :p4b, 2026-03-13, 1d
CRM contacts widget :p4e, 2026-03-16, 1d
Dates widget :p4f, 2026-03-17, 1d
Description widget :p4g, 2026-03-03, 1d
Error tracking widget :p4i, 2026-03-20, 1d
Labels widget :p4j, 2026-03-21, 2d
Linked resources :p4k, 2026-03-22, 2d
Milestone widget :p4l, 2026-03-10, 2d
Notifications widget :p4m, 2026-03-24, 1d
Parent widget :p4n, 2026-03-25, 2d
Time tracking widget :p4q, 2026-03-28, 1d
Title widget :p4r, 2026-03-03, 1d
Notes widget component :p4s, 2026-04-05, 5d
Detail component :p4u, 2026-03-03, 1d
Design management :p4w, 2026-03-25, 1d
Ancestors widget :p4x, 2026-03-20, 3d
Development widget :p4y, 2026-03-09, 1d
Links widget :p4z, 2026-03-30, 2d
Relationships widget :p4aa, 2026-04-02, 4d
Hierarchy widget :p4ab, 2026-04-02, 4d
section Phase 5 Notes Widget
Notes widget issue (587971) :p5, 2026-04-01, 2026-04-10
section Phase 6 Optimize
Optimize list query (589609) :p6, 2026-03-14, 1d
section Phase 7 Rollout
Feature flag rollout (587972) :milestone, p7, 2026-03-15, 2026-03-20
```
## Approach
We'll migrate progressively by querying both `widgets` and `features` together, gated behind a feature flag. This allows components to migrate widget-by-widget without breaking the cache or requiring all queries to change at once. Performance testing shows negligible overhead (within ms) when fetching both fields simultaneously.
The key insight is that each widget maps directly to a feature (assignees → features.assignees, milestone → features.milestone, etc.), so components can migrate incrementally. We gate the migration behind a feature flag to control rollout and provide a kill switch if issues arise.
## Requirements
- Add feature flag `work_item_features_field` with user actor only (no group actor needed)
- Update queries to conditionally fetch `features` field based on the flag (query both `widgets` and `features` when flag is enabled)
- Migrate components widget-by-widget to use `features` instead of `widgets`, gated by flag
- Update `WorkItems::LookAheadPreloads` to handle the new field
- Update cache utilities and resolvers to maintain both `widgets` and `features` during migration (handle create work item cache)
- In components, use conditional access: `data.features || data.widgets` to fallback gracefully
- Remove old code paths once confident in the new implementation
- Monitor performance during rollout, especially for complex queries like `workItemNotesByIid`
## Implementation Notes
- **No cache type policies needed**: By querying both fields with the FF, data transformers become unnecessary. Keep it simple and avoid masking errors deeper in cache.
- **Logged out users**: Keep on old path (widgets only) - this is a refactor, so no behavior change needed for unauthenticated users
- **Intermediate steps**: While migrating widget-by-widget, `features` will only work partially. All code accessing `features` must be gated behind the flag.
- **Performance considerations**: If performance degradation occurs during transition (especially for notes widget), we can iterate to remove widgets before release, but start with both for ease of development.
epic