Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions components/pandora/AuditEvidenceCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { AuditEvidenceItem, SmokeEvidenceSummary } from "./types";
export function AuditEvidenceCard({ items, smoke }: { items: AuditEvidenceItem[]; smoke: SmokeEvidenceSummary }) { return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Audit evidence</p><h3>Distill and smoke evidence</h3></div></div>{items.length ? <div className="pd-mini-grid">{items.map((item) => <div className="pd-mini" key={item.id}><strong>{item.action}</strong><span>Namespace: {item.namespace}</span><span>Record: {item.recordId}</span><span>Created: {item.createdAt}</span></div>)}</div> : <p>No memory_context_pack_distilled audit events returned.</p>}<div className="pd-mini-grid"><div className="pd-mini"><strong>Smoke evidence: {smoke.status === "not_run" ? "Not run" : smoke.status}</strong>{smoke.latest ? <><span>{smoke.latest.action}</span><span>{smoke.latest.namespace} / {smoke.latest.recordId}</span><span>{smoke.latest.createdAt}</span></> : <span>No smoke-test success is claimed without audit evidence.</span>}</div></div>{smoke.warnings.map((warning) => <p key={warning}>{warning}</p>)}</section>; }
2 changes: 1 addition & 1 deletion components/pandora/MobileBottomNav.tsx
Original file line number Diff line number Diff line change
@@ -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 <nav className="pd-mobile-nav" aria-label="Mobile Pandora navigation">{mobileNavItems.map((item, index) => { const Icon = icons[index]; const active = item === activeNav || (item === "Dashboard" && activeNav === "Dashboard"); return <button type="button" key={item} aria-current={active ? "page" : undefined} onClick={() => onNavChange(item === "Feed" ? "Memory Feed" : item)}><span><Icon size={18} aria-hidden="true" />{item === "Queue" ? <i aria-label="Queue has attention items" /> : null}</span>{item}</button>; })}</nav>; }
5 changes: 5 additions & 0 deletions components/pandora/NamespaceInvariantCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { NamespaceVerificationSummary } from "./types";

export function NamespaceInvariantCard({ namespaces }: { namespaces: NamespaceVerificationSummary[] }) {
return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Namespace invariants</p><h3>real_life and au isolation</h3></div></div><div className="pd-mini-grid">{namespaces.map((item) => <div className="pd-mini" key={item.namespace}><strong>{item.namespace}</strong><span>Status: {item.status}</span><span>Active masters: {item.activeMasterCount}</span><span>Archived masters: {item.archivedMasterCount}</span>{item.duplicateActiveMasterIds.length > 0 ? <span>Duplicate active: {item.duplicateActiveMasterIds.join(", ")}</span> : null}{item.warnings.map((warning) => <span key={warning}>{warning}</span>)}</div>)}</div></section>;
}
3 changes: 3 additions & 0 deletions components/pandora/PackSupersessionCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { PackMetadata, PackSupersessionSummary } from "./types";
function Pack({ label, pack }: { label: string; pack: PackMetadata | null }) { return <div className="pd-mini"><strong>{label}</strong>{pack ? <><span>ID: {pack.id}</span><span>{pack.namespace} / {pack.packType} / {pack.status}</span><span>Created: {pack.createdAt}</span>{pack.updatedAt ? <span>Updated: {pack.updatedAt}</span> : null}</> : <span>No pack evidence returned.</span>}</div>; }
export function PackSupersessionCard({ summary }: { summary: PackSupersessionSummary }) { return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Context-pack supersession</p><h3>Master pack status: {summary.status}</h3></div></div>{summary.namespaces.map((item) => <div className="pd-mini-grid" key={item.namespace}><Pack label={`${item.namespace} active master`} pack={item.newestActiveMaster} /><Pack label={`${item.namespace} previous archived`} pack={item.previousArchivedMaster} /></div>)}{summary.warnings.length ? <div className="browser-state-grid">{summary.warnings.map((warning) => <span className="browser-state-pill browser-state-pill--blocked" key={warning}>{warning}</span>)}</div> : null}</section>; }
2 changes: 2 additions & 0 deletions components/pandora/PandoraDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -41,6 +42,7 @@ export function PandoraDashboard({ dashboardData }: { dashboardData: PandoraDash
<section className="pd-stat-grid" aria-label="Pandora status stats">
{stats.map((stat) => <StatCard stat={stat} key={stat.id} />)}
</section>
<VerificationConsoleCard verification={dashboardData.verification} />
<div className="pd-dashboard-grid">
<div className="pd-dashboard-col pd-dashboard-col-wide">
<MemorySpacesCard spaces={dashboardData.memorySpaces} />
Expand Down
2 changes: 2 additions & 0 deletions components/pandora/RetrievalEvalCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { RetrievalEvalSummary } from "./types";
export function RetrievalEvalCard({ evalSummary }: { evalSummary: RetrievalEvalSummary }) { return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Retrieval eval</p><h3>Status: {evalSummary.status === "not_run" ? "Not run" : evalSummary.status}</h3></div></div><div className="pd-mini-grid"><div className="pd-mini"><strong>{evalSummary.resultLabel}</strong><span>Source: {evalSummary.source}</span><span>Latest run: {evalSummary.latestRunId ?? "None"}</span><span>Run at: {evalSummary.latestRunAt ?? "None"}</span><span>{evalSummary.realResultAvailable ? "Real eval/log row displayed" : "No score displayed without evidence"}</span></div></div>{evalSummary.warnings.map((warning) => <p key={warning}>{warning}</p>)}</section>; }
2 changes: 1 addition & 1 deletion components/pandora/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -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];

Expand Down
9 changes: 9 additions & 0 deletions components/pandora/VerificationConsoleCard.tsx
Original file line number Diff line number Diff line change
@@ -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 <section aria-label="Pandora operator verification console"><div className="pd-header-row"><div><p className="pd-label">Operator Verification Console</p><h2>Memory health evidence</h2><p>Read-only verification for master packs, namespace separation, retrieval eval status, context-pack distillation audit evidence, and smoke evidence.</p></div><div className="pd-badges"><span className="pd-pill pd-pill-emerald">Read-only</span><span className="pd-pill pd-pill-slate">No unsafe actions</span><span className="pd-pill pd-pill-amber">{verification.status}</span></div></div><div className="pd-dashboard-grid"><div className="pd-dashboard-col"><NamespaceInvariantCard namespaces={verification.namespaces} /></div><div className="pd-dashboard-col"><PackSupersessionCard summary={verification.packSupersession} /></div><div className="pd-dashboard-col"><RetrievalEvalCard evalSummary={verification.retrievalEval} /><AuditEvidenceCard items={verification.auditEvidence} smoke={verification.smokeEvidence} /></div></div>{verification.warnings.length ? <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Verification warnings</p><h3>Evidence gaps are not green</h3></div></div><div className="browser-state-grid">{verification.warnings.map((warning) => <span className="browser-state-pill browser-state-pill--blocked" key={warning}>{warning}</span>)}</div></section> : null}</section>;
}
6 changes: 3 additions & 3 deletions components/pandora/mock-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

2 changes: 2 additions & 0 deletions components/pandora/nav-items.ts
Original file line number Diff line number Diff line change
@@ -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"];
74 changes: 74 additions & 0 deletions components/pandora/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -93,4 +166,5 @@ export type PandoraDashboardData = {
description: string;
};
};
verification: PandoraVerificationData;
};
44 changes: 44 additions & 0 deletions docs/pandora-verification-console.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 4 additions & 1 deletion lib/services/pandora-dashboard-service.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -36,6 +37,7 @@ function eventSummary(event: Row) {

export async function loadPandoraDashboardData(client: PandoraDashboardDbClient, input: { userId: string; operatorLabel?: string }): Promise<PandoraDashboardData> {
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),
Expand All @@ -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: [
Expand All @@ -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,
};
}
Loading
Loading