diff --git a/docs/superpowers/plans/2026-08-22-local-only-logout-visibility.md b/docs/superpowers/plans/2026-08-22-local-only-logout-visibility.md
new file mode 100644
index 00000000..bb85108e
--- /dev/null
+++ b/docs/superpowers/plans/2026-08-22-local-only-logout-visibility.md
@@ -0,0 +1,337 @@
+# Local-Only Logout Visibility 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:** Show ThothII's `Log out` control only for standalone installations using local authentication, while keeping it absent from the Aritmolab server and every other non-local mode.
+
+**Architecture:** Reuse the already validated `GET /auth/config` response as the single runtime authority. `AuthGate` derives `canLogout` from `config.mode === "local"`, passes that explicit capability into `AppShell`, and `AppShell` conditionally renders only the existing button while leaving identity display and logout mechanics unchanged.
+
+**Tech Stack:** React 18, TypeScript 5.6, Vitest 2, React Testing Library, MSW, Vite 6.
+
+## Global Constraints
+
+- Source baseline is `6475ea3`; the approved design is `docs/superpowers/specs/2026-08-22-local-only-logout-visibility-design.md`.
+- `GET /auth/config` is the sole authority. Do not add platform, hostname, URL, issuer, packaging, or build-time detection.
+- The exact visibility rule is `config.mode === "local"`; `upstream`, `oidc`, `none`, `mock`, an absent config, or an invalid config must never enable the control.
+- Keep the authenticated username or display name visible in every mode.
+- Preserve the existing `POST /auth/logout` route, logout coordinator, authentication-state cleanup, query cleanup, and session/transcript isolation behavior.
+- Do not change backend, Compose, native `tht`, deployment, or packaging files.
+- Do not add dependencies or change the English UI string `Log out`.
+- Use strict TDD: add the focused regression tests, observe the intended RED, implement the minimum change, then run focused and full GREEN gates.
+- Preserve and do not stage the pre-existing changes in `.superpowers/sdd/progress.md`, `brain/index.md`, and `brain/codebase/psd-dwh-transport.md`.
+
+---
+
+### Task 1: Propagate the local-auth capability and conditionally render logout
+
+**Files:**
+- Modify: `frontend/src/auth/AuthGate.test.tsx:10-17,63-68`
+- Modify: `frontend/src/shell/AppShell.auth.test.tsx:11-28,45-89,90-153`
+- Modify: `frontend/src/auth/AuthGate.tsx:20-29,79-81`
+- Modify: `frontend/src/shell/AppShell.tsx:39-40,715-722`
+- Modify: `frontend/src/shell/AppShell.new-session.test.tsx:12-15`
+- Modify: `frontend/src/shell/AppShell.session-mgmt.test.tsx:22-26`
+- Modify: `frontend/src/shell/AppShell.session-target.test.tsx:12-15`
+
+**Interfaces:**
+- Consumes: validated `AuthPublicConfig.mode` from the existing `getAuthConfig()` call in `AuthGate`.
+- Produces:
+
+```ts
+interface AppShellProps {
+ canLogout: boolean;
+}
+
+export function AppShell({ canLogout }: AppShellProps): JSX.Element;
+```
+
+- `canLogout` is required at every `AppShell` call site. Production passes
+ `config?.mode === "local"`; focused non-auth shell tests pass `false`; existing local-auth shell
+ tests pass `true`.
+
+- [ ] **Step 1: Add RED tests for capability propagation and shell visibility**
+
+In `frontend/src/auth/AuthGate.test.tsx`, replace the `AppShell` mock with a capability-observing
+mock:
+
+```tsx
+vi.mock("../shell/AppShell", () => ({
+ AppShell: ({ canLogout }: { canLogout: boolean }) => (
+
+ Authenticated shell
+ {canLogout && (
+
+ )}
+
+ ),
+}));
+```
+
+Extend the existing local authenticated-shell test so it proves the positive contract:
+
+```tsx
+test("renders the authenticated shell from the safe /me DTO", async () => {
+ render();
+
+ const shell = await screen.findByTestId("authenticated-shell");
+ expect(shell).toHaveAttribute("data-can-logout", "true");
+ expect(screen.getByRole("button", { name: "Log out" })).toBeInTheDocument();
+ expect(screen.queryByRole("heading", { name: /sign in/i })).not.toBeInTheDocument();
+});
+```
+
+Add an upstream regression beside it:
+
+```tsx
+test("disables shell logout for trusted upstream authentication", async () => {
+ server.use(
+ http.get("/api/auth/config", () => HttpResponse.json({
+ mode: "upstream", localLogin: false, oidcLogin: false,
+ })),
+ http.get("/api/me", () => HttpResponse.json({
+ issuer: "portal",
+ subject: "portal-user",
+ displayName: "Portal user",
+ roles: ["user"],
+ permissions: ["session.use"],
+ isAdmin: false,
+ csrfToken: null,
+ session: null,
+ })),
+ );
+
+ render();
+
+ const shell = await screen.findByTestId("authenticated-shell");
+ expect(shell).toHaveAttribute("data-can-logout", "false");
+ expect(screen.queryByRole("button", { name: "Log out" })).not.toBeInTheDocument();
+});
+```
+
+In `frontend/src/shell/AppShell.auth.test.tsx`, add a `canLogout` argument to the existing helper
+and make the keyed/direct local-auth renders explicit:
+
+```tsx
+function renderShell(user: {
+ subject: string;
+ isAdmin: boolean;
+ roles: readonly ("user" | "admin")[];
+ permissions: readonly string[];
+}, canLogout = true) {
+ const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
+ setAuthState({
+ issuer: "local", ...user, csrfToken: null, session: null,
+ });
+ return render(
+
+
+ ,
+ );
+}
+
+function KeyedAuthenticatedShell() {
+ const user = useAuthUser();
+ const generation = useAuthGeneration();
+ return user
+ ?
+ : null;
+}
+```
+
+Change the direct render in the stale-logout test to ``. Then add this
+negative shell test before the existing local logout test:
+
+```tsx
+test("hides logout outside local authentication while preserving the identity", async () => {
+ let logoutCalls = 0;
+ server.use(http.post("/api/auth/logout", () => {
+ logoutCalls += 1;
+ return new HttpResponse(null, { status: 204 });
+ }));
+
+ renderShell({
+ subject: "portal-user",
+ isAdmin: false,
+ roles: ["user"],
+ permissions: ["session.use"],
+ }, false);
+
+ expect(await screen.findByText("portal-user")).toBeInTheDocument();
+ expect(screen.queryByRole("button", { name: "Log out" })).not.toBeInTheDocument();
+ expect(logoutCalls).toBe(0);
+});
+```
+
+- [ ] **Step 2: Run the focused tests and capture RED**
+
+Run from `frontend/`:
+
+```bash
+npx vitest run src/auth/AuthGate.test.tsx src/shell/AppShell.auth.test.tsx
+```
+
+Expected: FAIL. The local `AuthGate` test receives no `canLogout` prop, and the real `AppShell`
+still renders `Log out` when the new negative test passes `false`.
+
+- [ ] **Step 3: Implement the minimal explicit capability flow**
+
+In `frontend/src/auth/AuthGate.tsx`, replace `AuthenticatedContent` with:
+
+```tsx
+function AuthenticatedContent({
+ canLogout,
+ onExpired,
+}: {
+ canLogout: boolean;
+ onExpired: () => void;
+}) {
+ const user = useAuthUser();
+ const authGeneration = useAuthGeneration();
+ useEffect(() => {
+ if (!user) onExpired();
+ }, [onExpired, user]);
+ return user
+ ?
+ : null;
+}
+```
+
+Replace the authenticated branch with the closed local-only rule:
+
+```tsx
+if (status === "authenticated") {
+ return (
+ setStatus("login")}
+ />
+ );
+}
+```
+
+In `frontend/src/shell/AppShell.tsx`, add the required prop at the component boundary:
+
+```tsx
+interface AppShellProps {
+ canLogout: boolean;
+}
+
+export function AppShell({ canLogout }: AppShellProps) {
+ const authenticatedUser = useAuthUser();
+```
+
+Keep the identity row unchanged and wrap only the existing button:
+
+```tsx
+{authenticatedUser && (
+
+)}
+```
+
+Update the three non-authentication shell test helpers so their intent is explicit and they do
+not render an irrelevant logout control:
+
+```tsx
+// frontend/src/shell/AppShell.new-session.test.tsx
+return render(
+
+
+ ,
+);
+
+// frontend/src/shell/AppShell.session-mgmt.test.tsx
+return render(
+
+
+ ,
+);
+
+// frontend/src/shell/AppShell.session-target.test.tsx
+return render(
+
+
+ ,
+);
+```
+
+Do not alter `signOut()`, `logoutUser()`, `frontend/src/api/auth.ts`, or any backend route.
+
+- [ ] **Step 4: Run the focused tests and confirm GREEN**
+
+Run from `frontend/`:
+
+```bash
+npx vitest run src/auth/AuthGate.test.tsx src/shell/AppShell.auth.test.tsx
+```
+
+Expected: both test files PASS, including the new local-positive and upstream-negative cases.
+
+- [ ] **Step 5: Prove every `AppShell` caller supplies the capability**
+
+Run from `frontend/`:
+
+```bash
+npx tsc -b
+```
+
+Expected: exit code 0 with no TypeScript diagnostics. A missing `canLogout` at any production or
+test call site is a compile error.
+
+- [ ] **Step 6: Run complete frontend verification**
+
+Run from `frontend/`:
+
+```bash
+npx vitest run
+npm run build
+```
+
+Expected: the complete Vitest suite passes; the build completes TypeScript project checking and
+Vite production bundling with exit code 0.
+
+- [ ] **Step 7: Review scope and commit the implementation**
+
+Run from the repository root:
+
+```bash
+git diff --check
+git diff -- frontend/src/auth/AuthGate.tsx \
+ frontend/src/auth/AuthGate.test.tsx \
+ frontend/src/shell/AppShell.tsx \
+ frontend/src/shell/AppShell.auth.test.tsx \
+ frontend/src/shell/AppShell.new-session.test.tsx \
+ frontend/src/shell/AppShell.session-mgmt.test.tsx \
+ frontend/src/shell/AppShell.session-target.test.tsx
+git status --short
+```
+
+Expected: no whitespace errors; the implementation diff contains only capability propagation,
+conditional button rendering, and the corresponding test updates. The pre-existing brain and SDD
+files remain unstaged.
+
+Commit only the implementation files:
+
+```bash
+git add frontend/src/auth/AuthGate.tsx \
+ frontend/src/auth/AuthGate.test.tsx \
+ frontend/src/shell/AppShell.tsx \
+ frontend/src/shell/AppShell.auth.test.tsx \
+ frontend/src/shell/AppShell.new-session.test.tsx \
+ frontend/src/shell/AppShell.session-mgmt.test.tsx \
+ frontend/src/shell/AppShell.session-target.test.tsx
+git commit -m "fix(frontend): hide logout outside local auth"
+```
+
+Expected: one commit containing only the seven listed frontend files. Do not stage or commit the
+pre-existing workspace changes.