From a227cbe7558dace902e1904225af3b50172d18c1 Mon Sep 17 00:00:00 2001 From: mptyl Date: Fri, 14 Aug 2026 17:32:07 +0200 Subject: [PATCH] docs: plan Pi restart operator workflow --- ...6-08-14-pi-management-operator-workflow.md | 764 ++++++++++++++++++ 1 file changed, 764 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-14-pi-management-operator-workflow.md diff --git a/docs/superpowers/plans/2026-08-14-pi-management-operator-workflow.md b/docs/superpowers/plans/2026-08-14-pi-management-operator-workflow.md new file mode 100644 index 00000000..b7ec6d63 --- /dev/null +++ b/docs/superpowers/plans/2026-08-14-pi-management-operator-workflow.md @@ -0,0 +1,764 @@ +# 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/thothctl-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/thothctl-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/thothctl-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/thothctl-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.