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

7.9 KiB

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:

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.
{
  "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."
}