From 4d7151f1a20b6246d507168e0ed23cfccadc8ff8 Mon Sep 17 00:00:00 2001 From: besfeng23 <136943241+besfeng23@users.noreply.github.com> Date: Sat, 4 Jul 2026 04:00:14 +0800 Subject: [PATCH] Add Pandora operator verification console --- components/pandora/AuditEvidenceCard.tsx | 2 + components/pandora/MobileBottomNav.tsx | 2 +- components/pandora/NamespaceInvariantCard.tsx | 5 + components/pandora/PackSupersessionCard.tsx | 3 + components/pandora/PandoraDashboard.tsx | 2 + components/pandora/RetrievalEvalCard.tsx | 2 + components/pandora/Sidebar.tsx | 2 +- .../pandora/VerificationConsoleCard.tsx | 9 ++ components/pandora/mock-data.ts | 6 +- components/pandora/nav-items.ts | 2 + components/pandora/types.ts | 74 +++++++++++ docs/pandora-verification-console.md | 44 +++++++ lib/services/pandora-dashboard-service.ts | 5 +- lib/services/pandora-verification-service.ts | 120 ++++++++++++++++++ tests/unit/pandora-dashboard-guard.test.ts | 13 +- tests/unit/pandora-dashboard-ui.test.tsx | 20 +++ .../unit/pandora-verification-service.test.ts | 22 ++++ 17 files changed, 324 insertions(+), 9 deletions(-) create mode 100644 components/pandora/AuditEvidenceCard.tsx create mode 100644 components/pandora/NamespaceInvariantCard.tsx create mode 100644 components/pandora/PackSupersessionCard.tsx create mode 100644 components/pandora/RetrievalEvalCard.tsx create mode 100644 components/pandora/VerificationConsoleCard.tsx create mode 100644 components/pandora/nav-items.ts create mode 100644 docs/pandora-verification-console.md create mode 100644 lib/services/pandora-verification-service.ts create mode 100644 tests/unit/pandora-verification-service.test.ts diff --git a/components/pandora/AuditEvidenceCard.tsx b/components/pandora/AuditEvidenceCard.tsx new file mode 100644 index 0000000..ac54469 --- /dev/null +++ b/components/pandora/AuditEvidenceCard.tsx @@ -0,0 +1,2 @@ +import type { AuditEvidenceItem, SmokeEvidenceSummary } from "./types"; +export function AuditEvidenceCard({ items, smoke }: { items: AuditEvidenceItem[]; smoke: SmokeEvidenceSummary }) { return

Audit evidence

Distill and smoke evidence

{items.length ?
{items.map((item) =>
{item.action}Namespace: {item.namespace}Record: {item.recordId}Created: {item.createdAt}
)}
:

No memory_context_pack_distilled audit events returned.

}
Smoke evidence: {smoke.status === "not_run" ? "Not run" : smoke.status}{smoke.latest ? <>{smoke.latest.action}{smoke.latest.namespace} / {smoke.latest.recordId}{smoke.latest.createdAt} : No smoke-test success is claimed without audit evidence.}
{smoke.warnings.map((warning) =>

{warning}

)}
; } diff --git a/components/pandora/MobileBottomNav.tsx b/components/pandora/MobileBottomNav.tsx index f0365cc..7040a2b 100644 --- a/components/pandora/MobileBottomNav.tsx +++ b/components/pandora/MobileBottomNav.tsx @@ -1,4 +1,4 @@ import { Brain, Gauge, History, ListChecks, MoreHorizontal } from "lucide-react"; -import { mobileNavItems } from "./mock-data"; +import { mobileNavItems } from "./nav-items"; const icons = [Gauge, History, ListChecks, Brain, MoreHorizontal]; export function MobileBottomNav({ activeNav, onNavChange }: { activeNav: string; onNavChange: (item: string) => void }) { return ; } diff --git a/components/pandora/NamespaceInvariantCard.tsx b/components/pandora/NamespaceInvariantCard.tsx new file mode 100644 index 0000000..55e67bc --- /dev/null +++ b/components/pandora/NamespaceInvariantCard.tsx @@ -0,0 +1,5 @@ +import type { NamespaceVerificationSummary } from "./types"; + +export function NamespaceInvariantCard({ namespaces }: { namespaces: NamespaceVerificationSummary[] }) { + return

Namespace invariants

real_life and au isolation

{namespaces.map((item) =>
{item.namespace}Status: {item.status}Active masters: {item.activeMasterCount}Archived masters: {item.archivedMasterCount}{item.duplicateActiveMasterIds.length > 0 ? Duplicate active: {item.duplicateActiveMasterIds.join(", ")} : null}{item.warnings.map((warning) => {warning})}
)}
; +} diff --git a/components/pandora/PackSupersessionCard.tsx b/components/pandora/PackSupersessionCard.tsx new file mode 100644 index 0000000..7d3ccee --- /dev/null +++ b/components/pandora/PackSupersessionCard.tsx @@ -0,0 +1,3 @@ +import type { PackMetadata, PackSupersessionSummary } from "./types"; +function Pack({ label, pack }: { label: string; pack: PackMetadata | null }) { return
{label}{pack ? <>ID: {pack.id}{pack.namespace} / {pack.packType} / {pack.status}Created: {pack.createdAt}{pack.updatedAt ? Updated: {pack.updatedAt} : null} : No pack evidence returned.}
; } +export function PackSupersessionCard({ summary }: { summary: PackSupersessionSummary }) { return

Context-pack supersession

Master pack status: {summary.status}

{summary.namespaces.map((item) =>
)}{summary.warnings.length ?
{summary.warnings.map((warning) => {warning})}
: null}
; } diff --git a/components/pandora/PandoraDashboard.tsx b/components/pandora/PandoraDashboard.tsx index 0b7b8b6..b805ee3 100644 --- a/components/pandora/PandoraDashboard.tsx +++ b/components/pandora/PandoraDashboard.tsx @@ -10,6 +10,7 @@ import { RecentEventsTimeline } from "./RecentEventsTimeline"; import { Sidebar } from "./Sidebar"; import { StatCard } from "./StatCard"; import { TopBar } from "./TopBar"; +import { VerificationConsoleCard } from "./VerificationConsoleCard"; import { WorkQueueCard } from "./WorkQueueCard"; import type { PandoraDashboardData, StatItem } from "./types"; import { useState } from "react"; @@ -41,6 +42,7 @@ export function PandoraDashboard({ dashboardData }: { dashboardData: PandoraDash
{stats.map((stat) => )}
+
diff --git a/components/pandora/RetrievalEvalCard.tsx b/components/pandora/RetrievalEvalCard.tsx new file mode 100644 index 0000000..fc10e70 --- /dev/null +++ b/components/pandora/RetrievalEvalCard.tsx @@ -0,0 +1,2 @@ +import type { RetrievalEvalSummary } from "./types"; +export function RetrievalEvalCard({ evalSummary }: { evalSummary: RetrievalEvalSummary }) { return

Retrieval eval

Status: {evalSummary.status === "not_run" ? "Not run" : evalSummary.status}

{evalSummary.resultLabel}Source: {evalSummary.source}Latest run: {evalSummary.latestRunId ?? "None"}Run at: {evalSummary.latestRunAt ?? "None"}{evalSummary.realResultAvailable ? "Real eval/log row displayed" : "No score displayed without evidence"}
{evalSummary.warnings.map((warning) =>

{warning}

)}
; } diff --git a/components/pandora/Sidebar.tsx b/components/pandora/Sidebar.tsx index 8bb5278..8e0fbc8 100644 --- a/components/pandora/Sidebar.tsx +++ b/components/pandora/Sidebar.tsx @@ -1,5 +1,5 @@ import { Activity, Brain, Circle, FolderKanban, Gauge, History, ListChecks, Settings, Users } from "lucide-react"; -import { navItems } from "./mock-data"; +import { navItems } from "./nav-items"; const icons = [Gauge, History, FolderKanban, Brain, ListChecks, Users, FolderKanban, Activity, Settings]; diff --git a/components/pandora/VerificationConsoleCard.tsx b/components/pandora/VerificationConsoleCard.tsx new file mode 100644 index 0000000..7c10854 --- /dev/null +++ b/components/pandora/VerificationConsoleCard.tsx @@ -0,0 +1,9 @@ +import { AuditEvidenceCard } from "./AuditEvidenceCard"; +import { NamespaceInvariantCard } from "./NamespaceInvariantCard"; +import { PackSupersessionCard } from "./PackSupersessionCard"; +import { RetrievalEvalCard } from "./RetrievalEvalCard"; +import type { PandoraVerificationData } from "./types"; + +export function VerificationConsoleCard({ verification }: { verification: PandoraVerificationData }) { + return

Operator Verification Console

Memory health evidence

Read-only verification for master packs, namespace separation, retrieval eval status, context-pack distillation audit evidence, and smoke evidence.

Read-onlyNo unsafe actions{verification.status}
{verification.warnings.length ?

Verification warnings

Evidence gaps are not green

{verification.warnings.map((warning) => {warning})}
: null}
; +} diff --git a/components/pandora/mock-data.ts b/components/pandora/mock-data.ts index aa0bab0..ce76719 100644 --- a/components/pandora/mock-data.ts +++ b/components/pandora/mock-data.ts @@ -16,6 +16,6 @@ export const workQueue: WorkQueueData = { needsReview: 0, openLoops: 0, stalePac export const timelineEvents: TimelineEventData[] = [{ id: "fixture", color: "slate", title: "Fixture", time: "No live data", desc: "Mock only", namespace: "real_life" }]; export const coreSystems: SystemRow[] = [{ label: "Fixture", value: "No live data", state: "idle" }]; export const gatedSystems: SystemRow[] = [{ label: "Semantic retrieval", value: "Gated", state: "gated" }]; -export const fixtureDashboardData: PandoraDashboardData = { generatedAt: "No live data", operatorLabel: "Fixture", live: false, warnings: ["Mock only"], hero: { title: "Fixture dashboard", description: "Mock only: no live data.", primaryAction: "No live data", secondaryAction: "Semantic gated" }, evidence: "No live data", stats: [{ id: "fixture", title: "Fixture", value: "No live data", subtitle: "Mock only", color: "slate", sparklineData: [0, 0] }], memorySpaces, workQueue, profileSnapshot, timelineEvents, diagnostics: { coreSystems, gatedSystems, envelope: { title: "Fixture", description: "Mock only: no live data." } } }; -export const navItems = ["Dashboard", "Memory Feed", "Context Packs", "Adaptive Profiles", "Open Loops", "People", "Projects", "Retrieval Tests", "Settings"] as const; -export const mobileNavItems = ["Dashboard", "Memory Feed", "Queue", "Profiles", "More"]; +const verification = { generatedAt: "No live data", status: "not_run" as const, namespaces: [], packSupersession: { status: "not_run" as const, namespaces: [], warnings: ["Mock only"] }, retrievalEval: { status: "not_run" as const, source: "fixture", latestRunId: null, latestRunAt: null, resultLabel: "Not run", realResultAvailable: false, warnings: ["Mock only"] }, auditEvidence: [], smokeEvidence: { status: "not_run" as const, latest: null, warnings: ["Mock only"] }, invariantStatus: { exactlyOneActiveMasterPerNamespace: "not_run" as const, noCrossNamespacePackMixing: "not_run" as const, noDuplicateActiveMaster: "not_run" as const, retrievalEvalHasNoFabricatedScore: "pass" as const, smokeEvidence: "not_run" as const }, warnings: ["Mock only"] }; +export const fixtureDashboardData: PandoraDashboardData = { generatedAt: "No live data", operatorLabel: "Fixture", live: false, warnings: ["Mock only"], hero: { title: "Fixture dashboard", description: "Mock only: no live data.", primaryAction: "No live data", secondaryAction: "Semantic gated" }, evidence: "No live data", stats: [{ id: "fixture", title: "Fixture", value: "No live data", subtitle: "Mock only", color: "slate", sparklineData: [0, 0] }], memorySpaces, workQueue, profileSnapshot, timelineEvents, diagnostics: { coreSystems, gatedSystems, envelope: { title: "Fixture", description: "Mock only: no live data." } }, verification }; + diff --git a/components/pandora/nav-items.ts b/components/pandora/nav-items.ts new file mode 100644 index 0000000..3dfbaa0 --- /dev/null +++ b/components/pandora/nav-items.ts @@ -0,0 +1,2 @@ +export const navItems = ["Dashboard", "Memory Feed", "Context Packs", "Adaptive Profiles", "Open Loops", "People", "Projects", "Retrieval Tests", "Settings"] as const; +export const mobileNavItems = ["Dashboard", "Memory Feed", "Queue", "Profiles", "More"]; diff --git a/components/pandora/types.ts b/components/pandora/types.ts index 17ab35c..b0134f9 100644 --- a/components/pandora/types.ts +++ b/components/pandora/types.ts @@ -68,6 +68,79 @@ export type ProfileSnapshot = { evidence: string; }; +export type VerificationStatus = "pass" | "warning" | "fail" | "not_run"; +export type PandoraNamespace = "real_life" | "au"; + +export type PackMetadata = { + id: string; + namespace: PandoraNamespace; + packType: string; + status: string; + title: string; + createdAt: string; + updatedAt?: string; + supersededAt?: string; +}; + +export type NamespaceVerificationSummary = { + namespace: PandoraNamespace; + status: VerificationStatus; + activeMasterCount: number; + archivedMasterCount: number; + newestActiveMaster: PackMetadata | null; + previousArchivedMaster: PackMetadata | null; + duplicateActiveMasterIds: string[]; + warnings: string[]; +}; + +export type PackSupersessionSummary = { + status: VerificationStatus; + namespaces: NamespaceVerificationSummary[]; + warnings: string[]; +}; + +export type RetrievalEvalSummary = { + status: VerificationStatus; + source: string; + latestRunId: string | null; + latestRunAt: string | null; + resultLabel: string; + realResultAvailable: boolean; + warnings: string[]; +}; + +export type AuditEvidenceItem = { + id: string; + action: string; + namespace: PandoraNamespace | "unknown"; + recordId: string; + createdAt: string; +}; + +export type SmokeEvidenceSummary = { + status: VerificationStatus; + latest: AuditEvidenceItem | null; + warnings: string[]; +}; + +export type PandoraVerificationData = { + generatedAt: string; + status: VerificationStatus; + namespaces: NamespaceVerificationSummary[]; + packSupersession: PackSupersessionSummary; + retrievalEval: RetrievalEvalSummary; + auditEvidence: AuditEvidenceItem[]; + smokeEvidence: SmokeEvidenceSummary; + invariantStatus: { + exactlyOneActiveMasterPerNamespace: VerificationStatus; + noCrossNamespacePackMixing: VerificationStatus; + noDuplicateActiveMaster: VerificationStatus; + retrievalEvalHasNoFabricatedScore: VerificationStatus; + smokeEvidence: VerificationStatus; + }; + warnings: string[]; +}; + export type PandoraDashboardData = { generatedAt: string; operatorLabel: string; @@ -93,4 +166,5 @@ export type PandoraDashboardData = { description: string; }; }; + verification: PandoraVerificationData; }; diff --git a/docs/pandora-verification-console.md b/docs/pandora-verification-console.md new file mode 100644 index 0000000..1fc8a0b --- /dev/null +++ b/docs/pandora-verification-console.md @@ -0,0 +1,44 @@ +# Pandora Operator Verification Console + +## What is live + +`/pandora` remains an authenticated operator dashboard. The verification console adds read-only server-side checks for the authenticated Supabase user only: + +- active and archived/superseded `memory_context_packs` master-pack counts per namespace; +- newest active master pack metadata and previous archived/superseded master-pack metadata; +- recent `memory_context_pack_distilled` audit evidence from `audit_logs`; +- retrieval-log/eval evidence when a real row exists; +- smoke evidence when a real audit row exists. + +The loader fails safe: missing or unreadable tables become warnings and empty evidence states, not green success. + +## What remains gated + +The console does not enable model calls, embeddings, semantic retrieval, GPT Actions, MCP, public reads, public persistence, destructive actions, production ingest writes, protected non-dry-run jobs, or pruning application/archive/delete behavior. + +## Status meanings + +- `pass`: real rows support the invariant or evidence claim. +- `warning`: the dashboard rendered safely, but evidence is incomplete or a table was unavailable. +- `fail`: an invariant is violated, such as duplicate active master packs. +- `not_run`: no real evidence was returned, so the console makes no success claim. + +## Why retrieval eval must not show fake accuracy + +Retrieval status is only shown from real persisted evidence. If no retrieval eval/log row exists, the console says `Not run`. It must not display placeholder percentages or marketing copy because that would falsely imply retrieval quality has been measured. + +## Why smoke evidence is read-only + +Smoke evidence is an audit proof surface. The dashboard only reads audit rows and never starts a smoke test, writes proof rows, mutates memory, or marks a smoke check as successful without persisted evidence. + +## Manual Supabase verification + +When needed, verify with authenticated, user-scoped SQL or table views: + +1. Check `memory_context_packs` filtered by `user_id`, `namespace`, `pack_type = 'master'`, and `status`. +2. Confirm exactly one active master pack for `real_life` and exactly one for `au`. +3. Confirm archived/superseded previous master packs exist when supersession has happened. +4. Check `audit_logs` for `action = 'memory_context_pack_distilled'` and smoke/proof actions. +5. Check retrieval evidence tables/logs only if a real eval has been run. + +Do not use service-role browser code or client-supplied user IDs for this verification. diff --git a/lib/services/pandora-dashboard-service.ts b/lib/services/pandora-dashboard-service.ts index d8cbb12..a7ec8ad 100644 --- a/lib/services/pandora-dashboard-service.ts +++ b/lib/services/pandora-dashboard-service.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { PandoraDashboardData } from "@/components/pandora/types"; +import { loadPandoraVerificationData } from "@/lib/services/pandora-verification-service"; export type PandoraDashboardDbClient = { from: (table: string) => any }; type Namespace = "real_life" | "au"; @@ -36,6 +37,7 @@ function eventSummary(event: Row) { export async function loadPandoraDashboardData(client: PandoraDashboardDbClient, input: { userId: string; operatorLabel?: string }): Promise { const warnings: string[] = []; + const verification = await loadPandoraVerificationData(client, { userId: input.userId }); const data = await Promise.all(namespaces.map(async (namespace) => ({ namespace, events: await rows(client, "memory_events", input.userId, namespace, warnings, 500), @@ -61,7 +63,7 @@ export async function loadPandoraDashboardData(client: PandoraDashboardDbClient, generatedAt: new Date().toISOString(), operatorLabel: input.operatorLabel ?? input.userId, live: warnings.length === 0, - warnings, + warnings: Array.from(new Set([...warnings, ...verification.warnings])), hero: { title: "Pandora dashboard is reading live memory state.", description: "This route renders authenticated Supabase data for memory state while semantic retrieval, embeddings, model calls, GPT Actions, and MCP remain gated.", primaryAction: "Context pack data live", secondaryAction: "Retrieval eval Gated" }, evidence: warnings.length ? "Live dashboard read completed with safe empty states for unavailable tables." : "Live dashboard read complete from server-derived session scope.", stats: [ @@ -82,5 +84,6 @@ export async function loadPandoraDashboardData(client: PandoraDashboardDbClient, profileSnapshot: { name: profile?.title ?? "No active profile", status: profile ? "Live read" : "No live data", confidencePercent: confidence.percent, confidenceLabel: confidence.label, summary: profile?.summary ?? "No active adaptive profile returned for this session.", lastRefreshed: profile?.updated_at ?? "No profile timestamp returned", traits: ["Authenticated", "RLS scoped"], evidence: profile?.id ? `Live profile row ${profile.id}` : "No active profile row" }, timelineEvents: events.slice(0, 6).map((event) => ({ id: String(event.id ?? `${event.namespace}-${event.created_at ?? "event"}`), title: `${event.namespace} • ${event.status ?? "unknown"}`, time: event.created_at ?? "Live read", desc: eventSummary(event), namespace: event.namespace === "au" ? "au" : "real_life", color: event.namespace === "au" ? "purple" : "emerald" })), diagnostics: { coreSystems: [{ label: "Route exposure", value: "Auth gated", state: "healthy" }, { label: "Displayed data", value: warnings.length ? "Partial live reads" : "Live reads", state: warnings.length ? "attention" : "healthy" }, { label: "Master-pack invariant", value: duplicates ? `${duplicates} duplicate` : "OK", state: duplicates ? "attention" : "healthy" }, { label: "Client user_id", value: "Rejected", state: "healthy" }], gatedSystems: [{ label: "Semantic retrieval", value: "Gated Off", state: "gated" }, { label: "Embeddings", value: "Gated Off", state: "gated" }, { label: "Model calls", value: "Gated Off", state: "gated" }, { label: "Pruning automation", value: "Review-only", state: "gated" }], envelope: { title: "Dashboard Truth Envelope", description: warnings.length ? "Unavailable reads were converted to warnings and empty UI state." : "Live loader completed from authenticated Supabase reads." } }, + verification, }; } diff --git a/lib/services/pandora-verification-service.ts b/lib/services/pandora-verification-service.ts new file mode 100644 index 0000000..586b80a --- /dev/null +++ b/lib/services/pandora-verification-service.ts @@ -0,0 +1,120 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { AuditEvidenceItem, NamespaceVerificationSummary, PackMetadata, PandoraNamespace, PandoraVerificationData, RetrievalEvalSummary, VerificationStatus } from "@/components/pandora/types"; + +export type PandoraVerificationDbClient = { from: (table: string) => any }; +type Row = Record; +const namespaces: PandoraNamespace[] = ["real_life", "au"]; +const archivedStatuses = new Set(["archived", "superseded"]); + +async function readRows(client: PandoraVerificationDbClient, table: string, userId: string, warnings: string[], namespace?: PandoraNamespace, limit = 100): Promise { + try { + let query = client.from(table).select("*").eq("user_id", userId); + if (namespace) query = query.eq("namespace", namespace); + const result = await query.order("created_at", { ascending: false }).limit(limit); + if (result.error) { + warnings.push(`${table}${namespace ? `/${namespace}` : ""} read unavailable; verification shows safe empty state.`); + return []; + } + return Array.isArray(result.data) ? result.data : []; + } catch { + warnings.push(`${table}${namespace ? `/${namespace}` : ""} read unavailable; verification shows safe empty state.`); + return []; + } +} + +function asNamespace(value: unknown): PandoraNamespace | "unknown" { + return value === "real_life" || value === "au" ? value : "unknown"; +} + +function packMeta(row: Row | undefined): PackMetadata | null { + if (!row) return null; + const namespace = asNamespace(row.namespace); + if (namespace === "unknown") return null; + return { + id: String(row.id ?? "unknown"), + namespace, + packType: String(row.pack_type ?? "unknown"), + status: String(row.status ?? "unknown"), + title: String(row.title ?? row.id ?? "Untitled pack"), + createdAt: String(row.created_at ?? "No created_at returned"), + updatedAt: row.updated_at ? String(row.updated_at) : undefined, + supersededAt: row.superseded_at ? String(row.superseded_at) : undefined, + }; +} + +function worst(statuses: VerificationStatus[]): VerificationStatus { + if (statuses.includes("fail")) return "fail"; + if (statuses.includes("warning")) return "warning"; + if (statuses.includes("not_run")) return "not_run"; + return "pass"; +} + +function summarizeNamespace(namespace: PandoraNamespace, packs: Row[]): NamespaceVerificationSummary { + const scoped = packs.filter((pack) => pack.namespace === namespace && pack.pack_type === "master"); + const active = scoped.filter((pack) => pack.status === "active"); + const archived = scoped.filter((pack) => archivedStatuses.has(String(pack.status ?? ""))); + const warnings: string[] = []; + if (active.length === 0) warnings.push(`No active master pack returned for ${namespace}.`); + if (active.length > 1) warnings.push(`${namespace} has ${active.length} active master packs; supersession needs review.`); + if (archived.length === 0) warnings.push(`No previous archived/superseded master pack evidence returned for ${namespace}.`); + return { + namespace, + status: active.length === 1 ? (archived.length > 0 ? "pass" : "warning") : "fail", + activeMasterCount: active.length, + archivedMasterCount: archived.length, + newestActiveMaster: packMeta(active[0]), + previousArchivedMaster: packMeta(archived[0]), + duplicateActiveMasterIds: active.slice(1).map((pack) => String(pack.id ?? "unknown")), + warnings, + }; +} + +function auditItem(row: Row): AuditEvidenceItem { + return { id: String(row.id ?? "unknown"), action: String(row.action ?? "unknown"), namespace: asNamespace(row.namespace), recordId: String(row.record_id ?? row.recordId ?? "unknown"), createdAt: String(row.created_at ?? "No created_at returned") }; +} + +function retrievalSummary(rows: Row[], warnings: string[]): RetrievalEvalSummary { + if (warnings.some((warning) => warning.includes("retrieval_logs read unavailable"))) { + return { status: "not_run", source: "retrieval_logs", latestRunId: null, latestRunAt: null, resultLabel: "Unavailable", realResultAvailable: false, warnings: ["Retrieval eval source could not be read; no score is shown."] }; + } + const latest = rows[0]; + if (!latest) return { status: "not_run", source: "retrieval_logs", latestRunId: null, latestRunAt: null, resultLabel: "Not run", realResultAvailable: false, warnings: ["No retrieval eval/log rows returned for this operator."] }; + const label = latest.eval_result ?? latest.result ?? latest.status ?? latest.retrieval_service ?? "Real retrieval log row present; no score column returned"; + return { status: "pass", source: "retrieval_logs", latestRunId: String(latest.id ?? "unknown"), latestRunAt: String(latest.created_at ?? "No created_at returned"), resultLabel: String(label), realResultAvailable: true, warnings: [] }; +} + +export async function loadPandoraVerificationData(client: PandoraVerificationDbClient, input: { userId: string }): Promise { + const warnings: string[] = []; + const [packRowsByNamespace, auditRows, retrievalRows] = await Promise.all([ + Promise.all(namespaces.map((namespace) => readRows(client, "memory_context_packs", input.userId, warnings, namespace, 100))), + readRows(client, "audit_logs", input.userId, warnings, undefined, 50), + readRows(client, "retrieval_logs", input.userId, warnings, undefined, 25), + ]); + const packs = packRowsByNamespace.flat().filter((pack) => namespaces.includes(pack.namespace)); + const namespaceSummaries = namespaces.map((namespace) => summarizeNamespace(namespace, packs)); + const crossNamespaceRows = packRowsByNamespace.flat().filter((pack) => pack.namespace !== "real_life" && pack.namespace !== "au"); + if (crossNamespaceRows.length) warnings.push("Unreadable or unexpected pack namespace values were excluded from verification."); + const distillAudit = auditRows.filter((row) => row.action === "memory_context_pack_distilled").map(auditItem).slice(0, 10); + const smokeRows = auditRows.filter((row) => ["first_live_append_proof_executed", "live_one_reviewed_item_executed", "operator_smoke_test_passed"].includes(String(row.action ?? ""))).map(auditItem); + if (distillAudit.length === 0) warnings.push("No memory_context_pack_distilled audit evidence returned."); + if (smokeRows.length === 0) warnings.push("No smoke evidence audit rows returned; smoke status is not_run."); + const retrievalEval = retrievalSummary(retrievalRows, warnings); + const duplicateCount = namespaceSummaries.reduce((sum, item) => sum + item.duplicateActiveMasterIds.length, 0); + const exactlyOne = namespaceSummaries.every((item) => item.activeMasterCount === 1) ? "pass" : "fail"; + const noDuplicates = duplicateCount === 0 ? "pass" : "fail"; + const noCross = crossNamespaceRows.length === 0 ? "pass" : "fail"; + const smokeEvidence = { status: smokeRows.length ? "pass" as const : "not_run" as const, latest: smokeRows[0] ?? null, warnings: smokeRows.length ? [] : ["No smoke evidence exists for this operator session scope."] }; + const packStatus = worst(namespaceSummaries.map((item) => item.status)); + const status = worst([packStatus, retrievalEval.status, smokeEvidence.status, warnings.length ? "warning" : "pass"]); + return { + generatedAt: new Date().toISOString(), + status, + namespaces: namespaceSummaries, + packSupersession: { status: packStatus, namespaces: namespaceSummaries, warnings: namespaceSummaries.flatMap((item) => item.warnings) }, + retrievalEval, + auditEvidence: distillAudit, + smokeEvidence, + invariantStatus: { exactlyOneActiveMasterPerNamespace: exactlyOne, noCrossNamespacePackMixing: noCross, noDuplicateActiveMaster: noDuplicates, retrievalEvalHasNoFabricatedScore: "pass", smokeEvidence: smokeEvidence.status }, + warnings: Array.from(new Set([...warnings, ...retrievalEval.warnings, ...smokeEvidence.warnings])), + }; +} diff --git a/tests/unit/pandora-dashboard-guard.test.ts b/tests/unit/pandora-dashboard-guard.test.ts index b11921d..b93baf8 100644 --- a/tests/unit/pandora-dashboard-guard.test.ts +++ b/tests/unit/pandora-dashboard-guard.test.ts @@ -1,7 +1,7 @@ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; -const productionFiles = ["app/pandora/page.tsx", "components/pandora/PandoraDashboard.tsx"]; +const productionFiles = ["app/pandora/page.tsx", "components/pandora/PandoraDashboard.tsx", "components/pandora/Sidebar.tsx", "components/pandora/MobileBottomNav.tsx"]; describe("Pandora production dashboard guards", () => { it("does not import mock data from the route or dashboard shell", () => { @@ -10,17 +10,24 @@ describe("Pandora production dashboard guards", () => { it("does not accept user_id query params or props for dashboard loading", () => { const page = readFileSync("app/pandora/page.tsx", "utf8"); + const dashboard = readFileSync("components/pandora/PandoraDashboard.tsx", "utf8"); expect(page).not.toMatch(/searchParams/); - expect(page).not.toMatch(/searchParams.*user_id|searchParams.*userId|query.*user_id|query.*userId/s); + expect(`${page}\n${dashboard}`).not.toMatch(/searchParams\.user_id|searchParams\.userId|query\.user_id|query\.userId|body\.user_id|body\.userId|props\.userId/); expect(page).toContain("session.session.userId"); }); it("does not ship fake accuracy claims in production dashboard code", () => { - const files = ["app/pandora/page.tsx", "components/pandora/PandoraDashboard.tsx", "components/pandora/StatCard.tsx", "components/pandora/DiagnosticsCard.tsx", "lib/services/pandora-dashboard-service.ts"]; + const files = ["app/pandora/page.tsx", "components/pandora/PandoraDashboard.tsx", "components/pandora/StatCard.tsx", "components/pandora/DiagnosticsCard.tsx", "components/pandora/VerificationConsoleCard.tsx", "components/pandora/RetrievalEvalCard.tsx", "lib/services/pandora-dashboard-service.ts", "lib/services/pandora-verification-service.ts"]; for (const file of files) { const source = readFileSync(file, "utf8"); expect(source).not.toContain("94.3"); expect(source).not.toMatch(/retrieval accuracy/i); } }); + + it("verification loader remains read-only and service-role-free", () => { + const source = readFileSync("lib/services/pandora-verification-service.ts", "utf8"); + expect(source).not.toMatch(/insert\(|update\(|delete\(|upsert\(|rpc\(/); + expect(source).not.toMatch(/service-role|serviceRole|SUPABASE_SERVICE_ROLE|createAdmin/i); + }); }); diff --git a/tests/unit/pandora-dashboard-ui.test.tsx b/tests/unit/pandora-dashboard-ui.test.tsx index dd66fdb..7b57d9b 100644 --- a/tests/unit/pandora-dashboard-ui.test.tsx +++ b/tests/unit/pandora-dashboard-ui.test.tsx @@ -4,6 +4,22 @@ import { describe, expect, it } from "vitest"; import { PandoraDashboard } from "@/components/pandora/PandoraDashboard"; import type { PandoraDashboardData } from "@/components/pandora/types"; + +const verification = { + generatedAt: "2026-07-03T00:00:00Z", + status: "warning" as const, + namespaces: [ + { namespace: "real_life" as const, status: "pass" as const, activeMasterCount: 1, archivedMasterCount: 1, newestActiveMaster: { id: "rl-pack", namespace: "real_life" as const, packType: "master", status: "active", title: "real", createdAt: "now" }, previousArchivedMaster: { id: "rl-old", namespace: "real_life" as const, packType: "master", status: "archived", title: "old", createdAt: "before" }, duplicateActiveMasterIds: [], warnings: [] }, + { namespace: "au" as const, status: "warning" as const, activeMasterCount: 1, archivedMasterCount: 0, newestActiveMaster: { id: "au-pack", namespace: "au" as const, packType: "master", status: "active", title: "au", createdAt: "now" }, previousArchivedMaster: null, duplicateActiveMasterIds: [], warnings: ["No previous archived/superseded master pack evidence returned for au."] }, + ], + packSupersession: { status: "warning" as const, namespaces: [], warnings: ["No previous archived/superseded master pack evidence returned for au."] }, + retrievalEval: { status: "not_run" as const, source: "retrieval_logs", latestRunId: null, latestRunAt: null, resultLabel: "Not run", realResultAvailable: false, warnings: ["No retrieval eval/log rows returned for this operator."] }, + auditEvidence: [], + smokeEvidence: { status: "not_run" as const, latest: null, warnings: ["No smoke evidence exists for this operator session scope."] }, + invariantStatus: { exactlyOneActiveMasterPerNamespace: "pass" as const, noCrossNamespacePackMixing: "pass" as const, noDuplicateActiveMaster: "pass" as const, retrievalEvalHasNoFabricatedScore: "pass" as const, smokeEvidence: "not_run" as const }, + warnings: ["visible warning"], +}; + const data: PandoraDashboardData = { generatedAt: "2026-07-03T00:00:00Z", operatorLabel: "operator@example.com", @@ -17,6 +33,7 @@ const data: PandoraDashboardData = { profileSnapshot: { name: "Operator", status: "Live read", confidencePercent: 77, confidenceLabel: "77%", summary: "Live profile", lastRefreshed: "now", traits: ["Authenticated"], evidence: "profile row" }, timelineEvents: [{ id: "event", title: "real_life • captured", time: "now", desc: "Live event summary", namespace: "real_life", color: "emerald" }], diagnostics: { coreSystems: [{ label: "Displayed data", value: "Live reads", state: "healthy" }], gatedSystems: [{ label: "Semantic retrieval", value: "Gated Off", state: "gated" }], envelope: { title: "Dashboard Truth Envelope", description: "Live loader completed" } }, + verification, }; describe("PandoraDashboard", () => { @@ -29,5 +46,8 @@ describe("PandoraDashboard", () => { expect(html).toContain("operator@example.com"); expect(html).toContain("memory_profiles/au read unavailable"); expect(html).toContain("Gated Off"); + expect(html).toContain("Operator Verification Console"); + expect(html).toContain("Not run"); + expect(html).toContain("visible warning"); }); }); diff --git a/tests/unit/pandora-verification-service.test.ts b/tests/unit/pandora-verification-service.test.ts new file mode 100644 index 0000000..d772364 --- /dev/null +++ b/tests/unit/pandora-verification-service.test.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { describe, expect, it } from "vitest"; +import { loadPandoraVerificationData } from "@/lib/services/pandora-verification-service"; + +type Row = Record; +const USER = "user-1"; +const OTHER = "other-user"; +function makeClient(store: Record, failTables: string[] = [], ops: string[] = []) { + return { from(table: string) { if (failTables.includes(table)) throw new Error("missing"); let selected = [...(store[table] ?? [])]; const builder: any = { select() { return builder; }, eq(k: string, v: any) { selected = selected.filter((r) => r[k] === v); return builder; }, order(k: string) { selected = selected.sort((a,b)=>String(b[k]??"").localeCompare(String(a[k]??""))); return builder; }, limit(n: number) { selected = selected.slice(0,n); return builder; }, insert() { ops.push("insert"); return builder; }, update() { ops.push("update"); return builder; }, delete() { ops.push("delete"); return builder; }, upsert() { ops.push("upsert"); return builder; }, then(resolve: any, reject: any) { return Promise.resolve({ data: selected, error: null }).then(resolve, reject); } }; return builder; } } as any; +} +function pack(id: string, namespace: "real_life"|"au", status="active", user_id=USER): Row { return { id, user_id, namespace, pack_type: "master", status, title: id, created_at: id }; } +function base() { return { memory_context_packs: [pack("rl-active", "real_life"), pack("rl-old", "real_life", "archived"), pack("au-active", "au"), pack("au-old", "au", "superseded"), pack("other", "real_life", "active", OTHER)], audit_logs: [{ id: "a1", user_id: USER, namespace: "real_life", action: "memory_context_pack_distilled", record_id: "rl-active", created_at: "2026-07-03T00:00:00Z" }, { id: "s1", user_id: USER, namespace: "real_life", action: "first_live_append_proof_executed", record_id: "m1", created_at: "2026-07-03T01:00:00Z" }, { id: "other-a", user_id: OTHER, namespace: "au", action: "memory_context_pack_distilled", record_id: "x", created_at: "x" }], retrieval_logs: [] }; } + +describe("loadPandoraVerificationData", () => { + it("passes exactly one active master per namespace and hides other users", async () => { const data = await loadPandoraVerificationData(makeClient(base()), { userId: USER }); expect(data.invariantStatus.exactlyOneActiveMasterPerNamespace).toBe("pass"); expect(JSON.stringify(data)).not.toContain(OTHER); }); + it("fails duplicate active master in real_life", async () => { const s = base(); s.memory_context_packs.push(pack("rl-dup", "real_life")); const data = await loadPandoraVerificationData(makeClient(s), { userId: USER }); expect(data.namespaces.find((n)=>n.namespace==="real_life")?.status).toBe("fail"); expect(data.invariantStatus.noDuplicateActiveMaster).toBe("fail"); }); + it("fails duplicate active master in au", async () => { const s = base(); s.memory_context_packs.push(pack("au-dup", "au")); const data = await loadPandoraVerificationData(makeClient(s), { userId: USER }); expect(data.namespaces.find((n)=>n.namespace==="au")?.status).toBe("fail"); expect(data.invariantStatus.noDuplicateActiveMaster).toBe("fail"); }); + it("shows archived previous master and warns when missing", async () => { const data = await loadPandoraVerificationData(makeClient(base()), { userId: USER }); expect(data.namespaces[0].previousArchivedMaster?.id).toBe("rl-old"); const s = base(); s.memory_context_packs = s.memory_context_packs.filter((p)=>!(p.namespace==="au" && p.status!=="active")); const missing = await loadPandoraVerificationData(makeClient(s), { userId: USER }); expect(missing.namespaces.find((n)=>n.namespace==="au")?.status).toBe("warning"); }); + it("marks retrieval eval missing as not_run and present as real", async () => { const missing = await loadPandoraVerificationData(makeClient(base()), { userId: USER }); expect(missing.retrievalEval.status).toBe("not_run"); const s = base(); s.retrieval_logs.push({ id: "r1", user_id: USER, namespace: "real_life", retrieval_service: "internal_eval", created_at: "2026-07-03" }); const present = await loadPandoraVerificationData(makeClient(s), { userId: USER }); expect(present.retrievalEval).toMatchObject({ status: "pass", latestRunId: "r1", realResultAvailable: true }); }); + it("warns when audit or table reads are missing and never mutates", async () => { const ops: string[] = []; const data = await loadPandoraVerificationData(makeClient({ memory_context_packs: [] }, ["audit_logs"], ops), { userId: USER }); expect(data.warnings.length).toBeGreaterThan(0); expect(data.smokeEvidence.status).toBe("not_run"); expect(ops).toEqual([]); }); + it("never mixes AU and real_life rows", async () => { const data = await loadPandoraVerificationData(makeClient(base()), { userId: USER }); expect(data.namespaces.find((n)=>n.namespace==="real_life")?.newestActiveMaster?.id).toBe("rl-active"); expect(data.namespaces.find((n)=>n.namespace==="au")?.newestActiveMaster?.id).toBe("au-active"); }); +});