# Pi Management Operator Workflow Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add a safe `thothctl pi restart` command and replace the Pi Management dialog's duplicated technical copy with a structured Docker-only operator workflow. **Architecture:** Keep image changes in the existing recoverable `pi update` transaction. Implement configuration reload as a separate restart transaction that retains the current image, shares the Pi lifecycle lock, and uses a separate recovery file so the latest update remains rollback-capable. The React dialog remains a settings/diagnostics surface and only explains platform-specific host actions. **Tech Stack:** Go 1.26, Docker Compose v2, React 18, TypeScript, TanStack Query, Tailwind CSS, Vitest, Testing Library, shell documentation gates. **Spec:** `docs/superpowers/specs/2026-08-14-pi-management-operator-workflow-design.md` ## Global Constraints - Pi runs only inside the Docker Compose `core` service; add no host-Pi or raw-Compose operator guidance. - `pi restart` requires `--yes`; without `--drain` it refuses active sessions, and with `--drain` it waits without terminating them. - Restart retains the currently selected image and never builds, pulls, or promotes an image. - Restart and update use separate recovery files but one installation-scoped lifecycle lock. - The browser receives no Docker access, shell, credential values, or write access to `deploy/pi/*.json`. - Use `~` for GUI home-directory examples. All GUI strings remain English. - Platform tabs remain closed initially; dialog and instruction scrolling remain functional. - Do not stage or modify the unrelated existing changes in `frontend/src/shell/WorkspaceManager.tsx` and `frontend/src/shell/WorkspaceManager.test.tsx`. - `PiManagement.tsx` and its test contain approved uncommitted work from earlier revisions; edit and commit them only in the frontend task. ## File map - Create `tools/thothctl/internal/pi/restart.go` and `restart_test.go` for the restart transaction. - Modify `tools/thothctl/internal/pi/state.go` and `state_test.go` for one shared lifecycle lock. - Modify `tools/thothctl/internal/config/installation.go` and its test for `restart-state.json`. - Modify `tools/thothctl/internal/pi/update.go` and its test to share the active-session drain helper and compose restart recovery. - Modify the `Target.Source` comment in `tools/thothctl/internal/pi/state.go` when `"restart"` becomes a valid non-image lifecycle source. - Modify `tools/thothctl/cmd/thothctl/main.go` and its test for usage, parsing, dispatch, and recovery. - Modify `frontend/src/shell/PiManagement.tsx` and its test for the structured workflow. - Modify `docs/contracts/tht-pi.md`, `docs/install/pi-management.md`, `docs/general/pi-configuration.md`, and `scripts/verify-workspace-install-docs.sh`. - Modify `README.md` only if it claims to list the complete Pi command surface. --- ### Task 1: Separate restart state and share the lifecycle lock **Files:** - Modify: `tools/thothctl/internal/config/installation.go:234-249` - Test: `tools/thothctl/internal/config/installation_test.go` - Modify: `tools/thothctl/internal/pi/state.go:171-224` - Test: `tools/thothctl/internal/pi/state_test.go` **Interfaces:** - Produces: `func (Installation) RestartStatePath() string` - Produces: `func lifecycleLockPath(statePath string) string` - Preserves: `func acquireLock(statePath string) (*updateLock, error)` - [ ] **Step 1: Write failing path and cross-operation lock tests** Add to `installation_test.go`: ```go if got, want := installation.RestartStatePath(), filepath.Join(installation.ControlDirectory(), "restart-state.json"); got != want { t.Fatalf("RestartStatePath() = %q, want %q", got, want) } ``` Add to `state_test.go`: ```go func TestUpdateAndRestartStatePathsShareOneLifecycleLock(t *testing.T) { dir := t.TempDir() first, err := acquireLock(filepath.Join(dir, "update-state.json")) if err != nil { t.Fatal(err) } defer first.Release() second, err := acquireLock(filepath.Join(dir, "restart-state.json")) if !errors.Is(err, ErrLockHeld) || second != nil { t.Fatalf("second lock = %#v, %v; want nil, ErrLockHeld", second, err) } } ``` - [ ] **Step 2: Run the focused tests and verify RED** ```bash cd tools/thothctl go test ./internal/config ./internal/pi -run 'Test.*(RestartStatePath|ShareOneLifecycleLock)' -count=1 ``` Expected: compile failure for `RestartStatePath`, then lock-test failure until both files resolve to one lock. - [ ] **Step 3: Implement the installation path and common lock** Add to `installation.go`: ```go func (i Installation) RestartStatePath() string { return filepath.Join(i.ControlDirectory(), "restart-state.json") } ``` Change lock derivation in `state.go`: ```go func lifecycleLockPath(statePath string) string { return filepath.Join(filepath.Dir(statePath), "pi-lifecycle.lock") } var ErrLockHeld = errors.New("another Pi update, restart, or rollback is already in progress") ``` Keep `acquireLock(statePath)` but set `path := lifecycleLockPath(statePath)`. Preserve owner metadata and durable cleanup. - [ ] **Step 4: Run config, state, update, and rollback tests** ```bash cd tools/thothctl go test ./internal/config ./internal/pi -count=1 ``` Expected: PASS. - [ ] **Step 5: Commit** ```bash git add tools/thothctl/internal/config/installation.go tools/thothctl/internal/config/installation_test.go tools/thothctl/internal/pi/state.go tools/thothctl/internal/pi/state_test.go git commit -m "refactor(thothctl): share Pi lifecycle lock" ``` --- ### Task 2: Implement the core-only restart transaction **Files:** - Create: `tools/thothctl/internal/pi/restart.go` - Create: `tools/thothctl/internal/pi/restart_test.go` - Modify: `tools/thothctl/internal/pi/update.go:108-145,802-849` - Test: `tools/thothctl/internal/pi/update_test.go` **Interfaces:** - Consumes existing `Runner`, `lifecycleHooks`, lock, maintenance, session inventory, `Doctor`, `Status`, `renderedCore`, `runningImage`, `recreateCore`, state, and recovery helpers. - Produces: ```go type RestartRequest struct { StatePath string UpdateStatePath string Confirm bool Drain bool } type RestartResult struct { StatePath string Version string } func Restart(context.Context, Runner, RestartRequest) (RestartResult, error) func RecoverLifecycleMaintenance(context.Context, Runner, string, string, bool) error ``` - [ ] **Step 1: Write failing restart transaction tests** Create `restart_test.go` in package `pi`, reusing `newFakeRunner`, `assertCalled`, and `assertNotCalled`: ```go func TestRestartRequiresConfirmationWithoutInvokingCompose(t *testing.T) { dir := t.TempDir() fake := newFakeRunner() _, err := Restart(context.Background(), fake, RestartRequest{ StatePath: filepath.Join(dir, "restart-state.json"), UpdateStatePath: filepath.Join(dir, "update-state.json"), }) if !errors.Is(err, ErrConfirmationRequired) { t.Fatalf("Restart() error = %v, want ErrConfirmationRequired", err) } assertNotCalled(t, fake.calls, "compose") } func TestRestartDrainsRecreatesOnlyCoreAndRetainsImage(t *testing.T) { fake := newFakeRunner() fake.activeSessions = true dir := t.TempDir() hooks := defaultLifecycleHooks hooks.sleep = func(time.Duration) { fake.activeSessions = false } result, err := restartWithHooks(context.Background(), fake, RestartRequest{ StatePath: filepath.Join(dir, "restart-state.json"), UpdateStatePath: filepath.Join(dir, "update-state.json"), Confirm: true, Drain: true, }, hooks) if err != nil { t.Fatal(err) } if result.Version != fake.version { t.Fatalf("version = %q, want %q", result.Version, fake.version) } assertCalled(t, fake.calls, "up --detach --wait --wait-timeout 45 --no-deps --force-recreate core") assertNotCalled(t, fake.calls, "build --pull") assertNotCalled(t, fake.calls, "pull ") assertNotCalled(t, fake.calls, "frontend") if _, err := os.Stat(result.StatePath); !errors.Is(err, os.ErrNotExist) { t.Fatalf("successful restart state still exists: %v", err) } } ``` Also add: - `TestRestartRefusesActiveSessionsWithoutDrain` - `TestRestartRefusesInterruptedUpdateOrRestartState` - `TestRestartPreflightFailureNeverRecreatesCoreAndClearsMaintenance` - `TestRestartPostRecreateFailureKeepsMaintenanceAndRecoveryState` - `TestRecoverLifecycleMaintenanceVerifiesAndClearsRestartState` - `TestRestartRejectsImageConfigurationAndMountDrift` Extend the shared fake runner with a `recreated bool` field, set it when the force-recreate call is observed, and make the existing post-candidate failure branches apply when `fake.built || fake.recreated`. This lets restart failures occur after mutation without pretending an image build happened. For post-recreate failure set `fake.fail = "health"`, then assert maintenance remains true and `restart-state.json` remains. - [ ] **Step 2: Run restart tests and verify RED** ```bash cd tools/thothctl go test ./internal/pi -run 'TestRestart|TestRecoverLifecycleMaintenance' -count=1 ``` Expected: compile failure for the missing restart interfaces. - [ ] **Step 3: Extract the existing active-session loop** Move the 30-second loop from `updateWithHooks` into `update.go`: ```go func waitForInactiveSessions(ctx context.Context, runner Runner, drain bool, sleep func(time.Duration)) error { running, err := activeSessions(ctx, runner) if err != nil { return err } if !running { return nil } if !drain { return ErrActiveSessions } for attempts := 0; attempts < 30; attempts++ { running, err = activeSessions(ctx, runner) if err != nil { return err } if !running { return nil } sleep(time.Second) } return ErrActiveSessions } ``` Replace the original update loop with `waitForInactiveSessions(...)`. Preserve the second inventory check immediately before mutation. - [ ] **Step 4: Implement `Restart` in `restart.go`** Implement `Restart` as a wrapper around `restartWithHooks`. The transaction must execute in this exact order: 1. validate both state paths; 2. acquire the shared lock using `RestartStatePath`; 3. require confirmation; 4. reject recovery-required update or restart state; 5. activate maintenance and arrange cleanup for pre-mutation returns; 6. wait/refuse through `waitForInactiveSessions`; 7. run `Doctor` as preflight; 8. read current version, rendered core, running image, configuration SHA, and mount identity; 9. write restart state with `Target{Version: version, Source: "restart"}`; 10. recheck active sessions; 11. durably mark `MutationStarted`; 12. call `recreateCore(ctx, runner)` directly, without image override; 13. prove maintenance remains active; 14. record `PhaseRecreated`; 15. run `verifyRestart(ctx, runner, version, previous)`; 16. record `PhaseVerified`, remove only `restart-state.json`, and clear maintenance. Implement `verifyRestart` to run `Doctor`, reload rendered configuration and running image, require the same image ID, require the same `ConfigurationSHA`, and require `sameMounts(previous.Mounts, after.Mounts)`. Use stable errors for image, external-configuration, and persistence-mount drift. Update the `Target.Source` comment in `state.go` to include the non-image `restart` operation. Use: ```go func Restart(ctx context.Context, runner Runner, request RestartRequest) (RestartResult, error) { return restartWithHooks(ctx, runner, request, defaultLifecycleHooks) } ``` Use `recoveryRequired("Pi restart ...", err)` for every post-mutation failure and set the deferred maintenance cleanup flag to false. Do not overwrite or remove a verified `update-state.json`; it remains the image rollback record. - [ ] **Step 5: Implement combined maintenance recovery** Add: ```go func RecoverLifecycleMaintenance( ctx context.Context, runner Runner, updateStatePath string, restartStatePath string, confirm bool, ) error ``` When restart state requires recovery, run `verifyRestart` using the recorded target version and previous image contract, and remove `restart-state.json` only after verification. Then call existing update-state `RecoverMaintenance` without opening admission between the two checks. Missing restart state is allowed; malformed restart state fails closed. - [ ] **Step 6: Run internal Pi tests and verify GREEN** ```bash cd tools/thothctl go test ./internal/pi -count=1 ``` Expected: PASS, including existing update, rollback, mount identity, durability, and transport-loss tests. - [ ] **Step 7: Commit** ```bash git add tools/thothctl/internal/pi/restart.go tools/thothctl/internal/pi/restart_test.go tools/thothctl/internal/pi/update.go tools/thothctl/internal/pi/update_test.go tools/thothctl/internal/pi/state.go git commit -m "feat(thothctl): add safe Pi core restart" ``` --- ### Task 3: Expose `pi restart` through thothctl **Files:** - Modify: `tools/thothctl/cmd/thothctl/main.go:25-53,263-363,470-520` - Test: `tools/thothctl/cmd/thothctl/main_test.go:68-110,567-666` **Interfaces:** - Consumes Task 2's restart and recovery interfaces and Task 1's state paths. - Produces `func parsePiRestartArgs([]string, string, string) (pi.RestartRequest, error)`. - Public syntax: `thothctl --installation pi restart --yes [--drain]`. - [ ] **Step 1: Write failing usage, parser, and dispatch tests** Extend the usage test: ```go for _, expected := range []string{ "pi restart --yes [--drain]", "Recreate only core with the currently selected Pi image", } { if !strings.Contains(usage, expected) { t.Fatalf("usage missing %q", expected) } } ``` Add a table test for: ```go {name: "confirmed", args: []string{"--yes"}, want: pi.RestartRequest{StatePath: restartPath, UpdateStatePath: updatePath, Confirm: true}} {name: "drain", args: []string{"--yes", "--drain"}, want: pi.RestartRequest{StatePath: restartPath, UpdateStatePath: updatePath, Confirm: true, Drain: true}} {name: "duplicate yes", args: []string{"--yes", "--yes"}, wantErr: "--yes may be supplied once"} {name: "duplicate drain", args: []string{"--drain", "--drain"}, wantErr: "--drain may be supplied once"} {name: "unknown", args: []string{"--force"}, wantErr: "unknown pi restart option"} ``` Add command tests proving missing `--yes` exits 2 before mutation, success is sanitized, and maintenance recovery passes both state paths. - [ ] **Step 2: Run focused CLI tests and verify RED** ```bash cd tools/thothctl go test ./cmd/thothctl -run 'Test.*(Restart|Usage|Maintenance)' -count=1 ``` Expected: failure because parsing and dispatch are absent. - [ ] **Step 3: Add usage, parser, dispatch, and recovery routing** Add: ```text pi restart --yes [--drain] Recreate only core with the currently selected Pi image and verify readiness. ``` Dispatch before `case "update"`: ```go case "restart": request, err := parsePiRestartArgs( args[1:], installation.RestartStatePath(), installation.UpdateStatePath(), ) if err != nil { return commandUsageError(stderr, err.Error()) } result, err := pi.Restart(ctx, controlled, request) if err != nil { return piFailure(stderr, err, secretValues) } fmt.Fprintf(stdout, "Pi core restarted with the existing image; version %s readiness and smoke checks passed.\n", result.Version) return 0 ``` Implement exact duplicate and unknown-option errors from the tests. Route `pi maintenance recover --yes` through `RecoverLifecycleMaintenance` with both state paths. - [ ] **Step 4: Run CLI and full Go tests** ```bash cd tools/thothctl go test ./cmd/thothctl -count=1 go test ./... -count=1 ``` Expected: PASS with no secret leakage. - [ ] **Step 5: Build supported binaries** ```bash cd ../.. ./scripts/build-thothctl.sh ``` Expected: supported artifacts under `dist/thothctl/`, exit 0. - [ ] **Step 6: Commit** ```bash git add tools/thothctl/cmd/thothctl/main.go tools/thothctl/cmd/thothctl/main_test.go git commit -m "feat(thothctl): expose Pi restart command" ``` --- ### Task 4: Update contracts, operator docs, and documentation gates **Files:** - Modify: `docs/contracts/tht-pi.md` - Modify: `docs/install/pi-management.md` - Modify: `docs/general/pi-configuration.md` - Modify: `scripts/verify-workspace-install-docs.sh:1160-1188` - Modify: `README.md` only if it claims command completeness. **Interfaces:** - Consumes the exact Task 3 syntax and Task 2 recovery behavior. - Produces one consistent distinction among GUI defaults, host files, credentials, reload, update, and recovery. - [ ] **Step 1: Strengthen the documentation gate first** Require: ```bash "pi restart --yes --drain" \ "restart only core" \ "deploy/pi/models.json" \ "deploy/pi/settings.json" \ "PI_AUTH_FILE" \ "pi update" \ "pi rollback --yes" \ "pi maintenance recover --yes" ``` Add this negative gate: ```bash if grep -Fq '~/.pi/agent/' "$guide"; then echo "Pi management guide must not direct ThothII operators to native Pi paths" >&2 return 1 fi ``` - [ ] **Step 2: Run the documentation verifier and verify RED** ```bash ./scripts/verify-workspace-install-docs.sh ``` Expected: FAIL because restart and separated workflows are not documented. - [ ] **Step 3: Rewrite the two authoritative operator documents** In `docs/contracts/tht-pi.md`, document `pi restart --yes [--drain]`, confirmation, maintenance, bounded drain, current-image retention, core-only recreation, verification, separate restart state, shared lock, and recovery. In `docs/install/pi-management.md`, use these headings: - **Choose application defaults** — GUI Save defaults or CLI configure, not both. - **Edit the provider catalog and enabled-model policy** — project-root `deploy/pi/` files. - **Store provider credentials** — `PI_AUTH_FILE` from the installation environment file. - **Reload changed configuration** — one restart command. - **Update the bundled Pi version** — build command and digest-pinned pull alternative. - **Recover a failed lifecycle operation** — status, logs, rollback, maintenance recovery. - [ ] **Step 4: Add the Docker-operator callout** Add below the introduction of `docs/general/pi-configuration.md`: ```markdown > **ThothII operator note:** ThothII runs Pi only in Docker Compose. Paths under > `~/.pi/agent/` in this document describe Pi's container-side behavior. Operators edit > `deploy/pi/models.json` and `deploy/pi/settings.json` in the ThothII project root and use > the protected host credential file selected by `PI_AUTH_FILE`; they do not edit files inside > the running container. ``` - [ ] **Step 5: Run documentation gates** ```bash ./scripts/verify-workspace-install-docs.sh ./scripts/test-thothctl-build-contract.sh ``` Expected: both exit 0. - [ ] **Step 6: Commit** ```bash git add docs/contracts/tht-pi.md docs/install/pi-management.md docs/general/pi-configuration.md scripts/verify-workspace-install-docs.sh git commit -m "docs: clarify Pi reload and update workflows" ``` Add `README.md` only if it changed. --- ### Task 5: Restructure the Pi Management dialog **Files:** - Modify: `frontend/src/shell/PiManagement.tsx:1-225,286-305,376-449` - Test: `frontend/src/shell/PiManagement.test.tsx:170-230` **Interfaces:** - Consumes the public Task 3 commands. - Preserves API calls, readiness rail, defaults, test, logs, all-tabs-closed state, dialog height, and scrolling. - Removes `UPDATE_COMMAND`, `copyUpdateCommand`, global clipboard assertions, and the bottom Host update section. - [ ] **Step 1: Rewrite the frontend test first** Add these assertions: ```tsx expect(screen.getByText("Using the host terminal:")).toBeVisible(); expect(screen.queryByText(/not this browser page/i)).not.toBeInTheDocument(); expect(screen.queryByRole("region", { name: "Host update" })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Copy update command" })).not.toBeInTheDocument(); expect(screen.queryByText(/:5173/)).not.toBeInTheDocument(); await user.click(within(tablist).getByRole("tab", { name: "Linux" })); const linux = screen.getByRole("tabpanel", { name: "Linux" }); expect(within(linux).getAllByRole("listitem")).toHaveLength(7); expect(linux).toHaveTextContent("The deploy directory is in the ThothII project root, beside compose.yaml"); expect(linux).toHaveTextContent("deploy/pi/models.json"); expect(linux).toHaveTextContent("deploy/pi/settings.json"); expect(linux).toHaveTextContent("baseUrl is the provider API endpoint"); expect(linux).toHaveTextContent("enabledModels uses provider/model identifiers"); expect(linux).toHaveTextContent("PI_AUTH_FILE is a setting in the installation environment file"); expect(linux).toHaveTextContent("~/bin/thothctl --installation ~/thothii-installation.yaml pi restart --yes --drain"); expect(linux).toHaveTextContent("pi update --version --source build --yes --drain"); expect(linux).toHaveTextContent("pi rollback --yes"); expect(linux).not.toHaveTextContent("~/.pi/agent/"); ``` Add equivalent macOS and Windows path/command assertions. Preserve tests for tabs closed, dialog `max-h-[calc(100vh-6rem)]`, and `overflow-y-scroll`. Add: ```tsx expect(screen.getByText("Select the provider, model, and reasoning used for new Pi work. Credentials stay in protected host files.")).toBeVisible(); expect(screen.getByText("Shows at most 200 recent lines with declared secret values removed.")).toBeVisible(); ``` - [ ] **Step 2: Run the focused test and verify RED** ```bash cd frontend npx vitest run src/shell/PiManagement.test.tsx ``` Expected: FAIL on the new lead-in, ordered workflow, restart command, explanations, removed duplicate section, and revised descriptions. - [ ] **Step 3: Introduce shared structured platform data** Define: ```tsx type PiPlatformDetails = { modelsPath: string; settingsPath: string; terminal: string; credentialProtection: string; restartCommand: string; updateCommand: string; pullCommand: string; recoveryCommands: string; }; ``` Render `PiInstructionSteps` as an ordered list with exactly these seven headings: 1. Open the project root 2. Edit the provider catalog 3. Enable the model 4. Set the provider credential 5. Reload Pi configuration 6. Update the Pi version 7. Recover a failed update Use these normal commands: ```text Linux/macOS: ~/bin/thothctl --installation ~/thothii-installation.yaml pi restart --yes --drain ~/bin/thothctl --installation ~/thothii-installation.yaml pi update --version --source build --yes --drain Windows PowerShell: & (Resolve-Path "~\bin\thothctl-windows-amd64.exe") --installation (Resolve-Path "~\thothii-installation.yaml") pi restart --yes --drain & (Resolve-Path "~\bin\thothctl-windows-amd64.exe") --installation (Resolve-Path "~\thothii-installation.yaml") pi update --version --source build --yes --drain ``` Explain `baseUrl`, `api`, `models`, `id`, `name`, `enabledModels`, and `PI_AUTH_FILE` in compact lists. The lead-in must be exactly: ```tsx

Using the host terminal:

``` Keep digest-pinned pull and recovery commands visually subordinate. - [ ] **Step 4: Remove duplicate and developer-only content** Delete: - `UPDATE_COMMAND` - `copyUpdateCommand` - the bottom `Host update` section - `Clipboard` import if unused - Vite, `:5173`, frontend rebuild, native Pi, and container-edit text - mandatory configure, stop/start, status/doctor/test sequences Keep the positive statement that Docker mounts the selected host credential file read-only for Pi. - [ ] **Step 5: Clarify defaults and diagnostics** Use exactly: ```text Select the provider, model, and reasoning used for new Pi work. Credentials stay in protected host files. Shows at most 200 recent lines with declared secret values removed. ``` Do not change API behavior or readiness semantics. - [ ] **Step 6: Run focused and full frontend gates** ```bash cd frontend npx vitest run src/shell/PiManagement.test.tsx npx vitest run npx tsc -b npm run build ``` Expected: focused tests, full suite, typecheck, and production build pass. - [ ] **Step 7: Commit only Pi Management files** ```bash git add frontend/src/shell/PiManagement.tsx frontend/src/shell/PiManagement.test.tsx git commit -m "feat(frontend): simplify Pi operator workflow" ``` Confirm Workspace Manager files remain unstaged. --- ### Task 6: Final verification and local deployment **Files:** - Verify only; change source only to correct a failing gate attributable to Tasks 1-5. **Interfaces:** - Produces fresh evidence that CLI, docs, frontend, and port 8080 agree. - [ ] **Step 1: Run all relevant gates** ```bash cd tools/thothctl go test ./... -count=1 cd ../.. ./scripts/build-thothctl.sh ./scripts/test-thothctl-build-contract.sh ./scripts/verify-workspace-install-docs.sh cd frontend npx vitest run npx tsc -b npm run build cd .. git diff --check ``` Expected: every command exits 0. Existing Vite chunk-size warnings are acceptable. - [ ] **Step 2: Verify the built CLI** ```bash dist/thothctl/thothctl-darwin-arm64 --help ``` Expected: output contains `pi restart --yes [--drain]` plus update, rollback, and maintenance commands. - [ ] **Step 3: Rebuild only the active frontend service** Use project `thothii-9307255178c1`, the current installation environment values, and these Compose files: ```bash docker compose --project-name thothii-9307255178c1 \ -f compose.yaml \ -f deploy/compose.local.yaml \ -f deploy/compose.git-ssh.yaml \ -f deploy/psd/connector-secrets.yaml \ up -d --build --no-deps frontend ``` Never print or inline credential contents. - [ ] **Step 4: Verify port 8080 and health** Fetch `http://127.0.0.1:8080/` and its JavaScript assets. Require: ```text Using the host terminal: pi restart --yes --drain PI_AUTH_FILE is a setting in the installation environment file The deploy directory is in the ThothII project root ``` Reject: ```text not this browser page For native Pi outside Compose Copy update command :5173 ``` Run: ```bash docker ps --format '{{.Names}}|{{.Status}}' ``` Expected: `thothii-9307255178c1-frontend-1` is healthy. - [ ] **Step 5: Inspect final scope** ```bash git status --short git log --oneline -6 ``` Expected: lifecycle state, restart transaction, CLI, docs, and frontend commits are present. Only unrelated pre-existing Workspace Manager changes remain.