docs(frontend): clarify Pi management workflow

This commit is contained in:
User
2026-08-22 21:02:10 +02:00
parent 120816d81c
commit 80b3575b2e
2 changed files with 108 additions and 90 deletions
+46 -61
View File
@@ -238,30 +238,35 @@ test("shows a seven-step host-terminal workflow in scrollable platform tabs", as
"Open the project root", "Open the project root",
"Edit the provider catalog", "Edit the provider catalog",
"Enable the model", "Enable the model",
"Check the provider credential", "Complete the provider setup",
"Reload Pi configuration", "Reload Pi configuration",
"Update the Pi version", "Update the Pi version",
"Recover a failed update", "Diagnose and recover a failed operation",
]); ]);
expect(linux).toHaveTextContent("The deploy directory is beside compose.yaml"); expect(linux).toHaveTextContent("cd /absolute/path/to/ThothII");
expect(linux).toHaveTextContent("All commands below start in this project root"); expect(linux).toHaveTextContent("tht pi status");
expect(linux).toHaveTextContent("mkdir -p bin"); expect(linux).toHaveTextContent("tht pi doctor");
expect(linux).toHaveTextContent("go -C tools/thothctl build -o ../../bin/thothctl ./cmd/thothctl"); expect(linux).toHaveTextContent("Invalid JSON");
expect(linux).toHaveTextContent("Provider connectivity");
expect(linux).toHaveTextContent("If maintenance is inactive");
expect(linux).toHaveTextContent("active after an update");
expect(linux).toHaveTextContent("active after a restart");
expect(linux).toHaveTextContent("run every command in this list from that directory");
expect(linux).toHaveTextContent("deploy/pi/models.json"); expect(linux).toHaveTextContent("deploy/pi/models.json");
expect(linux).toHaveTextContent("deploy/pi/settings.json"); expect(linux).toHaveTextContent("deploy/pi/settings.json");
expect(linux).toHaveTextContent("baseUrl is the provider API endpoint"); expect(linux).toHaveTextContent("baseUrl is the provider API endpoint");
expect(linux).toHaveTextContent("enabledModels uses provider/model identifiers"); expect(linux).toHaveTextContent("enabledModels uses provider/model identifiers");
expect(linux).toHaveTextContent("Pi reads the provider API key from a protected file on the host"); expect(linux).toHaveTextContent("During the initial installation");
expect(linux).toHaveTextContent("Do not put the key in models.json or settings.json"); expect(linux).toHaveTextContent("Pi credentials file location");
expect(linux).toHaveTextContent("./bin/thothctl pi restart --yes --drain"); expect(linux).toHaveTextContent("tht pi restart --yes --drain");
expect(linux).toHaveTextContent("./bin/thothctl pi update"); expect(linux).toHaveTextContent("tht pi update");
expect(linux).toHaveTextContent("./bin/thothctl pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain"); expect(linux).toHaveTextContent("tht pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain");
expect(linux).toHaveTextContent("The command installs the Pi version pinned in docker/core.Dockerfile"); expect(linux).toHaveTextContent("tht pi maintenance status");
expect(linux).toHaveTextContent("./bin/thothctl pi maintenance status"); expect(linux).toHaveTextContent("tht pi logs");
expect(linux).toHaveTextContent("./bin/thothctl pi logs"); expect(linux).toHaveTextContent("tht pi rollback --yes");
expect(linux).toHaveTextContent("./bin/thothctl pi rollback --yes"); expect(linux).toHaveTextContent("tht pi maintenance recover --yes");
expect(linux).toHaveTextContent("./bin/thothctl pi maintenance recover --yes"); expect(linux).not.toHaveTextContent("PI_AUTH_FILE");
expect(linux).not.toHaveTextContent("~/bin/"); expect(linux).not.toHaveTextContent("thothctl");
expect(linux).not.toHaveTextContent("~/.pi/agent/"); expect(linux).not.toHaveTextContent("~/.pi/agent/");
await user.click(macosTab); await user.click(macosTab);
@@ -271,24 +276,22 @@ test("shows a seven-step host-terminal workflow in scrollable platform tabs", as
"Open the project root", "Open the project root",
"Edit the provider catalog", "Edit the provider catalog",
"Enable the model", "Enable the model",
"Check the provider credential", "Complete the provider setup",
"Reload Pi configuration", "Reload Pi configuration",
"Update the Pi version", "Update the Pi version",
"Recover a failed update", "Diagnose and recover a failed operation",
]); ]);
expect(macos).toHaveTextContent("The deploy directory is beside compose.yaml"); expect(macos).toHaveTextContent("cd /absolute/path/to/ThothII");
expect(macos).toHaveTextContent("All commands below start in this project root"); expect(macos).toHaveTextContent("run every command in this list from that directory");
expect(macos).toHaveTextContent("go -C tools/thothctl build -o ../../bin/thothctl ./cmd/thothctl");
expect(macos).toHaveTextContent("deploy/pi/models.json"); expect(macos).toHaveTextContent("deploy/pi/models.json");
expect(macos).toHaveTextContent("deploy/pi/settings.json"); expect(macos).toHaveTextContent("deploy/pi/settings.json");
expect(macos).toHaveTextContent("./bin/thothctl pi restart --yes --drain"); expect(macos).toHaveTextContent("tht pi restart --yes --drain");
expect(macos).toHaveTextContent("./bin/thothctl pi update"); expect(macos).toHaveTextContent("tht pi update");
expect(macos).toHaveTextContent("./bin/thothctl pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain"); expect(macos).toHaveTextContent("tht pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain");
expect(macos).toHaveTextContent("The command installs the Pi version pinned in docker/core.Dockerfile"); expect(macos).toHaveTextContent("tht pi maintenance status");
expect(macos).toHaveTextContent("./bin/thothctl pi maintenance status"); expect(macos).toHaveTextContent("tht pi logs");
expect(macos).toHaveTextContent("./bin/thothctl pi logs"); expect(macos).toHaveTextContent("tht pi rollback --yes");
expect(macos).toHaveTextContent("./bin/thothctl pi rollback --yes"); expect(macos).toHaveTextContent("tht pi maintenance recover --yes");
expect(macos).toHaveTextContent("./bin/thothctl pi maintenance recover --yes");
await user.click(windowsTab); await user.click(windowsTab);
const windows = screen.getByRole("tabpanel", { name: "Windows" }); const windows = screen.getByRole("tabpanel", { name: "Windows" });
@@ -297,44 +300,26 @@ test("shows a seven-step host-terminal workflow in scrollable platform tabs", as
"Open the project root", "Open the project root",
"Edit the provider catalog", "Edit the provider catalog",
"Enable the model", "Enable the model",
"Check the provider credential", "Complete the provider setup",
"Reload Pi configuration", "Reload Pi configuration",
"Update the Pi version", "Update the Pi version",
"Recover a failed update", "Diagnose and recover a failed operation",
]); ]);
expect(windows).toHaveTextContent("The deploy directory is beside compose.yaml"); expect(windows).toHaveTextContent("Set-Location C:\\absolute\\path\\to\\ThothII");
expect(windows).toHaveTextContent("All commands below start in this project root"); expect(windows).toHaveTextContent("run every command in this list from that directory");
expect(windows).toHaveTextContent("deploy\\pi\\models.json"); expect(windows).toHaveTextContent("deploy\\pi\\models.json");
expect(windows).toHaveTextContent("deploy\\pi\\settings.json"); expect(windows).toHaveTextContent("deploy\\pi\\settings.json");
expect(windows).toHaveTextContent("New-Item -ItemType Directory -Force bin"); expect(windows).toHaveTextContent('tht pi restart --yes --drain');
expect(windows).toHaveTextContent("go -C tools/thothctl build -o ../../bin/thothctl.exe ./cmd/thothctl"); expect(windows).toHaveTextContent('tht pi update');
expect(windows).toHaveTextContent('.\\bin\\thothctl.exe pi restart --yes --drain'); expect(windows).toHaveTextContent('tht pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain');
expect(windows).toHaveTextContent('.\\bin\\thothctl.exe pi update'); expect(windows).toHaveTextContent('tht pi maintenance status');
expect(windows).toHaveTextContent('.\\bin\\thothctl.exe pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain'); expect(windows).toHaveTextContent('tht pi logs');
expect(windows).toHaveTextContent("The command installs the Pi version pinned in docker/core.Dockerfile"); expect(windows).toHaveTextContent('tht pi rollback --yes');
expect(windows).toHaveTextContent('.\\bin\\thothctl.exe pi maintenance status'); expect(windows).toHaveTextContent('tht pi maintenance recover --yes');
expect(windows).toHaveTextContent('.\\bin\\thothctl.exe pi logs'); expect(windows).not.toHaveTextContent("PI_AUTH_FILE");
expect(windows).toHaveTextContent('.\\bin\\thothctl.exe pi rollback --yes'); expect(windows).not.toHaveTextContent("thothctl");
expect(windows).toHaveTextContent('.\\bin\\thothctl.exe pi maintenance recover --yes');
expect(windows).not.toHaveTextContent("~\\bin\\");
}); });
test("keeps the required PI_AUTH_FILE guidance in one static text node", async () => {
const user = userEvent.setup();
renderManagement();
const tablist = await screen.findByRole("tablist", { name: "Pi host platform" });
await user.click(within(tablist).getByRole("tab", { name: "Linux" }));
const credentialStep = screen.getByRole("heading", { name: "Check the provider credential" }).closest("li");
const guidance = credentialStep?.querySelector("p");
expect(
Array.from(guidance?.childNodes ?? []).some(
(node) => node.nodeType === Node.TEXT_NODE
&& node.textContent?.includes("Pi reads the provider API key from a protected file on the host"),
),
).toBe(true);
});
test("reloads installation defaults when the panel is reopened", async () => { test("reloads installation defaults when the panel is reopened", async () => {
let statusCalls = 0; let statusCalls = 0;
+62 -29
View File
@@ -107,12 +107,15 @@ type PiPlatformDetails = {
modelsPath: string; modelsPath: string;
settingsPath: string; settingsPath: string;
terminal: string; terminal: string;
changeDirectoryCommand: string;
credentialProtection: string; credentialProtection: string;
buildCommand: string;
restartCommand: string; restartCommand: string;
updateCommand: string; updateCommand: string;
pullCommand: string; pullCommand: string;
recoveryCommands: string; diagnosticCommands: string;
rollbackCommand: string;
recoverCommand: string;
verificationCommands: string;
}; };
const piPlatforms: Array<{ id: PiPlatform; label: string; details: PiPlatformDetails }> = [ const piPlatforms: Array<{ id: PiPlatform; label: string; details: PiPlatformDetails }> = [
@@ -123,12 +126,15 @@ const piPlatforms: Array<{ id: PiPlatform; label: string; details: PiPlatformDet
modelsPath: "deploy/pi/models.json", modelsPath: "deploy/pi/models.json",
settingsPath: "deploy/pi/settings.json", settingsPath: "deploy/pi/settings.json",
terminal: "a terminal", terminal: "a terminal",
changeDirectoryCommand: "cd /absolute/path/to/ThothII",
credentialProtection: "a protected host file with mode 0600", credentialProtection: "a protected host file with mode 0600",
buildCommand: "mkdir -p bin\ngo -C tools/thothctl build -o ../../bin/thothctl ./cmd/thothctl", restartCommand: "tht pi restart --yes --drain",
restartCommand: "./bin/thothctl pi restart --yes --drain", updateCommand: "tht pi update",
updateCommand: "./bin/thothctl pi update", pullCommand: "tht pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain",
pullCommand: "./bin/thothctl pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain", diagnosticCommands: "tht pi maintenance status\ntht pi status\ntht pi doctor\ntht pi logs",
recoveryCommands: "./bin/thothctl pi maintenance status\n./bin/thothctl pi logs\n./bin/thothctl pi rollback --yes\n./bin/thothctl pi maintenance recover --yes", rollbackCommand: "tht pi rollback --yes",
recoverCommand: "tht pi maintenance recover --yes",
verificationCommands: "tht pi maintenance status\ntht pi doctor\ntht pi test",
}, },
}, },
{ {
@@ -138,12 +144,15 @@ const piPlatforms: Array<{ id: PiPlatform; label: string; details: PiPlatformDet
modelsPath: "deploy/pi/models.json", modelsPath: "deploy/pi/models.json",
settingsPath: "deploy/pi/settings.json", settingsPath: "deploy/pi/settings.json",
terminal: "Terminal", terminal: "Terminal",
changeDirectoryCommand: "cd /absolute/path/to/ThothII",
credentialProtection: "a protected host file with mode 0600", credentialProtection: "a protected host file with mode 0600",
buildCommand: "mkdir -p bin\ngo -C tools/thothctl build -o ../../bin/thothctl ./cmd/thothctl", restartCommand: "tht pi restart --yes --drain",
restartCommand: "./bin/thothctl pi restart --yes --drain", updateCommand: "tht pi update",
updateCommand: "./bin/thothctl pi update", pullCommand: "tht pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain",
pullCommand: "./bin/thothctl pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain", diagnosticCommands: "tht pi maintenance status\ntht pi status\ntht pi doctor\ntht pi logs",
recoveryCommands: "./bin/thothctl pi maintenance status\n./bin/thothctl pi logs\n./bin/thothctl pi rollback --yes\n./bin/thothctl pi maintenance recover --yes", rollbackCommand: "tht pi rollback --yes",
recoverCommand: "tht pi maintenance recover --yes",
verificationCommands: "tht pi maintenance status\ntht pi doctor\ntht pi test",
}, },
}, },
{ {
@@ -153,12 +162,15 @@ const piPlatforms: Array<{ id: PiPlatform; label: string; details: PiPlatformDet
modelsPath: "deploy\\pi\\models.json", modelsPath: "deploy\\pi\\models.json",
settingsPath: "deploy\\pi\\settings.json", settingsPath: "deploy\\pi\\settings.json",
terminal: "PowerShell", terminal: "PowerShell",
changeDirectoryCommand: "Set-Location C:\\absolute\\path\\to\\ThothII",
credentialProtection: "a protected host file with a user-only ACL", credentialProtection: "a protected host file with a user-only ACL",
buildCommand: "New-Item -ItemType Directory -Force bin | Out-Null\ngo -C tools/thothctl build -o ../../bin/thothctl.exe ./cmd/thothctl", restartCommand: "tht pi restart --yes --drain",
restartCommand: ".\\bin\\thothctl.exe pi restart --yes --drain", updateCommand: "tht pi update",
updateCommand: ".\\bin\\thothctl.exe pi update", pullCommand: "tht pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain",
pullCommand: ".\\bin\\thothctl.exe pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain", diagnosticCommands: "tht pi maintenance status\ntht pi status\ntht pi doctor\ntht pi logs",
recoveryCommands: ".\\bin\\thothctl.exe pi maintenance status\n.\\bin\\thothctl.exe pi logs\n.\\bin\\thothctl.exe pi rollback --yes\n.\\bin\\thothctl.exe pi maintenance recover --yes", rollbackCommand: "tht pi rollback --yes",
recoverCommand: "tht pi maintenance recover --yes",
verificationCommands: "tht pi maintenance status\ntht pi doctor\ntht pi test",
}, },
}, },
]; ];
@@ -171,13 +183,21 @@ function PiInstructionSteps({ details }: { details: PiPlatformDetails }) {
return <ol className="grid gap-4 pl-5 marker:font-semibold marker:text-muted-foreground"> return <ol className="grid gap-4 pl-5 marker:font-semibold marker:text-muted-foreground">
<li> <li>
<h4 className="font-semibold text-foreground">Open the project root</h4> <h4 className="font-semibold text-foreground">Open the project root</h4>
<p className="mt-1 text-muted-foreground">Using {details.terminal}, open the current ThothII checkout or worktree root. All commands below start in this project root. The deploy directory is beside <code>compose.yaml</code>. If this checkout has no <code>bin</code> directory, create it and build the local CLI once:</p> <p className="mt-1 text-muted-foreground">Using {details.terminal}, before editing files or running any lifecycle command, enter the exact checkout or worktree root. Run the platform command below, then run every command in this list from that directory: <code>tht</code> resolves the installation descriptor and configuration from the selected project. A worktree is an independent checkout, so this prevents changing a different copy by mistake.</p>
<PiCodeBlock className="mt-2">{details.buildCommand}</PiCodeBlock> <PiCodeBlock className="mt-2">{details.changeDirectoryCommand}</PiCodeBlock>
<p className="mt-2 text-muted-foreground">The commands below use the executable built in this checkout, so they cannot accidentally target another worktree.</p> <p className="mt-2 text-muted-foreground">The relevant layout is:</p>
<PiCodeBlock className="mt-1">project-root/
├── compose.yaml
├── deploy/
│ └── pi/
│ ├── models.json
│ └── settings.json
└── docker/</PiCodeBlock>
<p className="mt-2 text-muted-foreground">The <code>deploy/</code> directory contains the selected installation descriptor and profile files. You do not need to create or manage a <code>bin/</code> directory: <code>tht</code> is the installed host CLI. If discovery finds multiple descriptors, pass the intended one explicitly with <code>--installation &lt;absolute-path&gt;/thothii-installation.yaml</code>. If <code>tht</code> is not on your <code>PATH</code>, install it using the installation guide.</p>
</li> </li>
<li> <li>
<h4 className="font-semibold text-foreground">Edit the provider catalog</h4> <h4 className="font-semibold text-foreground">Edit the provider catalog</h4>
<p className="mt-1 text-muted-foreground">Edit <code>{details.modelsPath}</code>. It is the provider catalog.</p> <p className="mt-1 text-muted-foreground">Edit <code>{details.modelsPath}</code> only when adding or correcting a provider/model definition. The provider catalog is an address book/map of the services Pi can call: each entry supplies the API endpoint and format, and lists the model identifiers offered there. It does not enable a model and never contains credentials.</p>
<dl className="mt-2 grid grid-cols-[auto_1fr] gap-x-2 gap-y-1 text-muted-foreground"> <dl className="mt-2 grid grid-cols-[auto_1fr] gap-x-2 gap-y-1 text-muted-foreground">
<dt className="font-mono text-foreground">baseUrl </dt><dd>is the provider API endpoint.</dd> <dt className="font-mono text-foreground">baseUrl </dt><dd>is the provider API endpoint.</dd>
<dt className="font-mono text-foreground">api </dt><dd>selects the provider API format.</dd> <dt className="font-mono text-foreground">api </dt><dd>selects the provider API format.</dd>
@@ -188,31 +208,44 @@ function PiInstructionSteps({ details }: { details: PiPlatformDetails }) {
</li> </li>
<li> <li>
<h4 className="font-semibold text-foreground">Enable the model</h4> <h4 className="font-semibold text-foreground">Enable the model</h4>
<p className="mt-1 text-muted-foreground">Edit <code>{details.settingsPath}</code>. It is the enabled-model policy, not the provider catalog.</p> <p className="mt-1 text-muted-foreground">Enable a model after the provider setup is ready. For a custom provider, first define the provider and model ID in <code>{details.modelsPath}</code>; built-in Pi models may already be known without a local catalog entry. Then edit <code>{details.settingsPath}</code> in the project root, add the exact <code>provider/model</code> identifier to <code>enabledModels</code>, and reload Pi. Do not edit it merely to choose the default provider/model: use this page or <code>tht pi configure</code>.</p>
<dl className="mt-2 grid grid-cols-[auto_1fr] gap-x-2 gap-y-1 text-muted-foreground"> <dl className="mt-2 grid grid-cols-[auto_1fr] gap-x-2 gap-y-1 text-muted-foreground">
<dt className="font-mono text-foreground">enabledModels </dt><dd>uses provider/model identifiers to choose the models available for new Pi work.</dd> <dt className="font-mono text-foreground">enabledModels </dt><dd>uses provider/model identifiers to choose the models available for new Pi work.</dd>
</dl> </dl>
</li> </li>
<li> <li>
<h4 className="font-semibold text-foreground">Check the provider credential</h4> <h4 className="font-semibold text-foreground">Complete the provider setup</h4>
<p className="mt-1 text-muted-foreground">Pi reads the provider API key from a protected file on the host. <code>PI_AUTH_FILE</code> tells this installation which file to use; Docker mounts it read-only into the core container. Do not put the key in <code>models.json</code> or <code>settings.json</code>. Keep {details.credentialProtection}.</p> <p className="mt-1 text-muted-foreground">During the initial installation, run <code>tht setup</code> and complete the prompt named “Pi credentials file location”; it can create the empty protected template. On an existing installation with a missing credential, correct the protected credential file selected during setup by following the installation guide. Keep {details.credentialProtection}, then restart and run <code>tht pi doctor</code> and <code>tht pi test</code>. Never paste credentials into either JSON file, the page, a command, or a log.</p>
</li> </li>
<li> <li>
<h4 className="font-semibold text-foreground">Reload Pi configuration</h4> <h4 className="font-semibold text-foreground">Reload Pi configuration</h4>
<p className="mt-1 text-muted-foreground">After changing the catalog, policy, or selected credential file, reload Pi configuration.</p> <p className="mt-1 text-muted-foreground">After changing <code>models.json</code>, <code>settings.json</code>, or the provider credential, reload the running <code>core</code> service so it reads the new files. This is a configuration reload, not a Pi version update; it keeps the current image.</p>
<PiCodeBlock className="mt-2">{details.restartCommand}</PiCodeBlock> <PiCodeBlock className="mt-2">{details.restartCommand}</PiCodeBlock>
</li> </li>
<li> <li>
<h4 className="font-semibold text-foreground">Update the Pi version</h4> <h4 className="font-semibold text-foreground">Update the Pi version</h4>
<p className="mt-1 text-muted-foreground">The command installs the Pi version pinned in <code>docker/core.Dockerfile</code>. Use <code>--version &lt;VERSION&gt;</code> only when you deliberately want another version.</p> <p className="mt-1 text-muted-foreground">Use <code>tht pi update</code> for the routine build: it resolves the latest stable Pi release from the registry, builds it locally, drains active sessions, and recreates only <code>core</code>. Add <code>--version &lt;VERSION&gt;</code> when an explicitly reviewed version is required. The digest-pinned <code>--source pull</code> form is for an already-built, reviewed registry image; its declared version must match the digest-pinned image. Configuration changes use Reload above, not update.</p>
<PiCodeBlock className="mt-2">{details.updateCommand}</PiCodeBlock> <PiCodeBlock className="mt-2">{details.updateCommand}</PiCodeBlock>
<p className="mt-2 text-[11px] text-muted-foreground">Advanced: pull an immutable, digest-pinned image.</p> <p className="mt-2 text-[11px] text-muted-foreground">Advanced: pull an immutable, digest-pinned image.</p>
<PiCodeBlock className="mt-1 text-[11px] text-muted-foreground">{details.pullCommand}</PiCodeBlock> <PiCodeBlock className="mt-1 text-[11px] text-muted-foreground">{details.pullCommand}</PiCodeBlock>
</li> </li>
<li> <li>
<h4 className="font-semibold text-foreground">Recover a failed update</h4> <h4 className="font-semibold text-foreground">Diagnose and recover a failed operation</h4>
<p className="mt-1 text-muted-foreground">Check maintenance status and bounded sanitized Pi logs first. For a failed update, use rollback. For restart or update maintenance recovery after repairing the reported problem, use maintenance recovery.</p> <p className="mt-1 text-muted-foreground">Start with these diagnostics, in order:</p>
<PiCodeBlock className="mt-2 text-[11px] text-muted-foreground">{details.recoveryCommands}</PiCodeBlock> <PiCodeBlock className="mt-2 text-[11px] text-muted-foreground">{details.diagnosticCommands}</PiCodeBlock>
<dl className="mt-2 grid gap-2 text-muted-foreground">
<dt className="font-semibold text-foreground">Configuration</dt><dd>Invalid JSON: fix the reported file and validate it. A provider/model ID mismatch or a model missing from <code>enabledModels</code>: correct the IDs or policy, then reload.</dd>
<dt className="font-semibold text-foreground">Credentials</dt><dd>Missing or unreadable credential: correct the protected credential file selected during setup and its permissions, without printing the file.</dd>
<dt className="font-semibold text-foreground">Provider connectivity</dt><dd>Wrong <code>baseUrl</code>, network, or provider error: correct the endpoint or network, then retry.</dd>
<dt className="font-semibold text-foreground">Docker and disk</dt><dd>Unhealthy Docker or insufficient disk: restore Docker health or free space before retrying.</dd>
</dl>
<p className="mt-2 text-muted-foreground">If maintenance is inactive, no recovery is needed: correct the cause and retry the original command.</p>
<p className="mt-2 text-muted-foreground">If maintenance is active after an update, rollback the previous image:</p>
<PiCodeBlock className="mt-1 text-[11px] text-muted-foreground">{details.rollbackCommand}</PiCodeBlock>
<p className="mt-2 text-muted-foreground">If maintenance is active after a restart, fix the cause first, then recover the captured lifecycle state:</p>
<PiCodeBlock className="mt-1 text-[11px] text-muted-foreground">{details.recoverCommand}</PiCodeBlock>
<p className="mt-2 text-muted-foreground">After the selected remedy, verify the installation:</p>
<PiCodeBlock className="mt-1 text-[11px] text-muted-foreground">{details.verificationCommands}</PiCodeBlock>
</li> </li>
</ol>; </ol>;
} }