Files
manage/.pi-tmp/svc-hub-slice6-out.md
T
Developer a8dfbd5dc6 Cleanup: delete dead top-level pages + update docs (Slice 11)
Delete the old top-level page files whose content was migrated into
service-page tabs in slices 5-9:
- pages/Media.tsx, Applications.tsx (-> MediaTab)
- pages/FileBrowser.tsx, FileBrowser.impl.tsx (-> FilesTab)
- pages/Actions.tsx (-> ActionsTab)
- pages/Users.tsx, UsersPage.impl.tsx (replaced by Authentik tabs)
- components/BackupsPage.tsx (-> JobsTab)
- components/ObservabilityPage.tsx (split into Alerts/Links/Metrics tabs)
- hooks/useUsers.ts (orphaned after Users page deletion)
- the corresponding page test files (Media, FileBrowser, Applications,
  Actions, UsersPage) that tested the deleted pages directly.

The service-tab components are the live implementations; ServicePage
renders them. No live code references the deleted files.

Docs: append an Information Architecture section to REQUIREMENTS.md
documenting the services-as-hub model (nav shape, service-page tabs,
service type registry, Users->Authentik, Observability split, legacy
route 404s, empty state). Add a CHANGELOG entry under [Unreleased].

92 frontend tests pass (was 112; -20 deleted page tests); 271 backend
tests pass; lint/build green.

Refs openspec/changes/services-as-hub-ia/ (tasks slice 11).
2026-06-26 20:11:02 +00:00

119 lines
7.9 KiB
Markdown

# Slice 6 — ssh_tasks content tabs: Files + Actions (worker output)
## Files changed
| File | Status | Lines |
|------|--------|-------|
| `frontend/src/pages/service-tabs/FilesTab.tsx` | new | 528 |
| `frontend/src/pages/service-tabs/ActionsTab.tsx` | new | 308 |
| `frontend/src/pages/service-tabs/__tests__/FilesTab.test.tsx` | new | 63 |
| `frontend/src/pages/service-tabs/__tests__/ActionsTab.test.tsx` | new | 49 |
| `frontend/src/pages/service-tabs/index.ts` | modified | +4/-4 (import real FilesTab/ActionsTab) |
| `frontend/src/pages/service-tabs/stubs.tsx` | modified | -8 (removed FilesTab/ActionsTab stubs) |
| `frontend/src/pages/service-tabs/MediaTab.tsx` | modified | +10/-1 (row-click nav fix) |
| `frontend/src/pages/service-tabs/__tests__/MediaTab.test.tsx` | modified | +4 (mock useServiceInstances) |
**Total: ~967 lines** (948 new + 27 modified diff). Over the 400-line budget; dominated by the verbatim lift of the FileBrowser content (~528 lines) and Actions content (~308 lines). The genuine new-logic delta is ~30 lines (instance wiring + row-click fix + tests).
## How instance.id is wired into hooks
**FilesTab**: `instance.id` replaces the old `machine_id` from search params. All hooks (`useDirectoryListing`, `useFfprobe`, `useRunJob`) receive `instance.id` directly as the machineId parameter. The machine-tab selector (`TabbedCard` + `useMonitoringSettings`), the machine_id search-param logic, and the "no file machines" fallback are all removed. The initial path is read from `?path=` search param for deep-link support.
**ActionsTab**: `instance.id` is used as the fixed `runServiceId` — the old `useServiceInstances("ssh_tasks")` call and the service selector dropdown are removed. Tasks run on this instance by default. The task editor dialog no longer has a "Default SSH task service" dropdown (the instance is implicit). The `services` prop on `TaskEditor`/`TaskDialog` is removed entirely since the instance is fixed.
## MediaTab row-click resolution (cross-slice fix from slice 5)
The old row-click navigated to `/files?path=...` (legacy route, now 404s). Fixed:
```tsx
const { data: sshServices = [] } = useServiceInstances("ssh_tasks");
const handleRowClick = (row: MediaItem) => {
const sshInstance = sshServices.find((s) => s.enabled);
const base = sshInstance
? `/services/ssh_tasks/${sshInstance.id}`
: "/services/ssh_tasks";
navigate(`${base}?path=${encodeURIComponent(row.path)}`);
};
```
If an enabled ssh_tasks instance exists, the link opens its service page with the path query param (FilesTab reads `?path=`). If none exists, the link goes to `/services/ssh_tasks` (ServiceTypePage empty state / resolver).
## Validation
```
npm run lint → 0 errors, 2 pre-existing warnings (UsersPage.impl.tsx, unrelated)
npm run build → ✓ built (tsc -b + vite)
npm run test → 29 files / 94 tests passed (was 90; +4 new)
```
## Deviations from design
1. **Over 400-line budget.** The FilesTab lift is ~528 lines because it includes all the ffprobe rendering helpers + component logic verbatim from FileBrowser.impl.tsx. ActionsTab is ~308 lines. Could not shrink without dropping features. The task explicitly acknowledged this: "FileBrowser.impl.tsx is large; the lift is mostly mechanical."
2. **ActionsTab simplified: no service-selector dropdown.** The old Actions page had a "Default SSH task service" dropdown in both the editor and the run pane, using `useServiceInstances("ssh_tasks")`. Since the tab is already on a specific instance, the run service is always `instance.id`. The dropdown and the `services` prop on TaskEditor/TaskDialog are removed. The `NONE` sentinel constant is also removed.
3. **No mobile layout.** This branch is based on main, NOT on mobile-responsive-parity. FilesTab lifts main's DataTable + column-visibility pattern (no MobileCardRow).
4. **Old page files kept.** FileBrowser.impl.tsx and Actions.tsx stay in the repo (cleanup is Slice 11). Their test files still pass since they render the page components directly.
## skill_resolution
`none` — no project/user SKILL.md paths were injected; no `.atl/skill-registry.md` found.
## Residual risks
- **FilesTab and ActionsTab duplicate code** from their old top-level pages. The duplicates are temporary (Slice 11 deletes the old pages).
- **ActionsTab removed the service-selector dropdown.** If users need to run a task on a DIFFERENT ssh_tasks instance (not the current one), they'd need to switch instances via the service page's instance switcher. This is consistent with the new IA (each instance has its own page).
- **MediaTab now depends on `useServiceInstances("ssh_tasks")`.** This adds a TanStack Query call but it's cache-shared with other ssh_tasks queries.
```acceptance-report
{
"criteriaSatisfied": [
{
"id": "criterion-1",
"status": "satisfied",
"evidence": "Slice 6 implements FilesTab (lift from FileBrowser.impl.tsx, instance-scoped hooks, ?path= deep-link), ActionsTab (lift from Actions.tsx, instance as default run service), resolves the MediaTab row-click cross-slice dependency (navigate to /services/ssh_tasks/<id>?path=...), and adds tests for both tabs. No scope widening: only service-tab files + MediaTab row-click + test mock touched. Old page files preserved for Slice 11. 94 tests pass; lint/build green."
}
],
"changedFiles": [
"frontend/src/pages/service-tabs/FilesTab.tsx",
"frontend/src/pages/service-tabs/ActionsTab.tsx",
"frontend/src/pages/service-tabs/__tests__/FilesTab.test.tsx",
"frontend/src/pages/service-tabs/__tests__/ActionsTab.test.tsx",
"frontend/src/pages/service-tabs/index.ts",
"frontend/src/pages/service-tabs/stubs.tsx",
"frontend/src/pages/service-tabs/MediaTab.tsx",
"frontend/src/pages/service-tabs/__tests__/MediaTab.test.tsx"
],
"testsAddedOrUpdated": [
"frontend/src/pages/service-tabs/__tests__/FilesTab.test.tsx",
"frontend/src/pages/service-tabs/__tests__/ActionsTab.test.tsx",
"frontend/src/pages/service-tabs/__tests__/MediaTab.test.tsx"
],
"commandsRun": [
{ "command": "cd frontend && npm run lint", "result": "passed", "summary": "0 errors, 2 pre-existing warnings" },
{ "command": "cd frontend && npm run build", "result": "passed", "summary": "tsc -b + vite build clean" },
{ "command": "cd frontend && npm run test", "result": "passed", "summary": "29 files / 94 tests passed (was 90; +4 new)" }
],
"validationOutput": [
"FilesTab: instance.id wired into useDirectoryListing/useFfprobe/useRunJob; ?path= read from useSearchParams; machine selector removed.",
"ActionsTab: instance.id used as fixed runServiceId; service-selector dropdown removed; TaskEditor/TaskDialog simplified.",
"MediaTab row-click: navigates to /services/ssh_tasks/<first-enabled-id>?path=... (resolves slice 5 cross-slice flag).",
"stubs.tsx: FilesTab/ActionsTab stubs removed; index.ts imports real components.",
"MediaTab.test.tsx: useServiceInstances mock added to fix QueryClient error."
],
"residualRisks": [
"FilesTab/ActionsTab duplicate code from old pages (temporary; Slice 11 deletes old files).",
"ActionsTab no longer has a service-selector dropdown (run is always on current instance; switch via instance switcher).",
"No mobile layout (branch is on main, not mobile-responsive-parity)."
],
"noStagedFiles": true,
"diffSummary": "~967 lines: FilesTab.tsx (528, verbatim lift from FileBrowser.impl.tsx with instance wiring + ?path= deep-link), ActionsTab.tsx (308, lift from Actions.tsx with instance as fixed run service), 4 test files (112 lines), index.ts/stubs.tsx wiring (12 lines), MediaTab.tsx row-click fix (10 lines). Over 400-line budget due to mechanical lift of two large pages.",
"reviewFindings": [
"no blockers"
],
"manualNotes": "git status confirms nothing is staged; all changes are unstaged/untracked. The old FileBrowser.impl.tsx and Actions.tsx are kept (Slice 11 deletes them). Their existing tests still pass."
}
```