workbench(vault): deepen vault entry inspector + raw metadata drawer (#762)

Surface a selected vault entry's real evidence instead of just
epistemic_state + a digest:

- widen VaultEntrySubjectData to carry epistemic_status + the full
  metadata dict (data already reaches the inspector at runtime; this is
  a type-only unlock, no new fetch/endpoint)
- inspector shows epistemic_status and epistemic_state distinctly, core
  identity rows (turn, role) with honest 'not recorded' when absent,
  present-only rows (corrected, energy_*, promotion_certificate_digest),
  a propositional_form headline when present, copyable vault:<index> and
  versor_digest handles, and a collapsible key-sorted raw metadata drawer
- curated rows key off fields that actually exist on vault entries; the
  raw drawer is the catch-all as the open metadata schema grows

Exact-recall doctrine preserved: no similarity/relevance/score text.
This commit is contained in:
Shay 2026-06-15 01:29:12 -07:00 committed by GitHub
parent 4522e5b1cf
commit 74dc7c7a3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 189 additions and 13 deletions

View file

@ -126,6 +126,85 @@ describe("RightInspector", () => {
expect(screen.getByText("sha256:practice")).toBeInTheDocument();
});
it("shows vault entry depth: distinct status/state, curated metadata, handle, raw drawer", () => {
function SetVaultEntry() {
const { setSubject } = useEvidenceSubject();
useEffect(() => {
setSubject({
kind: "vault_entry",
entryIndex: 7,
data: {
entry_index: 7,
epistemic_status: "coherent",
epistemic_state: "decoded",
versor_digest: "sha256:vvvvvvvvvvvv",
metadata: {
turn: 3,
role: "assistant",
energy_class: "warm",
coherence_residual: 0.01,
propositional_form: "alpha causes beta",
promotion_certificate_digest: "sha256:certcertcert",
},
},
});
}, [setSubject]);
return <RightInspector />;
}
render(
<EvidenceProvider>
<SetVaultEntry />
</EvidenceProvider>,
);
expect(screen.getByText("Vault Entry")).toBeInTheDocument();
expect(screen.getByText("#7")).toBeInTheDocument();
// epistemic_status (tier) and epistemic_state (trust display) are distinct
expect(screen.getByText("epistemic_status")).toBeInTheDocument();
expect(screen.getByText("epistemic_state")).toBeInTheDocument();
expect(screen.getByText("coherent")).toBeInTheDocument();
expect(screen.getByText("decoded")).toBeInTheDocument();
// core + present-only metadata rows
expect(screen.getByText("role")).toBeInTheDocument();
expect(screen.getByText("assistant")).toBeInTheDocument();
expect(screen.getByText("energy_class")).toBeInTheDocument();
expect(screen.getByText("promotion_certificate_digest")).toBeInTheDocument();
// propositional_form headline
expect(screen.getByText("alpha causes beta")).toBeInTheDocument();
// copyable evidence-address handle
expect(screen.getByText("vault:7")).toBeInTheDocument();
// raw metadata drawer present
expect(screen.getByText("Raw metadata")).toBeInTheDocument();
// exact-recall doctrine: never a similarity/relevance proxy
expect(screen.queryByText(/similarity|relevance|score/i)).not.toBeInTheDocument();
});
it("renders 'not recorded' for absent core vault fields, not silent disappearance", () => {
function SetSparseVaultEntry() {
const { setSubject } = useEvidenceSubject();
useEffect(() => {
setSubject({
kind: "vault_entry",
entryIndex: 9,
data: { entry_index: 9, versor_digest: null },
});
}, [setSubject]);
return <RightInspector />;
}
render(
<EvidenceProvider>
<SetSparseVaultEntry />
</EvidenceProvider>,
);
expect(screen.getByText("Vault Entry")).toBeInTheDocument();
// epistemic_status, epistemic_state, turn, role all absent → 4 "not recorded"
expect(screen.getAllByText("not recorded").length).toBe(4);
// handle still present; no raw drawer when metadata is absent
expect(screen.getByText("vault:9")).toBeInTheDocument();
expect(screen.queryByText("Raw metadata")).not.toBeInTheDocument();
});
it("shows a transient Copied confirmation after an address copy", () => {
vi.useFakeTimers();
try {

View file

@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import { useEvidenceSubject, type EvidenceSubject } from "./evidenceContext";
import { EvidenceChainRail } from "./EvidenceChainRail";
import { MetadataTable } from "../design/components/MetadataTable/MetadataTable";
import { MetadataTable, type MetadataRow } from "../design/components/MetadataTable/MetadataTable";
import { DigestBadge } from "../design/components/DigestBadge/DigestBadge";
import { Timestamp } from "../design/components/Timestamp/Timestamp";
import {
@ -465,24 +465,115 @@ function LogosAlignmentEdgeInspector({
);
}
// Honest absence for a core identity field that every session entry should
// carry — shown rather than silently dropped.
const NOT_RECORDED = "not recorded";
// Read one metadata field as a display string, or null when genuinely absent.
// The vault metadata dict is open; values are strings/numbers/booleans (turn,
// role, energy_*, corrected, propositional_form, ...).
function vaultMetaValue(
metadata: Record<string, unknown> | undefined,
key: string,
): string | null {
if (!metadata) return null;
const value = metadata[key];
if (value === null || value === undefined) return null;
if (typeof value === "string") return value;
if (typeof value === "number" || typeof value === "boolean") return String(value);
return JSON.stringify(value);
}
// Deterministic, recursively key-sorted JSON for the raw drawer — same bytes
// every render regardless of backend key order, so the drawer never churns.
function stableJson(value: unknown): string {
return JSON.stringify(
value,
(_key, val) =>
val && typeof val === "object" && !Array.isArray(val)
? Object.fromEntries(
Object.keys(val as Record<string, unknown>)
.sort()
.map((k) => [k, (val as Record<string, unknown>)[k]]),
)
: val,
2,
);
}
// Optional rows surfaced only when the (open) metadata actually carries them —
// these don't exist on every entry, so absence is correct, not "not recorded".
const VAULT_OPTIONAL_FIELDS: { key: string; mono?: boolean; copyable?: boolean }[] = [
{ key: "corrected" },
{ key: "energy_class" },
{ key: "energy_raw", mono: true },
{ key: "coherence_residual", mono: true },
{ key: "promotion_certificate_digest", mono: true, copyable: true },
];
function VaultEntryInspector({ subject }: { subject: Extract<EvidenceSubject, { kind: "vault_entry" }> }) {
const { data } = subject;
const handle = `vault:${subject.entryIndex}`;
if (!data) {
return (
<div className="grid gap-3">
<h3 className="text-xs font-semibold text-[var(--color-text-secondary)]">Vault Entry</h3>
<p className="m-0 font-mono text-xs text-[var(--color-text-primary)]">#{subject.entryIndex}</p>
<DetailNotLoaded />
</div>
);
}
const metadata = data.metadata;
// Headline only a string propositional_form; structured forms stay in the drawer.
const propositionalForm =
metadata && typeof metadata.propositional_form === "string"
? metadata.propositional_form
: null;
// Core identity rows — always shown, with honest "not recorded" when absent.
// epistemic_status (storage tier) and epistemic_state (trust display) are
// distinct fields and must read distinctly.
const coreRows: MetadataRow[] = [
{ key: "epistemic_status", value: data.epistemic_status ?? NOT_RECORDED },
{ key: "epistemic_state", value: data.epistemic_state ?? NOT_RECORDED },
{ key: "turn", value: vaultMetaValue(metadata, "turn") ?? NOT_RECORDED, mono: true },
{ key: "role", value: vaultMetaValue(metadata, "role") ?? NOT_RECORDED },
];
const optionalRows: MetadataRow[] = VAULT_OPTIONAL_FIELDS.flatMap(({ key, mono, copyable }) => {
const value = vaultMetaValue(metadata, key);
return value === null ? [] : [{ key, value, mono, copyable }];
});
// Copyable handles: the evidence address and the versor digest.
const handleRows: MetadataRow[] = [
{ key: "handle", value: handle, mono: true, copyable: true },
...(data.versor_digest
? [{ key: "versor_digest", value: data.versor_digest, mono: true, copyable: true } as const]
: []),
];
return (
<div className="grid gap-3">
<h3 className="text-xs font-semibold text-[var(--color-text-secondary)]">Vault Entry</h3>
<p className="m-0 font-mono text-xs text-[var(--color-text-primary)]">#{subject.entryIndex}</p>
{data ? (
<MetadataTable
rows={[
...(data.epistemic_state ? [{ key: "epistemic", value: data.epistemic_state }] : []),
...(data.versor_digest
? [{ key: "versor", value: data.versor_digest, mono: true, copyable: true }]
: []),
]}
/>
) : (
<DetailNotLoaded />
)}
{propositionalForm ? (
<p className="m-0 text-sm text-[var(--color-text-primary)] [text-wrap:balance]">
{propositionalForm}
</p>
) : null}
<MetadataTable rows={[...coreRows, ...optionalRows, ...handleRows]} />
{metadata && Object.keys(metadata).length > 0 ? (
<details className="rounded-md border border-[var(--color-border-subtle)] bg-[var(--color-surface-raised)]">
<summary className="cursor-pointer px-3 py-2 text-xs text-[var(--color-text-secondary)]">
Raw metadata
</summary>
<pre className="m-0 max-h-80 overflow-auto border-t border-[var(--color-border-subtle)] px-3 py-2 font-mono text-xs text-[var(--color-text-primary)]">
{stableJson(metadata)}
</pre>
</details>
) : null}
</div>
);
}

View file

@ -62,7 +62,13 @@ export interface LogosPackSubjectData
export interface VaultEntrySubjectData {
entry_index?: number;
epistemic_status?: string;
epistemic_state?: string;
// The full open metadata dict the reader passes through. Already carried at
// runtime (selectEntry sets the complete VaultEntry); typed here so the
// inspector can surface it. Curated rows key off fields that actually exist;
// the raw drawer renders the rest.
metadata?: Record<string, unknown>;
versor_digest?: string | null;
}