diff --git a/backend/src/config.ts b/backend/src/config.ts index 39f28a28..8052e058 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -30,6 +30,8 @@ export interface AppConfig { dataRoot?: string; ollamaEnsureTimeoutMs: number; piManagementTimeoutMs: number; + /** Host CLI platform projected into Docker; not the browser or container OS. */ + hostPlatform?: string; secretsFile?: string; installationConfigFile?: string; evidenceHostRegistryRoot?: string; @@ -475,6 +477,7 @@ export function loadConfig( dataRoot: env.THT_DATA_ROOT, ollamaEnsureTimeoutMs: Number(env.OLLAMA_ENSURE_TIMEOUT_MS ?? 60000), piManagementTimeoutMs: piManagementTimeout(env.PI_MANAGEMENT_TIMEOUT_MS), + hostPlatform: env.THT_HOST_PLATFORM, secretsFile, installationConfigFile, evidenceHostRegistryRoot: env.THT_EVIDENCE_HOST_REGISTRY_ROOT || undefined, diff --git a/backend/src/pi/management.ts b/backend/src/pi/management.ts index 8df76734..726da77d 100644 --- a/backend/src/pi/management.ts +++ b/backend/src/pi/management.ts @@ -36,6 +36,7 @@ export interface PiInstallationConfig { } export interface PiStatus { + hostPlatform: "linux" | "macos" | "windows"; version?: string; ready: boolean; credentials: PiCredentialStatus; @@ -92,6 +93,9 @@ interface PiManagementDeps { } export function createPiManagement(config: AppConfig, deps: PiManagementDeps): PiManagementService { + const platform = config.hostPlatform ?? process.platform; + const hostPlatform = platform === "darwin" ? "macos" + : platform === "windows" || platform === "win32" ? "windows" : "linux"; const now = deps.now ?? (() => new Date()); const diagnostics: string[] = []; const addDiagnostic = (message: string): void => { @@ -169,11 +173,11 @@ export function createPiManagement(config: AppConfig, deps: PiManagementDeps): P try { const currentVersion = await version(); addDiagnostic("Pi version probe succeeded"); - return { version: currentVersion, ready: true, credentials, config: current, checkedAt }; + return { hostPlatform, version: currentVersion, ready: true, credentials, config: current, checkedAt }; } catch (error) { const message = stableMessage(error, "Pi runtime is unavailable"); addDiagnostic(message); - return { ready: false, credentials, config: current, checkedAt, message }; + return { hostPlatform, ready: false, credentials, config: current, checkedAt, message }; } }, diff --git a/backend/test/pi-management.test.ts b/backend/test/pi-management.test.ts index 52d1ff02..3535b27d 100644 --- a/backend/test/pi-management.test.ts +++ b/backend/test/pi-management.test.ts @@ -42,6 +42,7 @@ function configFor(settingsFile = join(mkdtempSync(join(tmpdir(), "tht-pi-manage SETTINGS_FILE: settingsFile, PI_BIN: "/usr/local/bin/pi", PI_MANAGEMENT_TIMEOUT_MS: "750", + THT_HOST_PLATFORM: "linux", }); } @@ -60,6 +61,16 @@ function successfulExec(calls: Array<{ command: string; args: string[]; timeout: }; } +test.each([ + ["linux", "linux"], ["darwin", "macos"], ["windows", "windows"], +])("reports installation host %s independently of the backend container OS", async (host, expected) => { + const service = createPiManagement(loadConfig({ THT_HOST_PLATFORM: host }), { + modelCatalog, execute: successfulExec([]), readSettings: () => ({ thinking: "medium" }), + credentialStatus: () => "missing", + }); + expect((await service.status()).hostPlatform).toBe(expected); +}); + // Catches a Pi executable that emits unexpected text or is invoked through a shell, which could // turn a version display into a command-injection or information-disclosure surface. test("status parses only a Pi version from a fixed execFile argument array", async () => { @@ -73,6 +84,7 @@ test("status parses only a Pi version from a fixed execFile argument array", asy }); await expect(service.status()).resolves.toEqual({ + hostPlatform: "linux", version: "0.80.3", ready: true, credentials: "missing", @@ -104,6 +116,7 @@ test.each(["present", "missing"] as const)( const status = await service.status(); expect(status).toEqual({ + hostPlatform: "linux", version: "0.80.3", ready: true, credentials, diff --git a/docs/general/pi-configuration.md b/docs/general/pi-configuration.md index 4c789289..9db489af 100644 --- a/docs/general/pi-configuration.md +++ b/docs/general/pi-configuration.md @@ -8,6 +8,19 @@ declare providers, model allowlists, defaults, embeddings, dimensions, or vector Do not edit `deploy/pi/models.json`, `deploy/pi/settings.json`, files under `generated/`, or provider/model environment defaults. Those former sources are retired. +## Host instructions in Administration + +The **Pi configuration** navigation button opens **Pi management**. Its Host maintenance +section selects the installation host's Linux, macOS, or Windows tab automatically; the +browser's operating system does not affect it. Selecting another tab is still possible. + +The host CLI includes `THT_HOST_PLATFORM` in the generated Compose projection using the OS +on which it runs. Generate projections on the destination host, not on another computer, +and do not edit generated files. Without this projection (older deployments or native +development), the backend reports its own OS; a Linux Docker container cannot discover +whether the physical host is macOS or Windows. Run the updated host CLI's normal +configuration reload on the destination installation to regenerate this information. + ## Minimal catalog ```yaml diff --git a/docs/reports/2026-09-12-admin-issues-28-31.md b/docs/reports/2026-09-12-admin-issues-28-31.md new file mode 100644 index 00000000..035cb3e9 --- /dev/null +++ b/docs/reports/2026-09-12-admin-issues-28-31.md @@ -0,0 +1,42 @@ +# Administration follow-up: Gitea #28–#31 + +Baseline approved by the user: `f52bf22e` on `codex/prototype-administration-pages`. +Existing Admin, Core, sessions, and unified-model work is outside this review diff. + +## Ticket acceptance + +- [#28](https://git.tylconsulting.it/mptyl/ThothII/issues/28): Workspace page has four + tabs, in order: Preparation, Specific actions, About, Authentication. Every area's + content remains vertically accessible, with scrolling where required. +- [#29](https://git.tylconsulting.it/mptyl/ThothII/issues/29): Database opens at the + existing database list instead of automatically editing a connection. Owner comment: + remove the “Workspace preparation” footer and restore the table's original available + space and margins. Tables and columns remain accessible through the original grid actions. +- [#30](https://git.tylconsulting.it/mptyl/ThothII/issues/30): Pi fills the available + height minus its margins, scrolls overflowing content, and initially opens the tab + for the installation OS (Linux on a Linux server, macOS or Windows otherwise). +- [#31](https://git.tylconsulting.it/mptyl/ThothII/issues/31): Keep “management” in page + titles, remove it from Admin buttons; use “Pi configuration” for Pi. + +## Scope and compatibility + +The global workspace/model gate, operation locks, original Core, session navigation, +and unsaved-change guards are retained. The list currently retains the existing global +workspace scope; an asynchronous question asks whether the user wants to broaden this +to all workspaces. No cross-workspace operations are introduced without that decision. + +Workspace tab panels stay mounted, preserving secret drafts and diagnostics. Pi platform +selection stays mounted across the two Pi sections and does not replace a manual choice +when status refreshes. OS information is projected by the host CLI, not inferred from +the browser. Older unprojected installations fall back to the backend runtime OS; see +the [operator guide](../general/pi-configuration.md#host-instructions-in-administration). + +## Verification + +TDD seams: AppShell navigation and public management-page behavior with MSW API fixtures; +the existing public Pi service and host projection contracts cover OS transport. +Red tests were observed before adding tabs, restoring list entry, selecting OS tabs, +projecting the host platform, and shortening navigation labels. + +Verification results and independent Standards/Spec reviews are recorded below when complete. +Docker has not been rebuilt for this follow-up; the read-only QA preview uses the changed source. diff --git a/frontend/e2e/database-management-layout.spec.ts b/frontend/e2e/database-management-layout.spec.ts index 3091747f..69e6a66f 100644 --- a/frontend/e2e/database-management-layout.spec.ts +++ b/frontend/e2e/database-management-layout.spec.ts @@ -439,12 +439,12 @@ test("context panels stay inside the manager and the Tables grid sits in a sideb await administration.click(); await expect(administration).toHaveAttribute("aria-expanded", "false"); - await expect(adminNavigationRail.getByRole("button", { name: "Database management", exact: true })).toHaveCount(0); + await expect(adminNavigationRail.getByRole("button", { name: "Database", exact: true })).toHaveCount(0); await administration.click(); await expect(administration).toHaveAttribute("aria-expanded", "true"); await adminNavigationRail - .getByRole("button", { name: "Database management", exact: true }) + .getByRole("button", { name: "Database", exact: true }) .click(); await expectCurrentNavigation(page, "Database management"); @@ -795,7 +795,7 @@ test("Workspace and Pi management share the centered work-area panel without cov await expectWorkAreaPanelGeometry(page, "Workspace management"); await expectWorkAreaPanelHeaderUsesPrimary(page, "Workspace management"); - const piTrigger = page.getByRole("button", { name: "Pi management", exact: true }); + const piTrigger = page.getByRole("button", { name: "Pi configuration", exact: true }); await piTrigger.click(); await expectCurrentNavigation(page, "Pi management"); await expect( @@ -812,7 +812,7 @@ test("Workspace and Pi management share the centered work-area panel without cov } await page.setViewportSize({ width: 720, height: 800 }); - await page.getByRole("button", { name: "Workspace management", exact: true }).click(); + await page.getByRole("button", { name: "Workspace", exact: true }).click(); await expectCurrentNavigation(page, "Workspace management"); const compactWorkArea = page.getByTestId("conversation-column"); const compactPanel = compactWorkArea.getByRole("dialog", { name: "Workspace management" }); diff --git a/frontend/e2e/memory-real.spec.ts b/frontend/e2e/memory-real.spec.ts index 29f02f7d..324f981a 100644 --- a/frontend/e2e/memory-real.spec.ts +++ b/frontend/e2e/memory-real.spec.ts @@ -112,7 +112,7 @@ LocalEvidenceArchive(root).initialize() }).allTextContents()).map(text => text.trim())).toEqual([ "Database management", "Memory management", "Evidence management", ]); - await page.getByRole("button", { name: "Memory management", exact: true }).click(); + await page.getByRole("button", { name: "Memory", exact: true }).click(); await page.screenshot({ path: "/private/tmp/thothii-m1-memory-opening.png", fullPage: true }); const memory = page.getByRole("main", { name: "Memory management" }); await expect(memory).toBeVisible(); @@ -157,9 +157,9 @@ finally: service.close() THT_PRINCIPAL_ISSUER: "test", THT_PRINCIPAL_SUBJECT: "admin", THT_PRINCIPAL_IS_ADMIN: "true" }); expect(JSON.parse(recalled)).toEqual([]); await expect(page.getByRole("button", { name: "New card", exact: true })).toBeEnabled(); - if (!await page.getByRole("button", { name: "Evidence management", exact: true }).isVisible()) + if (!await page.getByRole("button", { name: "Evidence", exact: true }).isVisible()) await page.getByRole("button", { name: "Administration", exact: true }).click(); - await page.getByRole("button", { name: "Evidence management", exact: true }).click(); + await page.getByRole("button", { name: "Evidence", exact: true }).click(); const evidence = page.getByRole("main", { name: "Evidence management" }); await expect(evidence).toBeVisible(); await evidence.getByRole("combobox", { name: /Workspace/ }).selectOption("fixture-workspace"); @@ -179,12 +179,12 @@ finally: service.close() await page.screenshot({ path: "/private/tmp/thothii-acceptance-evidence-mobile.png", fullPage: true }); await page.getByRole("button", { name: "Navigation", exact: true }).click(); const navigation = page.getByRole("dialog", { name: "Navigation", exact: true }); - await expect(navigation.getByRole("button", { name: "Memory management", exact: true })).toBeVisible(); + await expect(navigation.getByRole("button", { name: "Memory", exact: true })).toBeVisible(); await page.keyboard.press("Escape"); await expect(navigation).toBeHidden(); await expect(page.getByRole("button", { name: "Navigation", exact: true })).toBeFocused(); await page.getByRole("button", { name: "Navigation", exact: true }).click(); - await navigation.getByRole("button", { name: "Memory management", exact: true }).click(); + await navigation.getByRole("button", { name: "Memory", exact: true }).click(); await expect(navigation).toBeHidden(); const mobileMemory = page.getByRole("main", { name: "Memory management" }); await expect(mobileMemory).toBeVisible(); diff --git a/frontend/src/api/pi-management.ts b/frontend/src/api/pi-management.ts index b21a6b2d..25296b68 100644 --- a/frontend/src/api/pi-management.ts +++ b/frontend/src/api/pi-management.ts @@ -9,6 +9,7 @@ export interface PiInstallationConfig { } export interface PiManagementStatus { + hostPlatform?: "linux" | "macos" | "windows"; version?: string; ready: boolean; credentials: "present" | "missing"; diff --git a/frontend/src/shell/AppShell.administration.test.tsx b/frontend/src/shell/AppShell.administration.test.tsx index de619080..505bdba3 100644 --- a/frontend/src/shell/AppShell.administration.test.tsx +++ b/frontend/src/shell/AppShell.administration.test.tsx @@ -39,6 +39,17 @@ beforeEach(() => { }); afterEach(() => vi.restoreAllMocks()); +test("uses short Admin buttons while keeping management in page titles", async () => { + renderShell(); + await screen.findByRole("textbox", { name: /new question/i }); + await userEvent.click(screen.getByRole("button", { name: "Administration" })); + for (const [label, title] of [["Workspace", "Workspace"], ["Evidence", "Evidence"], ["Memory", "Memory"], ["Database", "Database"], ["Pi configuration", "Pi"]]) { + await userEvent.click(screen.getByRole("button", { name: label })); + expect(await screen.findByRole("main", { name: `${title} management` })).toBeVisible(); + expect(screen.queryByRole("button", { name: `${title} management` })).not.toBeInTheDocument(); + } +}); + test.each(["Workspace", "Evidence", "Memory", "Database", "Pi"])("reloads the %s route as a full page", async name => { window.history.replaceState({ portal: "kept" }, "", `/portal?lang=it&thoth_route=administration/${name.toLowerCase()}#host`); renderShell(); @@ -52,8 +63,8 @@ test("preserves host state and query parameters through Back and Forward", async window.history.replaceState({ portal: { view: "thoth" } }, "", "/portal?lang=it#host"); renderShell(); await userEvent.click(screen.getByRole("button", { name: "Administration" })); - await userEvent.click(screen.getByRole("button", { name: "Workspace management" })); - await userEvent.click(screen.getByRole("button", { name: "Pi management" })); + await userEvent.click(screen.getByRole("button", { name: "Workspace" })); + await userEvent.click(screen.getByRole("button", { name: "Pi configuration" })); act(() => window.history.back()); expect(await screen.findByRole("main", { name: "Workspace management" })).toBeVisible(); act(() => window.history.forward()); @@ -75,8 +86,8 @@ test.each(["workspace", "database", "memory", "evidence", "pi"])("denies a direc test("Back and Core navigation preserve a Memory draft until the editor cancels it", async () => { renderShell(); await userEvent.click(screen.getByRole("button", { name: "Administration" })); - await userEvent.click(screen.getByRole("button", { name: "Workspace management" })); - await userEvent.click(screen.getByRole("button", { name: "Memory management" })); + await userEvent.click(screen.getByRole("button", { name: "Workspace" })); + await userEvent.click(screen.getByRole("button", { name: "Memory" })); const page = await screen.findByRole("main", { name: "Memory management" }); await userEvent.click(await within(page).findByRole("button", { name: "New card" })); await userEvent.type(within(page).getByLabelText("Title"), "Keep this draft"); @@ -122,7 +133,7 @@ test("option A has one collapsible global context and preserves the Core draft a await userEvent.click(screen.getByRole("button", { name: "Done" })); expect(screen.queryByRole("combobox", { name: "Model" })).not.toBeInTheDocument(); await userEvent.click(screen.getByRole("button", { name: "Administration" })); - await userEvent.click(screen.getByRole("button", { name: "Memory management" })); + await userEvent.click(screen.getByRole("button", { name: "Memory" })); expect(await screen.findByRole("main", { name: "Memory management" })).toBeVisible(); expect(screen.queryByRole("textbox", { name: /new question/i })).not.toBeInTheDocument(); await userEvent.click(screen.getByRole("button", { name: "Return to session" })); diff --git a/frontend/src/shell/AppShell.auth.test.tsx b/frontend/src/shell/AppShell.auth.test.tsx index 000252ef..cc03cc58 100644 --- a/frontend/src/shell/AppShell.auth.test.tsx +++ b/frontend/src/shell/AppShell.auth.test.tsx @@ -52,7 +52,7 @@ describe("authenticated shell permissions", () => { renderShell({ subject: "admin", isAdmin: true, roles: ["admin"], permissions: ["session.use", "memory.manage", "database.manage"] }); await userEvent.click(await screen.findByRole("button", { name: "Administration" })); - await userEvent.click(screen.getByRole("button", { name: "Memory management" })); + await userEvent.click(screen.getByRole("button", { name: "Memory" })); const memory = screen.getByRole("main", { name: "Memory management" }); expect(memory).toBeVisible(); expect(within(memory).queryByRole("combobox", { name: "Workspace" })).not.toBeInTheDocument(); @@ -111,9 +111,9 @@ describe("authenticated shell permissions", () => { expect(await screen.findByRole("button", { name: "New session" })).toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Administration" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Database management" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Workspace management" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Pi management" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Database" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Workspace" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Pi configuration" })).not.toBeInTheDocument(); expect(screen.queryByRole("tab", { name: "All sessions" })).not.toBeInTheDocument(); }); @@ -137,12 +137,12 @@ describe("authenticated shell permissions", () => { expect(trigger).toHaveAttribute("aria-expanded", "true"); const panel = within(sessionNavigation).getByRole("region", { name: "Administration" }); - const database = within(panel).getByRole("button", { name: "Database management" }); - const memory = within(panel).getByRole("button", { name: "Memory management" }); - const evidence = within(panel).getByRole("button", { name: "Evidence management" }); + const database = within(panel).getByRole("button", { name: "Database" }); + const memory = within(panel).getByRole("button", { name: "Memory" }); + const evidence = within(panel).getByRole("button", { name: "Evidence" }); const separator = within(panel).getByRole("separator"); - const workspace = within(panel).getByRole("button", { name: "Workspace management" }); - const pi = within(panel).getByRole("button", { name: "Pi management" }); + const workspace = within(panel).getByRole("button", { name: "Workspace" }); + const pi = within(panel).getByRole("button", { name: "Pi configuration" }); expect(panel.children[0]).toBe(database); expect(panel.children[1]).toBe(memory); expect(panel.children[2]).toBe(evidence); @@ -156,7 +156,7 @@ describe("authenticated shell permissions", () => { await user.keyboard(" "); expect(trigger).toHaveAttribute("aria-expanded", "false"); expect(within(sessionNavigation).queryByRole("region", { name: "Administration" })).not.toBeInTheDocument(); - expect(within(sessionNavigation).queryByRole("button", { name: "Database management" })).not.toBeInTheDocument(); + expect(within(sessionNavigation).queryByRole("button", { name: "Database" })).not.toBeInTheDocument(); }); test("keeps identity but hides logout outside local authentication", async () => { @@ -277,8 +277,8 @@ describe("authenticated shell permissions", () => { act(() => clearAuthState()); expect(screen.queryByRole("button", { name: "Administration" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Workspace management" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Pi management" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Workspace" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Pi configuration" })).not.toBeInTheDocument(); expect(screen.queryByRole("tab", { name: "All sessions" })).not.toBeInTheDocument(); }); diff --git a/frontend/src/shell/AppShell.database-management.test.tsx b/frontend/src/shell/AppShell.database-management.test.tsx index b2a9a56f..50d457df 100644 --- a/frontend/src/shell/AppShell.database-management.test.tsx +++ b/frontend/src/shell/AppShell.database-management.test.tsx @@ -107,7 +107,7 @@ test("starts on the core activity after a hard refresh even if metadata manageme const sessionNavigation = screen.getByRole("complementary", { name: "Session navigation" }); expect(within(sessionNavigation).getByRole("button", { name: "New session" })) .toHaveAttribute("aria-current", "page"); - expect(within(sessionNavigation).getByRole("button", { name: "Database management" })) + expect(within(sessionNavigation).getByRole("button", { name: "Database" })) .not.toHaveAttribute("aria-current"); expect(screen.queryByRole("main", { name: "Database management" })).not.toBeInTheDocument(); }); @@ -119,7 +119,7 @@ test("replaces the core conversation while keeping the session navigation", asyn const conversationColumn = screen.getByTestId("conversation-column"); const sessionNavigation = screen.getByRole("complementary", { name: "Session navigation" }); const newSession = within(sessionNavigation).getByRole("button", { name: "New session" }); - const databaseManagement = within(sessionNavigation).getByRole("button", { name: "Database management" }); + const databaseManagement = within(sessionNavigation).getByRole("button", { name: "Database" }); expect(newSession).toHaveAttribute("aria-current", "page"); expect(newSession).toHaveAttribute("data-navigation-state", "current"); @@ -178,7 +178,7 @@ test("keeps a live core session connected while returning from database manageme await waitFor(() => expect(FakeEventSource.instances).toHaveLength(1)); const source = FakeEventSource.instances[0]; - await userEvent.click(screen.getByRole("button", { name: "Database management" })); + await userEvent.click(screen.getByRole("button", { name: "Database" })); expect(screen.getByRole("main", { name: "Database management" })).toBeVisible(); expect(source.closed).toBe(false); @@ -210,9 +210,9 @@ test("hides the complete administrative navigation from a regular user", () => { expect(screen.getByRole("button", { name: "New session" })).toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Administration" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Workspace management" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Database management" })).not.toBeInTheDocument(); - expect(screen.queryByRole("button", { name: "Pi management" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Workspace" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Database" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Pi configuration" })).not.toBeInTheDocument(); }); test("requires saving or cancelling a dirty database form before leaving", async () => { @@ -250,7 +250,8 @@ test("requires saving or cancelling a dirty database form before leaving", async renderShell(); await expandAdministration(); - await userEvent.click(screen.getByRole("button", { name: "Database management" })); + await userEvent.click(screen.getByRole("button", { name: "Database" })); + await userEvent.click(await screen.findByRole("button", { name: "Edit Policlinico San Donato" })); const schema = await screen.findByLabelText("Schema"); await userEvent.clear(schema); await userEvent.type(schema, "reporting"); diff --git a/frontend/src/shell/AppShell.new-session.test.tsx b/frontend/src/shell/AppShell.new-session.test.tsx index e21b2429..d0df2552 100644 --- a/frontend/src/shell/AppShell.new-session.test.tsx +++ b/frontend/src/shell/AppShell.new-session.test.tsx @@ -204,13 +204,13 @@ test("opens Workspace management from the right sidebar without interrupting the renderShell(); await userEvent.click(screen.getByRole("button", { name: "Administration" })); - await userEvent.click(screen.getByRole("button", { name: "Workspace management" })); + await userEvent.click(screen.getByRole("button", { name: "Workspace" })); expect(await screen.findByRole("heading", { name: "Workspace management" })).toBeVisible(); const dialog = screen.getByRole("main", { name: "Workspace management" }); const workArea = screen.getByTestId("conversation-column"); const sessionNavigation = screen.getByRole("complementary", { name: "Session navigation" }); - const workspaceManagement = within(sessionNavigation).getByRole("button", { name: "Workspace management" }); + const workspaceManagement = within(sessionNavigation).getByRole("button", { name: "Workspace" }); const newSession = within(sessionNavigation).getByRole("button", { name: "New session" }); expect(workArea).toContainElement(dialog); expect(sessionNavigation).not.toContainElement(dialog); diff --git a/frontend/src/shell/AppShell.session-mgmt.test.tsx b/frontend/src/shell/AppShell.session-mgmt.test.tsx index a4d69186..388e3bcc 100644 --- a/frontend/src/shell/AppShell.session-mgmt.test.tsx +++ b/frontend/src/shell/AppShell.session-mgmt.test.tsx @@ -122,12 +122,12 @@ test("Pi management preserves the open session summary and the model activity ti await user.click(await screen.findByText("Attiva uno")); expect(await screen.findByRole("complementary", { name: "Session summary" })).toHaveTextContent("Domanda originale"); await user.click(screen.getByRole("button", { name: "Administration" })); - await user.click(screen.getByRole("button", { name: "Pi management" })); + await user.click(screen.getByRole("button", { name: "Pi configuration" })); expect(await screen.findByRole("heading", { name: "Pi management" })).toBeVisible(); const dialog = screen.getByRole("main", { name: "Pi management" }); const workArea = screen.getByTestId("conversation-column"); const sessionNavigation = screen.getByRole("complementary", { name: "Session navigation" }); - const piManagement = within(sessionNavigation).getByRole("button", { name: "Pi management" }); + const piManagement = within(sessionNavigation).getByRole("button", { name: "Pi configuration" }); const newSession = within(sessionNavigation).getByRole("button", { name: "New session" }); expect(workArea).toContainElement(dialog); expect(sessionNavigation).not.toContainElement(dialog); @@ -151,7 +151,7 @@ test("Pi management preserves the open session summary and the model activity ti await user.click(screen.getByRole("button", { name: "Resume" })); await screen.findByRole("button", { name: "Show model activity" }); act(() => useSessionStore.getState().setLastUserEntry({ kind: "input", text: "Preserved activity" })); - await user.click(screen.getByRole("button", { name: "Pi management" })); + await user.click(screen.getByRole("button", { name: "Pi configuration" })); await user.click(await screen.findByRole("button", { name: "Return to session" })); await waitFor(() => expect(screen.queryByRole("heading", { name: "Pi management" })).not.toBeInTheDocument()); await user.click(screen.getByRole("button", { name: "Show model activity" })); diff --git a/frontend/src/shell/AppShell.tsx b/frontend/src/shell/AppShell.tsx index 193befbb..5a96c559 100644 --- a/frontend/src/shell/AppShell.tsx +++ b/frontend/src/shell/AppShell.tsx @@ -999,7 +999,7 @@ export function AppShell({ canLogout }: AppShellProps) { navigate({ surface: "database-management" }, true); }} > - Database management + Database + >Memory + >Evidence