From 49307a16e343be2dc925b74cd6d76bd9a4181312 Mon Sep 17 00:00:00 2001
From: besfeng23 <136943241+besfeng23@users.noreply.github.com>
Date: Fri, 26 Jun 2026 15:08:30 +0800
Subject: [PATCH] Fix Phase 3D verification gate checks
---
app/admin/memory/verification/page.tsx | 46 ++++++++
docs/phase-3d-production-verification.md | 83 ++++++++++++++
.../admin-memory-route-guard-contract.ts | 31 +++++
.../admin-memory-verification-loader.ts | 96 ++++++++++++++++
tests/unit/admin-memory-verification.test.ts | 107 ++++++++++++++++++
5 files changed, 363 insertions(+)
create mode 100644 app/admin/memory/verification/page.tsx
create mode 100644 docs/phase-3d-production-verification.md
create mode 100644 lib/services/admin-memory-route-guard-contract.ts
create mode 100644 lib/services/admin-memory-verification-loader.ts
create mode 100644 tests/unit/admin-memory-verification.test.ts
diff --git a/app/admin/memory/verification/page.tsx b/app/admin/memory/verification/page.tsx
new file mode 100644
index 0000000..bc8558d
--- /dev/null
+++ b/app/admin/memory/verification/page.tsx
@@ -0,0 +1,46 @@
+import Link from "next/link";
+import { AppShell } from "@/components/layout/app-shell";
+import { PageHeader } from "@/components/ui/page-header";
+import { SectionCard } from "@/components/ui/section-card";
+import { resolvePandoraServerSession, createRepositoryContextFromPandoraSession } from "@/lib/auth/pandora-server-session-resolver";
+import { createSupabaseServerClient } from "@/lib/supabase/server";
+import { SupabasePersistedMemoryReadRepository } from "@/lib/db/supabase-persisted-memory-read-repository";
+import { resolvePandoraRuntimeSafetyConfig } from "@/lib/config/pandora-runtime-safety-config";
+import { loadAdminMemoryVerification, type VerificationLine } from "@/lib/services/admin-memory-verification-loader";
+
+export const dynamic = "force-dynamic";
+
+const Badge = ({ line }: Readonly<{ line: VerificationLine }>) =>
{line.label}: {line.status} — {line.detail};
+
+export default async function AdminMemoryVerificationPage() {
+ const namespace = "real_life";
+ const session = await resolvePandoraServerSession();
+ const context = createRepositoryContextFromPandoraSession({ sessionResult: session, namespace });
+ const runtime = resolvePandoraRuntimeSafetyConfig();
+ const supabase = await createSupabaseServerClient();
+ const dto = await loadAdminMemoryVerification({ session, context: context.ok ? context.context : { namespace }, repository: new SupabasePersistedMemoryReadRepository(supabase as never), runtime });
+ const loginPath = `/auth/login?next=${encodeURIComponent("/admin/memory/verification")}`;
+ return (
+
+
+ {!session.ok ? Start operator session : null}
+
+
+
+ {dto.vercelEnvProof.map((line) => )}
+ {dto.checklist.map((line) => )}
+
+ {dto.guardExpectations.map((guard) => - {guard.route}: authenticated={String(guard.authenticatedSupabaseSessionRequired)}, adminOnly={String(guard.adminOnly)}, readOnly={String(guard.readOnly)}, namespaceScoped={String(guard.namespaceScoped)}, serverDerivedUserOnly={String(guard.serverDerivedUserOnly)}, publicReadAllowed={String(guard.publicReadAllowed)}, serviceRoleAllowed={String(guard.serviceRoleAllowed)}, mutationAllowed={String(guard.mutationAllowed)}
)}
+
+
+ );
+}
diff --git a/docs/phase-3d-production-verification.md b/docs/phase-3d-production-verification.md
new file mode 100644
index 0000000..d0c1802
--- /dev/null
+++ b/docs/phase-3d-production-verification.md
@@ -0,0 +1,83 @@
+# Phase 3D production verification
+
+Phase 3D adds a read-only production verification console for closure hardening. It does not enable public memory reads, public persistence, unsafe writes, model calls, embeddings, semantic retrieval, GPT Actions, or MCP.
+
+## Exact production URLs
+
+Replace `` with the deployed Vercel host.
+
+- `https:///admin/memory/verification`
+- `https:///admin/memory/browser?namespace=real_life`
+- `https:///admin/memory/audit?namespace=real_life`
+- `https:///memory/browser`
+
+## Manual login test steps
+
+1. Open `https:///admin/memory/verification` in a private browser window.
+2. Confirm unauthenticated access shows an authentication-required/login state and no persisted memory rows.
+3. Log in with the reviewed Supabase operator account.
+4. Re-open `/admin/memory/verification` and confirm the safety summary loads.
+5. Open `/admin/memory/browser?namespace=real_life` and `/admin/memory/audit?namespace=real_life` from the same authenticated session.
+
+## Expected auth behavior
+
+- Admin memory routes require a Supabase-authenticated operator session.
+- User identity must be server-derived from the session.
+- Client-supplied `user_id`, `userId`, `client_user_id`, or `clientUserId` values must not be accepted.
+- Unauthenticated sessions must see login/auth-required states, not memory rows.
+
+## Expected public redirect behavior
+
+- `https:///memory/browser` redirects to `/admin/memory/browser?namespace=real_life`.
+- The public route must not instantiate a Supabase read repository.
+- The public route must not render persisted rows, source proof, patch proof, or audit proof.
+- No public proof or audit route should exist for memory browser data.
+
+## Expected read-only behavior
+
+The admin browser, audit viewer, and verification route must remain read-only. They must not expose edit, delete, persist, execute, patch-write, model, embedding, semantic retrieval, GPT Actions, MCP, or public-read controls.
+
+## Unsafe gate env variables to inspect
+
+These should be absent or not set to `true` unless a separate reviewed production operation explicitly requires them:
+
+- `PANDORA_ENABLE_MEMORY_INGEST_PRODUCTION_WRITES`
+- `PANDORA_ENABLE_APPROVED_REVIEW_MEMORY_PERSISTENCE`
+- `PANDORA_ENABLE_PUBLIC_MEMORY_READ`
+- `PANDORA_ENABLE_PUBLIC_MEMORY_PERSISTENCE`
+- `PANDORA_ENABLE_MODEL_CALLS`
+- `PANDORA_ENABLE_EMBEDDINGS`
+- `PANDORA_ENABLE_SEMANTIC_RETRIEVAL`
+- `PANDORA_ENABLE_GPT_ACTIONS`
+- `PANDORA_ENABLE_MCP`
+
+Useful proof-only values:
+
+- `VERCEL_GIT_COMMIT_SHA` or `GIT_COMMIT_SHA`
+- `VERCEL_ENV`
+- `VERCEL_URL`
+- `PANDORA_SKILLS_COMMIT_SHA`
+- `PANDORA_SKILLS_PROOF_STATUS`
+
+## Supabase/RLS proof expectations
+
+- `PANDORA_ENABLE_PERSISTED_MEMORY_READ` is the read-availability gate; verification must report disabled when it is false and must not force a database read proof.
+- Reads are performed through the authenticated Supabase server client.
+- Reads are scoped by server-derived user ID and namespace.
+- Missing tables, missing proof fields, or RLS denial should display `unavailable` or `blocked`; the page must not crash.
+- Browser proof should include source and patch fields where available.
+- Audit proof should include audit route availability or an explicit unavailable state.
+
+## Close/no-close decision criteria
+
+Phase 3D can close after deployment only if:
+
+- The deployed commit SHA is visible or otherwise captured in release proof.
+- `/admin/memory/verification` loads for an authenticated operator.
+- `/admin/memory/browser?namespace=real_life` and `/admin/memory/audit?namespace=real_life` remain authenticated and read-only.
+- `/memory/browser` redirects to the admin browser and does not render rows publicly.
+- The persisted-memory read gate is enabled for the production proof pass and Supabase/RLS read proof is available.
+- Public reads and unsafe mutation/integration gates are disabled.
+- Source, patch, audit, and skills commit proof are available or explicitly recorded as not configured with a follow-up decision.
+
+Do not close if the persisted-memory read gate is disabled for the proof pass, public reads are enabled, unsafe mutation/integration gates are enabled without review, admin routes expose mutation controls, RLS blocks expected operator reads without explanation, or deployed proof cannot be tied to a commit.
diff --git a/lib/services/admin-memory-route-guard-contract.ts b/lib/services/admin-memory-route-guard-contract.ts
new file mode 100644
index 0000000..ebeab00
--- /dev/null
+++ b/lib/services/admin-memory-route-guard-contract.ts
@@ -0,0 +1,31 @@
+export type AdminMemoryRouteGuardExpectation = {
+ route: "/admin/memory/browser" | "/admin/memory/audit" | "/admin/memory/verification";
+ authenticatedSupabaseSessionRequired: true;
+ adminOnly: true;
+ readOnly: true;
+ namespaceScoped: true;
+ serverDerivedUserOnly: true;
+ publicReadAllowed: false;
+ serviceRoleAllowed: false;
+ mutationAllowed: false;
+};
+
+export const adminMemoryRouteGuardExpectations: AdminMemoryRouteGuardExpectation[] = [
+ "/admin/memory/browser",
+ "/admin/memory/audit",
+ "/admin/memory/verification",
+].map((route) => ({
+ route: route as AdminMemoryRouteGuardExpectation["route"],
+ authenticatedSupabaseSessionRequired: true,
+ adminOnly: true,
+ readOnly: true,
+ namespaceScoped: true,
+ serverDerivedUserOnly: true,
+ publicReadAllowed: false,
+ serviceRoleAllowed: false,
+ mutationAllowed: false,
+}));
+
+export function getAdminMemoryRouteGuardExpectation(route: AdminMemoryRouteGuardExpectation["route"]) {
+ return adminMemoryRouteGuardExpectations.find((item) => item.route === route);
+}
diff --git a/lib/services/admin-memory-verification-loader.ts b/lib/services/admin-memory-verification-loader.ts
new file mode 100644
index 0000000..273a5a1
--- /dev/null
+++ b/lib/services/admin-memory-verification-loader.ts
@@ -0,0 +1,96 @@
+import { resolvePandoraRuntimeSafetyConfig, type PandoraRuntimeGate, type PandoraRuntimeSafetyConfigResult } from "@/lib/config/pandora-runtime-safety-config";
+import { loadPersistedMemoryBrowserView } from "@/lib/services/persisted-memory-browser-loader";
+import type { PersistedMemoryReadRepository } from "@/lib/db/persisted-memory-read-repository-contract";
+import type { PersistedMemoryReadContext } from "@/lib/services/persisted-memory-read-contract";
+import type { PandoraServerSessionResult } from "@/lib/auth/pandora-server-session-contract";
+import { adminMemoryRouteGuardExpectations } from "@/lib/services/admin-memory-route-guard-contract";
+
+export type VerificationStatus = "available" | "not configured" | "unavailable" | "blocked" | "disabled";
+export type VerificationLine = { label: string; status: VerificationStatus; detail: string };
+export type AdminMemoryVerificationDto = {
+ readOnly: true;
+ route: "/admin/memory/verification";
+ commitSha: VerificationLine;
+ vercelEnvProof: VerificationLine[];
+ persistedMemoryReadGateStatus: VerificationLine;
+ supabaseReadAvailability: VerificationLine;
+ memoryBrowserRouteStatus: VerificationLine;
+ auditRouteStatus: VerificationLine;
+ unsafeGateStatus: VerificationLine & { enabledDangerousGates: string[] };
+ publicReadStatus: VerificationLine;
+ recommendation: VerificationLine & { closeRecommended: boolean };
+ checklist: VerificationLine[];
+ guardExpectations: typeof adminMemoryRouteGuardExpectations;
+};
+
+const dangerousGates: PandoraRuntimeGate[] = [
+ "adminPersistenceConsoleEnabled",
+ "approvedReviewPersistenceEnabled",
+ "operatorQaFlowEnabled",
+ "ingestProductionWriteEnabled",
+ "publicMemoryReadEnabled",
+ "publicMemoryPersistenceEnabled",
+ "modelCallsEnabled",
+ "embeddingsEnabled",
+ "semanticRetrievalEnabled",
+ "gptActionsEnabled",
+ "mcpEnabled",
+];
+
+const value = (v?: string) => v && v.trim() ? v : undefined;
+const configured = (label: string, envVar: string, env: Partial): VerificationLine => {
+ const v = value(env[envVar]);
+ return { label, status: v ? "available" : "not configured", detail: v ? `${envVar}=configured` : `${envVar}=not configured` };
+};
+const enabledDangerousGates = (runtime: PandoraRuntimeSafetyConfigResult) => dangerousGates.filter((gate) => runtime.config[gate]).map((gate) => `${runtime.gates[gate].envVar} (${gate})`);
+
+export async function loadAdminMemoryVerification(input: { session: PandoraServerSessionResult; context?: Partial; repository?: PersistedMemoryReadRepository; runtime?: PandoraRuntimeSafetyConfigResult; env?: Partial }): Promise {
+ const env = input.env ?? process.env;
+ const runtime = input.runtime ?? resolvePandoraRuntimeSafetyConfig(env);
+ const commit = value(env.VERCEL_GIT_COMMIT_SHA) ?? value(env.GIT_COMMIT_SHA) ?? value(env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA);
+ const persistedReadEnabled = runtime.config.persistedMemoryReadEnabled;
+ const persistedMemoryReadGateStatus: VerificationLine = { label: "Persisted memory read gate", status: persistedReadEnabled ? "available" : "disabled", detail: `${runtime.gates.persistedMemoryReadEnabled.envVar}=${persistedReadEnabled ? "true" : "false"}` };
+ const browser = await loadPersistedMemoryBrowserView({ authenticated: input.session.ok, context: input.context, repository: input.repository, runtime, filters: { namespace: input.context?.namespace ?? "real_life" } });
+ const authBlocked = !input.session.ok;
+ const dbBlocked = browser.blockers.find((b) => b.code !== "auth_required");
+ const readGateBlocked = browser.blockers.find((b) => b.message.toLowerCase().includes("read gate is disabled"));
+ const supabaseReadAvailability: VerificationLine = authBlocked
+ ? { label: "Supabase read availability", status: "blocked", detail: "Authenticated admin/operator session is required before reading through RLS." }
+ : !persistedReadEnabled || readGateBlocked
+ ? { label: "Supabase read availability", status: "disabled", detail: "Persisted-memory read gate is disabled; no read proof was forced." }
+ : dbBlocked
+ ? { label: "Supabase read availability", status: "unavailable", detail: dbBlocked.message }
+ : { label: "Supabase read availability", status: "available", detail: browser.empty ? "Read path returned no rows for this user/namespace." : "Read path returned user-scoped rows." };
+ const enabledUnsafe = enabledDangerousGates(runtime);
+ const unsafe = enabledUnsafe.length > 0;
+ const publicRead = runtime.config.publicMemoryReadEnabled;
+ const browserOk = input.session.ok && persistedReadEnabled ? "available" : "blocked";
+ const auditOk = input.session.ok && persistedReadEnabled ? "available" : "blocked";
+ const readProofAvailable = supabaseReadAvailability.status === "available";
+ const closeRecommended = Boolean(commit) && !unsafe && input.session.ok && readProofAvailable;
+ return {
+ readOnly: true,
+ route: "/admin/memory/verification",
+ commitSha: { label: "Latest deployed commit SHA", status: commit ? "available" : "not configured", detail: commit ?? "VERCEL_GIT_COMMIT_SHA/GIT_COMMIT_SHA not configured" },
+ vercelEnvProof: [configured("Vercel environment", "VERCEL_ENV", env), configured("Vercel URL", "VERCEL_URL", env), configured("Skills commit proof", "PANDORA_SKILLS_COMMIT_SHA", env), configured("Skills proof status", "PANDORA_SKILLS_PROOF_STATUS", env)],
+ persistedMemoryReadGateStatus,
+ supabaseReadAvailability,
+ memoryBrowserRouteStatus: { label: "Memory browser route", status: browserOk, detail: persistedReadEnabled ? "/admin/memory/browser is authenticated, namespace-scoped, and read-only by contract." : "/admin/memory/browser remains gated because persisted-memory reads are disabled." },
+ auditRouteStatus: { label: "Audit route", status: auditOk, detail: persistedReadEnabled ? "/admin/memory/audit is authenticated, namespace-scoped, and read-only by contract." : "/admin/memory/audit remains gated because persisted-memory reads are disabled." },
+ unsafeGateStatus: { label: "Unsafe mutation/integration gates", status: unsafe ? "blocked" : "disabled", detail: unsafe ? `Enabled dangerous gates: ${enabledUnsafe.join(", ")}. Do not close without review.` : "All dangerous mutation/integration gates are disabled.", enabledDangerousGates: enabledUnsafe },
+ publicReadStatus: { label: "Public read status", status: publicRead ? "blocked" : "disabled", detail: publicRead ? "PANDORA_ENABLE_PUBLIC_MEMORY_READ=true; public reads are not closure-safe." : "Public memory reads are disabled; /memory/browser redirects to the admin route." },
+ recommendation: { label: "Final recommendation", status: closeRecommended ? "available" : "blocked", detail: closeRecommended ? "Close after deployed manual checklist passes." : "Do not close until commit proof, auth/session, persisted read proof, and dangerous gate blockers are resolved.", closeRecommended },
+ checklist: [
+ { label: "Authenticated browser", status: input.session.ok && persistedReadEnabled ? "available" : "blocked", detail: "Open /admin/memory/browser?namespace=real_life while logged in." },
+ { label: "Unauthenticated admin denial/login", status: "blocked", detail: "In a private browser, admin routes must show login/auth-required and no rows." },
+ { label: "Public redirect", status: "disabled", detail: "/memory/browser must redirect to /admin/memory/browser?namespace=real_life." },
+ { label: "Read-only behavior", status: "available", detail: "No edit, delete, persist, execute, model, embedding, retrieval, GPT Actions, or MCP controls." },
+ { label: "Persisted read gate", status: persistedMemoryReadGateStatus.status, detail: persistedMemoryReadGateStatus.detail },
+ { label: "Disabled unsafe gates", status: unsafe ? "blocked" : "disabled", detail: unsafe ? `Review enabled dangerous gates: ${enabledUnsafe.join(", ")}.` : "All dangerous gates are disabled." },
+ { label: "Audit proof availability", status: auditOk, detail: "Audit route should show audit rows or an explicit unavailable state." },
+ { label: "Source/patch proof availability", status: supabaseReadAvailability.status, detail: "Browser should show source and patch proof fields or an explicit unavailable state." },
+ { label: "Skills commit proof availability", status: value(env.PANDORA_SKILLS_COMMIT_SHA) ? "available" : "not configured", detail: "PANDORA_SKILLS_COMMIT_SHA should be configured for closure proof." },
+ ],
+ guardExpectations: adminMemoryRouteGuardExpectations,
+ };
+}
diff --git a/tests/unit/admin-memory-verification.test.ts b/tests/unit/admin-memory-verification.test.ts
new file mode 100644
index 0000000..ab66d59
--- /dev/null
+++ b/tests/unit/admin-memory-verification.test.ts
@@ -0,0 +1,107 @@
+import { readFileSync, existsSync } from "node:fs";
+import { join } from "node:path";
+import { describe, expect, it } from "vitest";
+import { loadAdminMemoryVerification } from "@/lib/services/admin-memory-verification-loader";
+import { adminMemoryRouteGuardExpectations } from "@/lib/services/admin-memory-route-guard-contract";
+import type { PersistedMemoryReadRepository } from "@/lib/db/persisted-memory-read-repository-contract";
+
+const root = process.cwd();
+const okList = { ok: true as const, items: [], page: 1, pageSize: 25 };
+let readCalls = 0;
+const repo: PersistedMemoryReadRepository = {
+ listMemoryItems: async () => { readCalls += 1; return okList; },
+ listMemorySources: async () => okList,
+ listMemoryPatches: async () => okList,
+ listMemoryAuditEvents: async () => okList,
+ getMemoryItemDetail: async () => ({ ok: false, blocker: { code: "not_found", message: "not found" } }),
+ getMemorySourceDetail: async () => ({ ok: false, blocker: { code: "not_found", message: "not found" } }),
+};
+const session = { ok: true as const, session: { userId: "u1", authenticated: true, allowedNamespaces: ["real_life"], serverDerivedOnly: true, clientUserIdAccepted: false, serviceRoleUsed: false, publicReadAllowed: false, publicPersistenceAllowed: false }, blockers: [] };
+const safeEnv = { VERCEL_GIT_COMMIT_SHA: "abc123", PANDORA_SKILLS_COMMIT_SHA: "skills123", PANDORA_ENABLE_PERSISTED_MEMORY_READ: "true" };
+
+describe("admin memory verification safety", () => {
+ it("builds a closure safety summary with public reads and unsafe gates disabled", async () => {
+ const dto = await loadAdminMemoryVerification({ session, context: { userId: "u1", namespace: "real_life" }, repository: repo, env: safeEnv });
+ expect(dto.readOnly).toBe(true);
+ expect(dto.persistedMemoryReadGateStatus.status).toBe("available");
+ expect(dto.publicReadStatus.status).toBe("disabled");
+ expect(dto.unsafeGateStatus.status).toBe("disabled");
+ expect(dto.unsafeGateStatus.enabledDangerousGates).toEqual([]);
+ expect(dto.recommendation.closeRecommended).toBe(true);
+ expect(dto.checklist.map((i) => i.label)).toEqual(expect.arrayContaining(["Authenticated browser", "Public redirect", "Read-only behavior", "Persisted read gate", "Audit proof availability", "Source/patch proof availability", "Skills commit proof availability"]));
+ });
+
+ it("does not override persistedMemoryReadEnabled when the deployed gate is false", async () => {
+ readCalls = 0;
+ const dto = await loadAdminMemoryVerification({ session, context: { userId: "u1", namespace: "real_life" }, repository: repo, env: { VERCEL_GIT_COMMIT_SHA: "abc123" } });
+ expect(dto.persistedMemoryReadGateStatus.status).toBe("disabled");
+ expect(dto.supabaseReadAvailability.status).toBe("disabled");
+ expect(dto.supabaseReadAvailability.detail).toContain("no read proof was forced");
+ expect(dto.recommendation.closeRecommended).toBe(false);
+ expect(readCalls).toBe(0);
+ });
+
+ it("blocks closure when public reads or unsafe writes are enabled and lists the enabled gates", async () => {
+ const dto = await loadAdminMemoryVerification({ session, context: { userId: "u1", namespace: "real_life" }, repository: repo, env: { ...safeEnv, PANDORA_ENABLE_PUBLIC_MEMORY_READ: "true", PANDORA_ENABLE_MEMORY_INGEST_PRODUCTION_WRITES: "true" } });
+ expect(dto.publicReadStatus.status).toBe("blocked");
+ expect(dto.unsafeGateStatus.status).toBe("blocked");
+ expect(dto.unsafeGateStatus.detail).toContain("PANDORA_ENABLE_PUBLIC_MEMORY_READ");
+ expect(dto.unsafeGateStatus.detail).toContain("PANDORA_ENABLE_MEMORY_INGEST_PRODUCTION_WRITES");
+ expect(dto.recommendation.closeRecommended).toBe(false);
+ });
+
+ it("blocks closure for model, embedding, retrieval, GPT Actions, and MCP gates", async () => {
+ const dto = await loadAdminMemoryVerification({ session, context: { userId: "u1", namespace: "real_life" }, repository: repo, env: { ...safeEnv, PANDORA_ENABLE_MODEL_CALLS: "true", PANDORA_ENABLE_EMBEDDINGS: "true", PANDORA_ENABLE_SEMANTIC_RETRIEVAL: "true", PANDORA_ENABLE_GPT_ACTIONS: "true", PANDORA_ENABLE_MCP: "true" } });
+ expect(dto.unsafeGateStatus.status).toBe("blocked");
+ expect(dto.unsafeGateStatus.enabledDangerousGates.join(" ")).toEqual(expect.stringContaining("PANDORA_ENABLE_MODEL_CALLS"));
+ expect(dto.unsafeGateStatus.enabledDangerousGates.join(" ")).toEqual(expect.stringContaining("PANDORA_ENABLE_EMBEDDINGS"));
+ expect(dto.unsafeGateStatus.enabledDangerousGates.join(" ")).toEqual(expect.stringContaining("PANDORA_ENABLE_SEMANTIC_RETRIEVAL"));
+ expect(dto.unsafeGateStatus.enabledDangerousGates.join(" ")).toEqual(expect.stringContaining("PANDORA_ENABLE_GPT_ACTIONS"));
+ expect(dto.unsafeGateStatus.enabledDangerousGates.join(" ")).toEqual(expect.stringContaining("PANDORA_ENABLE_MCP"));
+ expect(dto.recommendation.closeRecommended).toBe(false);
+ });
+
+ it("blocks closure when admin persistence console, approved persistence, operator QA, or public persistence gates are enabled", async () => {
+ const dto = await loadAdminMemoryVerification({ session, context: { userId: "u1", namespace: "real_life" }, repository: repo, env: { ...safeEnv, PANDORA_ENABLE_ADMIN_PERSISTENCE_CONSOLE: "true", PANDORA_ENABLE_APPROVED_REVIEW_MEMORY_PERSISTENCE: "true", PANDORA_ENABLE_OPERATOR_MEMORY_QA_FLOW: "true", PANDORA_ENABLE_PUBLIC_MEMORY_PERSISTENCE: "true" } });
+ expect(dto.unsafeGateStatus.status).toBe("blocked");
+ expect(dto.unsafeGateStatus.enabledDangerousGates.join(" ")).toEqual(expect.stringContaining("PANDORA_ENABLE_ADMIN_PERSISTENCE_CONSOLE"));
+ expect(dto.unsafeGateStatus.enabledDangerousGates.join(" ")).toEqual(expect.stringContaining("PANDORA_ENABLE_APPROVED_REVIEW_MEMORY_PERSISTENCE"));
+ expect(dto.unsafeGateStatus.enabledDangerousGates.join(" ")).toEqual(expect.stringContaining("PANDORA_ENABLE_OPERATOR_MEMORY_QA_FLOW"));
+ expect(dto.unsafeGateStatus.enabledDangerousGates.join(" ")).toEqual(expect.stringContaining("PANDORA_ENABLE_PUBLIC_MEMORY_PERSISTENCE"));
+ expect(dto.recommendation.closeRecommended).toBe(false);
+ });
+
+ it("documents consistent read-only route guard expectations", () => {
+ expect(adminMemoryRouteGuardExpectations).toHaveLength(3);
+ for (const guard of adminMemoryRouteGuardExpectations) {
+ expect(guard.authenticatedSupabaseSessionRequired).toBe(true);
+ expect(guard.adminOnly).toBe(true);
+ expect(guard.readOnly).toBe(true);
+ expect(guard.namespaceScoped).toBe(true);
+ expect(guard.serverDerivedUserOnly).toBe(true);
+ expect(guard.publicReadAllowed).toBe(false);
+ expect(guard.serviceRoleAllowed).toBe(false);
+ expect(guard.mutationAllowed).toBe(false);
+ }
+ });
+
+ it("keeps public route as redirect-only and avoids public proof/audit routes", () => {
+ const publicPage = readFileSync(join(root, "app/memory/browser/page.tsx"), "utf8");
+ expect(publicPage).toContain('redirect("/admin/memory/browser?namespace=real_life")');
+ expect(publicPage).not.toMatch(/SupabasePersistedMemoryReadRepository|loadPersistedMemoryBrowserView|memory_items|audit_logs|listMemory/i);
+ expect(existsSync(join(root, "app/memory/audit/page.tsx"))).toBe(false);
+ expect(existsSync(join(root, "app/memory/proof/page.tsx"))).toBe(false);
+ expect(existsSync(join(root, "app/api/memory/browser/route.ts"))).toBe(false);
+ expect(existsSync(join(root, "app/api/memory/audit/route.ts"))).toBe(false);
+ });
+
+ it("admin browser, audit, and verification routes are read-only and service-role-free", () => {
+ const files = ["app/admin/memory/browser/page.tsx", "app/admin/memory/audit/page.tsx", "app/admin/memory/verification/page.tsx", "lib/services/admin-memory-verification-loader.ts", "lib/services/admin-memory-route-guard-contract.ts"];
+ const text = files.map((f) => readFileSync(join(root, f), "utf8")).join("\n");
+ expect(text).toMatch(/resolvePandoraServerSession/);
+ expect(text).not.toMatch(/SUPABASE_SERVICE_ROLE|service-role|createServiceRole|service_role_key/i);
+ expect(text).not.toMatch(/\.insert\(|\.update\(|\.delete\(|\.upsert\(|executeApproved|persistApproved|appendReviewDecision/i);
+ const imports = text.split("\n").filter((line) => line.startsWith("import ")).join("\n");
+ expect(imports).not.toMatch(/openai|anthropic|embedding|pgvector|semantic|gpt-actions|mcp/i);
+ });
+});