feat: complete evidence restructuring worktree

This commit is contained in:
Codex
2026-08-26 11:39:02 +02:00
parent a54d4769dd
commit 38f02cfd08
56 changed files with 1981 additions and 1801 deletions
+31
View File
@@ -50,6 +50,37 @@ test("accepts the immutable workspace revision contract", async () => {
await expect(getWorkspace("psd-clinical")).resolves.toEqual({ workspace, revision });
});
test("accepts the evidence schema version materialized by the backend", async () => {
const persistedWorkspace = {
...workspace,
evidence: {
schema_version: 1,
source: {
type: "filesystem",
uri: "psd-clinical/evidence",
patterns: ["**/*.md"],
max_bytes: 10 * 1024 * 1024,
},
policy: { max_chunk_chars: 5000, retain_published_generations: 3 },
},
};
server.use(http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
workspace: persistedWorkspace,
revision,
})));
await expect(getWorkspace("psd-clinical")).resolves.toEqual({
workspace: {
...persistedWorkspace,
evidence: {
source: persistedWorkspace.evidence.source,
policy: persistedWorkspace.evidence.policy,
},
},
revision,
});
});
test("decodes runtime requirements but rejects any secret value returned by the server", async () => {
server.use(http.get(
"/api/workspaces/psd-clinical/runtime-configuration",
@@ -159,6 +159,7 @@ test("opens Workspace management from the right sidebar without interrupting the
server.use(
http.get("/api/workspace-registry/status", () =>
HttpResponse.json({ branch: "main", ahead: 0, behind: 0, degraded: false })),
http.get("/api/health/dwh", () => HttpResponse.json({ ok: false })),
);
renderShell();
@@ -168,6 +169,21 @@ test("opens Workspace management from the right sidebar without interrupting the
expect(screen.getByTestId("app-shell")).toHaveAttribute("data-activity-layout", "closed");
});
test("does not block the shell when the DWH is unavailable at startup", async () => {
let healthChecks = 0;
server.use(
http.get("/api/health/dwh", () => {
healthChecks += 1;
return HttpResponse.json({ ok: false });
}),
);
renderShell();
expect(await screen.findByRole("button", { name: "Workspace management" })).toBeVisible();
expect(screen.queryByRole("heading", { name: "Connection unavailable" })).not.toBeInTheDocument();
expect(healthChecks).toBe(0);
});
test("marks the composer as awaiting input for a pending freetext gate", () => {
useSessionStore.setState({
-36
View File
@@ -23,7 +23,6 @@ import { toast } from "sonner";
import {
closeSession, listSessions, resumeSession, getSession,
renameSession, setSessionGroup, archiveSession, unarchiveSession, deleteSession, prewarmRuntime,
checkDwhHealth,
} from "../api/sessions";
import { logout as logoutUser } from "../api/auth";
import {
@@ -129,23 +128,8 @@ export function AppShell({ canLogout }: AppShellProps) {
const [stopConfirm, setStopConfirm] = useState(false);
const [collapsedGroups, setCollapsedGroups] = useState<Record<string, boolean>>({});
const [renameGroupTarget, setRenameGroupTarget] = useState<string | null>(null);
const [dwhDown, setDwhDown] = useState(false);
const [dwhChecking, setDwhChecking] = useState(true);
const [dwhCheckEpoch, setDwhCheckEpoch] = useState(0);
const operationEpochRef = useRef(0);
useEffect(() => () => { operationEpochRef.current += 1; }, []);
useEffect(() => {
let cancelled = false;
const operation = captureAuthOperation({ disposalEpoch: operationEpochRef.current });
if (!operation) return () => { cancelled = true; };
setDwhChecking(true);
checkDwhHealth().then((r) => {
if (cancelled || !isAuthOperationCurrent(operation, { disposalEpoch: operationEpochRef.current })) return;
setDwhDown(!r.ok);
setDwhChecking(false);
});
return () => { cancelled = true; };
}, [dwhCheckEpoch]);
const groups = useMemo(
() => [...new Set(sessions.map((s) => s.group).filter((g): g is string => !!g))].sort(),
@@ -958,26 +942,6 @@ export function AppShell({ canLogout }: AppShellProps) {
}}
/>
)}
{dwhDown && (
<Dialog open onOpenChange={() => {}}>
<DialogContent showCloseButton={false} className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Connection unavailable</DialogTitle>
<DialogDescription>
The database is unreachable. Check the VPN connection and try again.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
disabled={dwhChecking}
onClick={() => setDwhCheckEpoch((e) => e + 1)}
>
{dwhChecking ? "Checking…" : "Retry"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)}
</div>
);
}
+1 -1
View File
@@ -197,7 +197,7 @@ function PiInstructionSteps({ details }: { details: PiPlatformDetails }) {
</li>
<li>
<h4 className="font-semibold text-foreground">Edit the provider catalog</h4>
<p className="mt-1 text-muted-foreground">Edit <code>{details.modelsPath}</code> only when adding or correcting a provider/model definition. The provider catalog is an address book/map of the services Pi can call: each entry supplies the API endpoint and format, and lists the model identifiers offered there. It does not enable a model and never contains credentials.</p>
<p className="mt-1 text-muted-foreground">Edit <code>{details.modelsPath}</code> only when adding or correcting a provider/model definition. The provider catalog is an address book/map of the services Pi can call: each entry supplies the API endpoint and format, and lists the model identifiers offered there. It does not enable a model and never contains credentials. Provider integrations must remain declarative; do not add model-provider code under <code>harness/.pi/extensions/</code>.</p>
<dl className="mt-2 grid grid-cols-[auto_1fr] gap-x-2 gap-y-1 text-muted-foreground">
<dt className="font-mono text-foreground">baseUrl </dt><dd>is the provider API endpoint.</dd>
<dt className="font-mono text-foreground">api </dt><dd>selects the provider API format.</dd>
+8 -2
View File
@@ -313,7 +313,11 @@ function copyS3Evidence(value: unknown): EvidenceSource | undefined {
}
function copyEvidence(value: unknown, id: string): WorkspaceEvidence | undefined {
const source = exactRecord(value, ["source", "policy"]);
// The backend's canonical YAML parser materializes the evidence schema version
// in responses, even though the browser draft does not need to retain it.
const source = exactRecord(value, ["schema_version", "source", "policy"]);
const schemaVersion = source?.schema_version;
if (!source) return undefined;
if (!source) return undefined;
const type = record(source.source)?.type;
const evidenceSource = type === "filesystem"
@@ -324,7 +328,9 @@ function copyEvidence(value: unknown, id: string): WorkspaceEvidence | undefined
? copyS3Evidence(source.source)
: undefined;
const policy = copyEvidencePolicy(source.policy);
return evidenceSource && policy ? { source: evidenceSource, policy } : undefined;
return (schemaVersion === 1 || schemaVersion === 2) && evidenceSource && policy
? { source: evidenceSource, policy }
: undefined;
}
function oneOf<T extends string>(value: unknown, choices: readonly T[]): T | undefined {