|
| 1 | +# Endpoint → Deployment UI Migration Dev Plan |
| 2 | + |
| 3 | +## Spec Reference |
| 4 | + |
| 5 | +`.specs/FR-1368-endpoint-deployment-migration/spec.md` |
| 6 | + |
| 7 | +## Epic |
| 8 | + |
| 9 | +- **FR-1368** — Model deployment & Revision #1 ([Jira](https://lablup.atlassian.net/browse/FR-1368)) |
| 10 | +- **Spec Task**: FR-1416 — Transition from Serving page to Deployment page |
| 11 | + |
| 12 | +## Stories and Sub-tasks |
| 13 | + |
| 14 | +Six Stories map directly to the implementation phases declared in the spec (§구현 단계 계획 Phase 1-5 + Flow 7). |
| 15 | +Each Story forms one PR stack on Graphite; sub-tasks inside a Story are generally sequential. |
| 16 | + |
| 17 | +--- |
| 18 | + |
| 19 | +### Story 1: Deployment UI Phase 1 — Foundation — FR-2657 |
| 20 | + |
| 21 | +> Spec section: §Feature Flag, §URL 경로 변경 + Fallback 리디렉션, §i18n 키 추가 |
| 22 | +> PR Stack: `feat/endpoint-deployment-migration-foundation` |
| 23 | +
|
| 24 | +Foundation layer. Must land first because every downstream story consumes routes, menu keys, i18n keys, or the feature flag. |
| 25 | + |
| 26 | +#### 1.1 Add `model-deployment-extended-filter` feature flag (26.4.3+) — FR-2663 |
| 27 | + |
| 28 | +- **Parent Story**: FR-2657 |
| 29 | +- **Changed files**: `src/lib/backend.ai-client-esm.ts` |
| 30 | +- **Dependencies**: None |
| 31 | +- **Review complexity**: Low |
| 32 | + |
| 33 | +#### 1.2 Add routes for `/deployments` + `/admin-deployments` with legacy fallbacks — FR-2664 |
| 34 | + |
| 35 | +- **Parent Story**: FR-2657 |
| 36 | +- **Changed files**: `react/src/routes.tsx`, stub page files under `react/src/pages/` |
| 37 | +- **Dependencies**: None (parallel with 1.1, 1.3) |
| 38 | +- **Review complexity**: Medium |
| 39 | + |
| 40 | +#### 1.3 Update sidebar menu: Serving → Deployments — FR-2665 |
| 41 | + |
| 42 | +- **Parent Story**: FR-2657 |
| 43 | +- **Changed files**: `react/src/hooks/useWebUIMenuItems.tsx` |
| 44 | +- **Dependencies**: None (parallel with 1.1, 1.2) |
| 45 | +- **Review complexity**: Low |
| 46 | + |
| 47 | +#### 1.4 Add Deployment i18n keys (en.json) + propagate to 21 languages — FR-2666 |
| 48 | + |
| 49 | +- **Parent Story**: FR-2657 |
| 50 | +- **Changed files**: `resources/i18n/*.json` (22 files) |
| 51 | +- **Dependencies**: None (parallel with 1.1, 1.2, 1.3) |
| 52 | +- **Review complexity**: Low (mechanical translation tier, can batch together) |
| 53 | + |
| 54 | +--- |
| 55 | + |
| 56 | +### Story 2: Deployment UI Phase 2 — Shared status/owner components — FR-2658 |
| 57 | + |
| 58 | +> Spec section: §ReplicaStatusTag 컴포넌트, §Flow 1, §Flow 6 |
| 59 | +> PR Stack: `feat/endpoint-deployment-migration-shared-components` |
| 60 | +
|
| 61 | +Shared presentational components consumed by list and detail stories. |
| 62 | + |
| 63 | +#### 2.1 Add `ReplicaStatusTag` component + Storybook story — FR-2667 |
| 64 | + |
| 65 | +- **Parent Story**: FR-2658 |
| 66 | +- **Changed files**: `react/src/components/ReplicaStatusTag.tsx`, `react/src/components/ReplicaStatusTag.stories.tsx` |
| 67 | +- **Dependencies**: 1.4 (FR-2666) — needs tooltip i18n keys |
| 68 | +- **Review complexity**: Medium |
| 69 | + |
| 70 | +#### 2.2 Add `DeploymentStatusTag` component — FR-2668 |
| 71 | + |
| 72 | +- **Parent Story**: FR-2658 |
| 73 | +- **Changed files**: `react/src/components/DeploymentStatusTag.tsx` |
| 74 | +- **Dependencies**: 1.4 (FR-2666) — needs label i18n keys |
| 75 | +- **Review complexity**: Low |
| 76 | + |
| 77 | +#### 2.3 Add `DeploymentOwnerInfo` component — FR-2669 |
| 78 | + |
| 79 | +- **Parent Story**: FR-2658 |
| 80 | +- **Changed files**: `react/src/components/DeploymentOwnerInfo.tsx` |
| 81 | +- **Dependencies**: 1.4 (FR-2666) |
| 82 | +- **Review complexity**: Low-Medium |
| 83 | + |
| 84 | +--- |
| 85 | + |
| 86 | +### Story 3: Deployment UI Phase 3 — Deployment list pages (user + admin) — FR-2659 |
| 87 | + |
| 88 | +> Spec section: §Flow 1, §Flow 6 |
| 89 | +> PR Stack: `feat/endpoint-deployment-migration-list-pages` |
| 90 | +
|
| 91 | +#### 3.1 Add `DeploymentList` fragment-receiving table (server-side filter/sort/paginate) — FR-2670 |
| 92 | + |
| 93 | +- **Parent Story**: FR-2659 |
| 94 | +- **Changed files**: `react/src/components/DeploymentList.tsx` |
| 95 | +- **Dependencies**: 2.1 (FR-2667), 2.2 (FR-2668) |
| 96 | +- **Review complexity**: High (new pattern — server-side pagination with nuqs URL state, BAIGraphQLPropertyFilter wiring) |
| 97 | + |
| 98 | +#### 3.2 Add `DeploymentListPage` (user) — owns `myDeployments` query — FR-2671 |
| 99 | + |
| 100 | +- **Parent Story**: FR-2659 |
| 101 | +- **Changed files**: `react/src/pages/DeploymentListPage.tsx` |
| 102 | +- **Dependencies**: 3.1 (FR-2670), 1.2 (FR-2664) |
| 103 | +- **Review complexity**: Medium |
| 104 | + |
| 105 | +#### 3.3 Add `AdminDeploymentListPage` + admin-only filters/columns (26.4.3+) — FR-2672 |
| 106 | + |
| 107 | +- **Parent Story**: FR-2659 |
| 108 | +- **Changed files**: `react/src/pages/AdminDeploymentListPage.tsx`, extends `DeploymentList` admin mode |
| 109 | +- **Dependencies**: 3.1 (FR-2670), 2.3 (FR-2669), 1.2 (FR-2664) |
| 110 | +- **Review complexity**: Medium |
| 111 | + |
| 112 | +#### 3.4 Remove legacy `ServingPage` and `EndpointList` / `EndpointStatusTag` — FR-2673 |
| 113 | + |
| 114 | +- **Parent Story**: FR-2659 |
| 115 | +- **Changed files (delete)**: `react/src/pages/ServingPage.tsx`, `react/src/components/EndpointList.tsx`, `react/src/components/EndpointStatusTag.tsx` |
| 116 | +- **Dependencies**: 3.2 (FR-2671), 3.3 (FR-2672) — must wait until both new pages are live |
| 117 | +- **Review complexity**: Low (mechanical deletion) |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +### Story 4: Deployment UI Phase 4 — Deployment Launcher (create + edit) — FR-2660 |
| 122 | + |
| 123 | +> Spec section: §Flow 2, §Flow 4, §폼 필드 매핑 |
| 124 | +> PR Stack: `feat/endpoint-deployment-migration-launcher` |
| 125 | +
|
| 126 | +#### 4.1 Add `DeploymentLauncherPageContent` — multi-step form body — FR-2674 |
| 127 | + |
| 128 | +- **Parent Story**: FR-2660 |
| 129 | +- **Changed files**: `react/src/components/DeploymentLauncherPageContent.tsx` |
| 130 | +- **Dependencies**: 1.4 (FR-2666) transitively via Phase 1 |
| 131 | +- **Review complexity**: High (new multi-step form, nuqs URL sync, antd v6 Steps API) |
| 132 | + |
| 133 | +#### 4.2 Add `DeploymentLauncherPage` (create/edit entry) + GQL mutations — FR-2675 |
| 134 | + |
| 135 | +- **Parent Story**: FR-2660 |
| 136 | +- **Changed files**: `react/src/pages/DeploymentLauncherPage.tsx`; delete `react/src/pages/ServiceLauncherPage.tsx`, `react/src/components/ServiceLauncherPageContent.tsx` |
| 137 | +- **Dependencies**: 4.1 (FR-2674), 1.2 (FR-2664) |
| 138 | +- **Review complexity**: High |
| 139 | + |
| 140 | +--- |
| 141 | + |
| 142 | +### Story 5: Deployment UI Phase 5 — Detail page with tabs, Drawer, and Rollback — FR-2661 |
| 143 | + |
| 144 | +> Spec section: §Flow 3, §Flow 5 |
| 145 | +> PR Stack: `feat/endpoint-deployment-migration-detail-page` |
| 146 | +
|
| 147 | +#### 5.1 Add `DeploymentConfigurationSection` (Overview card + Edit button) — FR-2676 |
| 148 | + |
| 149 | +- **Parent Story**: FR-2661 |
| 150 | +- **Changed files**: `react/src/components/DeploymentConfigurationSection.tsx` |
| 151 | +- **Dependencies**: None internal; *related to* 4.2 (FR-2675) because the Edit button target must exist |
| 152 | +- **Review complexity**: Medium |
| 153 | + |
| 154 | +#### 5.2 Add `DeploymentReplicasTab` + Replica detail Drawer — FR-2677 |
| 155 | + |
| 156 | +- **Parent Story**: FR-2661 |
| 157 | +- **Changed files**: `react/src/components/DeploymentReplicasTab.tsx` |
| 158 | +- **Dependencies**: 2.1 (FR-2667) |
| 159 | +- **Review complexity**: High |
| 160 | + |
| 161 | +#### 5.3 Add `DeploymentRevisionHistoryTab` + Rollback flow — FR-2678 |
| 162 | + |
| 163 | +- **Parent Story**: FR-2661 |
| 164 | +- **Changed files**: `react/src/components/DeploymentRevisionHistoryTab.tsx` |
| 165 | +- **Dependencies**: None internal |
| 166 | +- **Review complexity**: Medium |
| 167 | + |
| 168 | +#### 5.4 Add `DeploymentAccessTokensTab` (list + create + delete) — FR-2679 |
| 169 | + |
| 170 | +- **Parent Story**: FR-2661 |
| 171 | +- **Changed files**: `react/src/components/DeploymentAccessTokensTab.tsx` |
| 172 | +- **Dependencies**: None internal |
| 173 | +- **Review complexity**: Medium |
| 174 | + |
| 175 | +#### 5.5 Add `DeploymentAutoScalingTab` (wrap existing `AutoScalingRuleList`) — FR-2680 |
| 176 | + |
| 177 | +- **Parent Story**: FR-2661 |
| 178 | +- **Changed files**: `react/src/components/DeploymentAutoScalingTab.tsx` |
| 179 | +- **Dependencies**: None internal |
| 180 | +- **Review complexity**: Low |
| 181 | + |
| 182 | +#### 5.6 Add `DeploymentDetailPage` (header + Overview + 4 tabs + URL tab sync) — FR-2681 |
| 183 | + |
| 184 | +- **Parent Story**: FR-2661 |
| 185 | +- **Changed files**: `react/src/pages/DeploymentDetailPage.tsx` |
| 186 | +- **Dependencies**: 5.1 (FR-2676), 5.2 (FR-2677), 5.3 (FR-2678), 5.4 (FR-2679), 5.5 (FR-2680), 2.2 (FR-2668), 1.2 (FR-2664) |
| 187 | +- **Review complexity**: High |
| 188 | + |
| 189 | +#### 5.7 Remove legacy `EndpointDetailPage` + related Endpoint components — FR-2682 |
| 190 | + |
| 191 | +- **Parent Story**: FR-2661 |
| 192 | +- **Changed files (delete)**: `react/src/pages/EndpointDetailPage.tsx`, `react/src/components/EndpointOwnerInfo.tsx`, `EndpointDiagnosticsSection.tsx`, `EndpointTokenGenerationModal.tsx` |
| 193 | +- **Dependencies**: 5.6 (FR-2681) |
| 194 | +- **Review complexity**: Low |
| 195 | + |
| 196 | +--- |
| 197 | + |
| 198 | +### Story 6: Flow 7 — Quick Deploy from model folder (`useDeploymentLauncher`) — FR-2662 |
| 199 | + |
| 200 | +> Spec section: §Flow 7 |
| 201 | +> PR Stack: `feat/endpoint-deployment-migration-quick-deploy` |
| 202 | +
|
| 203 | +#### 6.1 Add `useDeploymentLauncher` hook (GQL-based Quick Deploy) — FR-2683 |
| 204 | + |
| 205 | +- **Parent Story**: FR-2662 |
| 206 | +- **Changed files**: `react/src/hooks/useDeploymentLauncher.ts` |
| 207 | +- **Dependencies**: 1.1 (FR-2663) — needs feature flag for version gating |
| 208 | +- **Review complexity**: High |
| 209 | + |
| 210 | +#### 6.2 Migrate Deploy button sites to `[Deploy | ▼]` split button using `useDeploymentLauncher` — FR-2684 |
| 211 | + |
| 212 | +- **Parent Story**: FR-2662 |
| 213 | +- **Changed files**: Every call site of `useModelServiceLauncher` (hunt with `rg`), likely model card / model folder components |
| 214 | +- **Dependencies**: 6.1 (FR-2683), 4.2 (FR-2675) — launcher page must accept `?model=` pre-fill param, 1.2 (FR-2664) — new route |
| 215 | +- **Review complexity**: Medium |
| 216 | + |
| 217 | +--- |
| 218 | + |
| 219 | +## PR Stack Strategy |
| 220 | + |
| 221 | +Six PR stacks — one per Story — most of which can run in parallel once Story 1 lands. |
| 222 | + |
| 223 | +| Stack | Story | Sub-tasks (sequential within stack) | Runs after | |
| 224 | +|-------|-------|-------------------------------------|------------| |
| 225 | +| 1 | Foundation | 1.1 · 1.2 · 1.3 · 1.4 (can all be one PR if reviewer OK — mechanical tier) | — | |
| 226 | +| 2 | Shared Components | 2.1 · 2.2 · 2.3 | Stack 1 | |
| 227 | +| 3 | List Pages | 3.1 · 3.2 · 3.3 · 3.4 | Stack 2 | |
| 228 | +| 4 | Launcher | 4.1 · 4.2 | Stack 1 (parallel with Stack 2/3) | |
| 229 | +| 5 | Detail Page | 5.1 · 5.2 · 5.3 · 5.4 · 5.5 · 5.6 · 5.7 | Stack 2 | |
| 230 | +| 6 | Quick Deploy | 6.1 · 6.2 | Stack 1; 6.2 blocked by Stack 4 (4.2) | |
| 231 | + |
| 232 | +Within Stack 5, subtasks 5.1 – 5.5 can each be separate PRs or folded into 5.6 depending on reviewer preference; current decomposition keeps them as independent sub-tasks so reviewers can approve tab-by-tab. |
| 233 | + |
| 234 | +## Dependency Graph |
| 235 | + |
| 236 | +``` |
| 237 | + ┌────────────────────────────────────────────────┐ |
| 238 | + │ Story 1 — Foundation (FR-2657) │ |
| 239 | + │ FR-2663 (flag) · FR-2664 (routes) · │ |
| 240 | + │ FR-2665 (menu) · FR-2666 (i18n) │ |
| 241 | + └────────────┬──────────────┬────────────────────┘ |
| 242 | + │ │ |
| 243 | + ┌──────────┘ └──────────────────┐ |
| 244 | + ▼ ▼ |
| 245 | + ┌─────────────────────┐ ┌──────────────────────┐ |
| 246 | + │ Story 2 — Shared │ │ Story 6.1 (FR-2683) │ |
| 247 | + │ (FR-2667, 2668, │ │ useDeploymentLauncher│ |
| 248 | + │ 2669) │ └──────────┬───────────┘ |
| 249 | + └──────┬─────┬────────┘ │ |
| 250 | + │ │ │ |
| 251 | + ┌───────────┘ └────────────────────┐ │ |
| 252 | + ▼ ▼ │ |
| 253 | + ┌───────────────────────┐ ┌───────────────────────┐ │ |
| 254 | + │ Story 3 — List Pages │ │ Story 5 — Detail │ │ |
| 255 | + │ FR-2670 (list table) │ │ FR-2676 (Overview) │ │ |
| 256 | + │ │ │ │ FR-2677 (Replicas) │ │ |
| 257 | + │ ├──► FR-2671 (user) │ │ FR-2678 (History) │ │ |
| 258 | + │ └──► FR-2672 (admin) │ │ FR-2679 (Tokens) │ │ |
| 259 | + │ │ │ │ FR-2680 (AutoScaling) │ │ |
| 260 | + │ ▼ │ │ │ │ │ |
| 261 | + │ FR-2673 cleanup │ │ ▼ │ │ |
| 262 | + └───────────────────────┘ │ FR-2681 (page) │ │ |
| 263 | + │ │ │ │ |
| 264 | + │ ▼ │ │ |
| 265 | + │ FR-2682 cleanup │ │ |
| 266 | + └───────────────────────┘ │ |
| 267 | + │ |
| 268 | + ┌───────────────────────┐ │ |
| 269 | + │ Story 4 — Launcher │ │ |
| 270 | + │ FR-2674 (content) │ │ |
| 271 | + │ │ │ │ |
| 272 | + │ ▼ │ │ |
| 273 | + │ FR-2675 (page) ───────┼─────────┤ |
| 274 | + └───────────────────────┘ │ |
| 275 | + ▼ |
| 276 | + ┌───────────────────────┐ |
| 277 | + │ Story 6.2 (FR-2684) │ |
| 278 | + │ Split-button callers │ |
| 279 | + └───────────────────────┘ |
| 280 | +``` |
| 281 | + |
| 282 | +### Execution waves (for `/batch-implement`) |
| 283 | + |
| 284 | +Assuming all sub-tasks in a story can be stacked in one PR stack and waves represent sequential gating: |
| 285 | + |
| 286 | +- **Wave 1 (no blockers)**: FR-2663, FR-2664, FR-2665, FR-2666 — Foundation parallel batch. |
| 287 | +- **Wave 2**: FR-2667, FR-2668, FR-2669 (shared components) — parallel. FR-2674 (launcher content) — parallel with wave 2. FR-2683 (Quick Deploy hook) — parallel with wave 2. |
| 288 | +- **Wave 3**: FR-2670 (list table), FR-2675 (launcher page), FR-2676-2680 (detail sub-components). |
| 289 | +- **Wave 4**: FR-2671 (user list page), FR-2672 (admin list page), FR-2681 (detail page). |
| 290 | +- **Wave 5**: FR-2673 (list cleanup), FR-2682 (detail cleanup), FR-2684 (split-button migration). |
| 291 | + |
| 292 | +## Open Questions / Risks |
| 293 | + |
| 294 | +1. **Legacy `useModelServiceLauncher` during migration window**: Spec §Flow 7 says "keep old hook as fallback for <26.4.2 backends". Current plan treats 26.4.2+ as hard minimum (§개요). Need to confirm whether fallback is actually required, or if we can delete the old hook immediately after 6.2. **Defer to user confirmation in Story 6.** |
| 295 | +2. **Stub pages in Sub-task 1.2**: Creating temporary stub pages so the route PR is mergeable introduces a transient "blank page" state on `/deployments` until Phase 3 lands. If that's unacceptable, 1.2 should be merged together with 3.2 and 5.6 in a single mega-PR — but that violates "single focus per PR". Current plan favors merge-ability; reviewers should be aware. |
| 296 | +3. **Access Tokens tab (5.4) scope**: Spec says "reference old EndpointTokenGenerationModal logic, re-implement fresh". If the new GQL schema for `accessTokens` mutations is still in flux, this sub-task may need a spike first. **Flag for investigation before work starts.** |
| 297 | +4. **AutoScalingRuleList reuse (5.5)**: Spec §범위 외 says existing `AutoScalingRuleList` is already Deployment-API based. Quick verification pass needed to confirm — if any adjustment is required, 5.5 grows in scope. |
| 298 | +5. **DOMAIN/PROJECT/RESOURCE_GROUP sorters (Story 3.3)**: Available only on 26.4.3+. Confirm gating UI is acceptable (sort arrows disappear) vs. always-on (compat error). Spec leans toward feature-flag gate via `baiClient.supports('model-deployment-extended-filter')`. |
| 299 | + |
| 300 | +## Change Log |
| 301 | + |
| 302 | +- 2026-04-22: Initial dev plan generated from spec (post PR-review feedback). 22 sub-tasks across 6 stories created in Jira with dependency links. |
0 commit comments