` + `{confirmDelete}`. There is no list, no back button, no `useNavigate`. When the sheet closes — via Cancel today, or via Save once Blocker-1 is fixed — the user is stranded on a blank `` with no way back except browser history. This is a genuine UX defect, not an acceptable artifact of the sheet pattern: this page is reached via `/services/:serviceType/:serviceId` (deep link / row tap from ServicesPage), so closing the editor must return the user somewhere.
**Recommendation:** on sheet close (both save-success and cancel), navigate back to the services list — e.g. add `const navigate = useNavigate();` and `onOpenChange={(o) => { setSheetOpen(o); if (!o) navigate("/services"); }}`, or render a fallback "Back to services" affordance when `!sheetOpen`. This should be resolved in this slice, not deferred, because Blocker-1's fix makes it user-visible.
### Risk-2 — R4.5 dirty-state outside-click confirm not implemented
R4.5 also says the sheet *"does not close on outside-click while the form is dirty (confirm prompt)."* `SheetForm` passes `onOpenChange` straight through to Radix `Sheet` with no dirty guard, and ServicePage wires `onOpenChange={setSheetOpen}` directly. This is likely a cross-slice concern owned by the Slice-1 `SheetForm` deliverable, but it is currently unmet for this form. Flag for the verify pass / Slice 1 retro.
### Suggestion-1 — Strengthen the mobile Save payload assertion
Mobile test #2 (`ServicePage.test.tsx`) only asserts `input.name` and `input.id`. To lock the save semantics claimed by the slice, also assert `input.config` (equals draftConfig), `input.enabled`, and `input.secrets === {}`. Cheap and prevents regressions.
### Suggestion-2 — `save()` async-onClick typing
`SheetForm.onSave` is typed `() => void` but receives an async function; the promise is fire-and-forget. `isPending` correctly gates the button so this is functionally fine, but worth a comment or a `.catch` if error toast UX is added later.
## Verdict
**fix-then-commit.**
The desktop non-regression, Rules-of-Hooks, secrets/delete semantics, and test scaffolding are all correct and verified. However, **Blocker-1** (sheet does not close on save) is a clear, spec-cited (R4.5) deviation, and **Risk-1** (empty page after close) is a real UX bug that becomes user-visible the moment Blocker-1 is fixed. Both should be addressed in this slice before commit. Risk-2 and the two suggestions are non-blocking follow-ups.
## Acceptance
```acceptance-report
{
"criteriaSatisfied": [
{
"id": "criterion-1",
"status": "partially-satisfied",
"evidence": "Slice 6 implements ServicePage mobile SheetForm without widening scope (only ServicePage.tsx + new test). Desktop output verified token-identical to HEAD; Rules-of-Hooks clean; secrets/delete semantics preserved; lint/build/test green. BUT R4.5 'sheet closes on successful save' is not implemented (save() never calls setSheetOpen(false)) and closing the sheet strands the user on an empty page — must-fix before commit."
}
],
"changedFiles": [
"frontend/src/pages/ServicePage.tsx"
],
"testsAddedOrUpdated": [
"frontend/src/pages/__tests__/ServicePage.test.tsx"
],
"commandsRun": [
{ "command": "cd frontend && npm run lint", "result": "passed", "summary": "0 errors; 2 pre-existing warnings in UsersPage.impl.tsx (unrelated)" },
{ "command": "cd frontend && npm run build", "result": "passed", "summary": "vite build green (chunk-size advisory only)" },
{ "command": "cd frontend && npm run test", "result": "passed", "summary": "110/110 tests pass; ServicePage suite 5/5" },
{ "command": "git show HEAD:frontend/src/pages/ServicePage.tsx", "result": "passed", "summary": "Used to verify desktop branch token-identical to pre-change page" }
],
"validationOutput": [
"Desktop non-regression: CONFIRMED token-identical (heading, General, Connection, Widgets, ConfirmDialog).",
"Mobile SheetForm wiring (open-on-mount, title=draft name, onSave=save, onCancel closes, isPending disables Save): CONFIRMED.",
"Connection fields render without SectionCard inside sheet on mobile: CONFIRMED.",
"buildInput() + save()→mutateAsync: CONFIRMED.",
"Secrets leave-blank-to-keep (onlyChanged filter; General secrets:{}): CONFIRMED.",
"ConfirmDialog rendered OUTSIDE SheetForm on mobile (sibling): CONFIRMED.",
"Rules of Hooks (all hooks unconditional, before early returns): CONFIRMED clean.",
"R4.5 'closes on successful save': NOT MET — save() does not call setSheetOpen(false).",
"Empty page after sheet close (cancel/save): real UX bug, no back navigation."
],
"residualRisks": [
"Blocker-1: Sheet does not close on successful save (R4.5 violation) — ServicePage.tsx:117-119.",
"Risk-1: Closing the sheet (cancel, or save once fixed) leaves an empty page with no path back to /services — ServicePage.tsx mobile branch.",
"Risk-2: R4.5 dirty-state outside-click confirm not implemented at ServicePage/SheetForm level (likely Slice-1 cross-cutting concern)."
],
"noStagedFiles": true,
"diffSummary": "Adds a mobile (isMobile) branch to ServicePage that renders the edit form inside a SheetForm (open-on-mount, draft-name title, onSave=save, onCancel=close) with Connection fields unwrapped and ConfirmDialog as a sibling; extracts desktop JSX into configFields/widgetsCard/confirmDelete consts and renames ServiceConnectionCard→ServiceConnectionFields (isMobile prop) so the desktop output stays token-identical. Adds 5 Vitest cases (2 desktop, 3 mobile).",
"reviewFindings": [
"blocker: ServicePage.tsx:117-119 — save() does not close the sheet on success; violates R4.5.",
"blocker: ServicePage.tsx:161-191 — mobile branch has no back navigation; closing the sheet strands the user on an empty page (becomes visible once blocker-1 is fixed).",
"note: R4.5 dirty-state outside-click confirm not implemented (SheetForm passes onOpenChange through).",
"suggestion: ServicePage.test.tsx mobile Save test should also assert config/enabled/secrets payload, not just name+id."
],
"manualNotes": "Verdict: fix-then-commit. Desktop non-regression, hooks, and core save/delete/secrets semantics are correct and verified. The two blockers are tightly coupled (fixing save-close surfaces the empty-page gap) and should be resolved together in this slice: close sheet on save AND navigate back to /services (or render a fallback) on close."
}
```