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
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import assert from "node:assert/strict";
import { renderToStaticMarkup } from "react-dom/server";

import type { DecisionResearchView } from "../src/data/decision-research";
import { fetchPresentationProjection, parsePresentationProjection, publishedResearchLineage, type DecisionResearchView } from "../src/data/decision-research";
import projectionFixture from "../src/data/fixtures/presentation-projection.example.json";
import { presentationSurfaceSchema } from "../src/data/status";
import {
SourcePeriodMetricsCard,
SpotMarketIdentityCard,
DecisionResearchSurface,
} from "../src/views/decision-research-surface";

type SourcePeriodMetric = DecisionResearchView["source_period_metrics"][number];
Expand Down Expand Up @@ -133,4 +136,60 @@ assert.match(spotMarkup, /SYN \/ USDC/);
assert.match(spotMarkup, /4.25/);
assert.match(spotMarkup, /missing \(not zero\)/);

console.log("Decision research source-period rendering coverage passed");
const projection = parsePresentationProjection(projectionFixture);
const surface = presentationSurfaceSchema.parse({
extension_id: projection.extension_id, extension_revision: projection.extension_revision,
surface_id: projection.surface_id, surface_kind: projection.surface_kind,
title: "Synthetic research", view_schema: projection.view_schema, visibility: "public-safe",
state: "review_due", goal_id: projection.goal_id, generated_at: projection.generated_at,
review_due_at: projection.review_due_at, diagnostic: null,
empty_state_title: "No research", empty_state_detail: "No projection",
detail_ref: {extension_id: projection.extension_id, surface_id: projection.surface_id,
extension_revision: projection.extension_revision, payload_sha256: projection.payload_sha256},
});
const markup = renderToStaticMarkup(<DecisionResearchSurface goals={[]} surface={surface}
view={projection.view} projection={{...projection, lineage: {...projection.lineage,
supersedes: ["synthetic-previous-source"]}}} />);
for (const value of [projection.goal_id, projection.generated_at, projection.review_due_at!,
"synthetic-research-2026-01-15", "Source version", "synthetic-previous-source",
projection.extension_revision, projection.payload_sha256, "does not extend the review deadline"]) {
assert(markup.includes(value), `Published reference must retain ${value}`);
}
assert.match(markup, /<details[^>]*data-testid="research-reference-details"/);
assert.deepEqual(publishedResearchLineage(), {sourceId: null, version: null, supersedes: null});
assert.deepEqual(publishedResearchLineage({...projection, lineage: {source_id: false, version: "1", supersedes: false}}),
{sourceId: null, version: null, supersedes: null});
const legacy = {...projection, review_due_at: null, lineage: undefined};
const legacyMarkup = renderToStaticMarkup(<DecisionResearchSurface goals={[]}
surface={{...surface, review_due_at: null}} view={legacy.view} projection={legacy} zh />);
assert.match(legacyMarkup, /原复核期限/);
assert.match(legacyMarkup, /未知/);
assert(!legacyMarkup.includes("synthetic-research-2026-01-15"), "Absent lineage must not be guessed from the view");

const originalFetch = globalThis.fetch;
let reply = projection;
globalThis.fetch = async () => new Response(JSON.stringify({ok: true, projection: reply}));
try {
assert.deepEqual(await fetchPresentationProjection("http://localhost/extension-projection", surface), projection);
for (const changes of [
{payload_sha256: "0".repeat(64)}, {extension_revision: "old-revision"},
{goal_id: "other-goal"}, {generated_at: "2026-01-16T12:00:00Z"},
{review_due_at: "2026-03-15T12:00:00Z"},
]) {
reply = {...projection, ...changes};
await assert.rejects(fetchPresentationProjection("http://localhost/extension-projection", surface),
/does not match the requested detail_ref/);
}
reply = projection;
assert.deepEqual(await fetchPresentationProjection("http://localhost/extension-projection", surface), projection);
await assert.rejects(fetchPresentationProjection("http://localhost/extension-projection",
{...surface, visibility: "owner-only"}), /public-safe/);
await assert.rejects(fetchPresentationProjection("http://localhost/extension-projection",
{extension_id: surface.extension_id, extension_revision: surface.extension_revision,
surface_id: surface.surface_id, surface_kind: surface.surface_kind, title: surface.title,
view_schema: surface.view_schema, visibility: "public-safe", goal_id: null, generated_at: null,
review_due_at: null, diagnostic: null, empty_state_title: "Empty", empty_state_detail: "Disabled", state: "empty"}),
/no published projection/);
} finally {globalThis.fetch = originalFetch;}

console.log("Decision research source-period and published-reference coverage passed");
15 changes: 15 additions & 0 deletions apps/presentation/dashboard/src/data/decision-research.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,19 @@ export type PresentationProjectionEnvelope = z.infer<
typeof presentationProjectionEnvelopeSchema
>;

/** Lineage comes from the resolved envelope, never the status summary or view. */
export function publishedResearchLineage(projection?: PresentationProjectionEnvelope | null) {
const lineage = projection?.lineage;
const source = z.string().min(1).refine(value => value.trim().length > 0).safeParse(lineage?.source_id);
const version = z.number().int().positive().safeParse(lineage?.version);
const supersedes = z.array(z.string().min(1)).max(20).safeParse(lineage?.supersedes);
return {
sourceId: source.success ? source.data : null,
version: version.success ? version.data : null,
supersedes: supersedes.success ? supersedes.data : null,
};
}

export function parsePresentationProjection(
payload: unknown,
): PresentationProjectionEnvelope {
Expand Down Expand Up @@ -434,6 +447,8 @@ export async function fetchPresentationProjection(
|| projection.view_schema !== surface.view_schema
|| projection.surface_kind !== surface.surface_kind
|| projection.goal_id !== surface.goal_id
|| projection.generated_at !== surface.generated_at
|| projection.review_due_at !== surface.review_due_at
) {
throw new Error("projection response does not match the requested detail_ref");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {useEffect, useState} from "react";
import {RefreshCw} from "lucide-react";
import {fetchPresentationProjection, type DecisionResearchView} from "../../data/decision-research";
import {fetchPresentationProjection, type PresentationProjectionEnvelope} from "../../data/decision-research";
import {parsePresentationSurfaceCollectionResponse, type PresentationSurface, type RunGoal} from "../../data/status";
import {DecisionResearchSurface} from "../../views/decision-research-surface";

export type GoalResearchApi = {indexUrl: string; detailUrl: string; goals: RunGoal[]};
type Result = {surface: PresentationSurface; view?: DecisionResearchView; error?: string};
type Result = {surface: PresentationSurface; projection?: PresentationProjectionEnvelope; error?: string};

/** The active Goal's published research; the extension index owns availability. */
export function GoalResearchResults({goalId, api, zh}: {goalId: string; api: GoalResearchApi; zh: boolean}) {
Expand All @@ -29,7 +29,7 @@ export function GoalResearchResults({goalId, api, zh}: {goalId: string; api: Goa
if (surface.state !== "ready" && surface.state !== "review_due") return {surface};
try {
const projection = await fetchPresentationProjection(api.detailUrl, surface);
return {surface, view: projection.view};
return {surface, projection};
} catch (failure) {return {surface, error: String(failure)};}
}));
if (current) setResults(next);
Expand All @@ -50,6 +50,7 @@ export function GoalResearchResults({goalId, api, zh}: {goalId: string; api: Goa
{error ? <p role="alert">{zh ? "无法核验研究结果;旧内容已清除。" : "Cannot verify research results; previous content was cleared."} {error}</p> : null}
{!busy && !error && !results.length ? <p>{zh ? "本 Goal 暂无可读取的公开研究成果。" : "No readable public research results for this Goal yet."}</p> : null}
{results.map(result => <DecisionResearchSurface key={`${result.surface.extension_id}:${result.surface.surface_id}`}
goals={api.goals} surface={result.surface} view={result.view} viewError={result.error}/>) }
goals={api.goals} surface={result.surface} view={result.projection?.view}
projection={result.projection} viewError={result.error} zh={zh}/>) }
</section>;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { CircleAlert, Clock3, FileSearch, ShieldCheck } from "lucide-react";

import type { PresentationSurface, RunGoal } from "../data/status";
import type { DecisionResearchView } from "../data/decision-research";
import {
publishedResearchLineage,
type DecisionResearchView,
type PresentationProjectionEnvelope,
} from "../data/decision-research";
import { Badge } from "../components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";

Expand Down Expand Up @@ -63,10 +67,26 @@ function TextList({ empty, items }: { empty: string; items: string[] }) {
);
}

function ProvenanceCard({ surface }: { surface: PresentationSurface }) {
function ProvenanceCard({ surface, projection, zh }: {
surface: PresentationSurface;
projection?: PresentationProjectionEnvelope | null;
zh: boolean;
}) {
if (surface.state !== "ready" && surface.state !== "review_due") {
return null;
}
const lineage = publishedResearchLineage(projection);
const unknown = zh ? "未知" : "unknown";
const references = [
[zh ? "原 Goal" : "Original Goal", surface.goal_id],
[zh ? "来源编号" : "Source ID", lineage.sourceId],
[zh ? "来源版本" : "Source version", lineage.version],
[zh ? "替代来源" : "Supersedes", lineage.supersedes === null ? null
: lineage.supersedes.length ? lineage.supersedes.join(", ") : zh ? "无" : "none"],
[zh ? "扩展 / 展示面" : "Extension / surface", `${surface.extension_id} / ${surface.surface_id}`],
[zh ? "展示 schema" : "View schema", surface.view_schema],
[zh ? "扩展 revision" : "Extension revision", surface.detail_ref.extension_revision],
];
return (
<Card data-testid="decision-research-surface-summary">
<CardHeader>
Expand All @@ -77,50 +97,53 @@ function ProvenanceCard({ surface }: { surface: PresentationSurface }) {
<Badge variant="neutral">{surface.visibility}</Badge>
</div>
<p className="mt-3 text-sm leading-6 text-slate-600 dark:text-zinc-300">
Rendered from the published projection resolved by content-addressed
reference. Core status remains a compact provider-neutral index.
{zh ? "已发布研究的原始时间与来源引用;刷新页面不延长复核期限。"
: "Original publication times and source reference. Refreshing does not extend the review deadline."}
</p>
</div>
<FileSearch className="h-5 w-5 text-slate-400 dark:text-zinc-500" />
</CardHeader>
<CardContent>
<dl className="grid gap-3 text-sm sm:grid-cols-2">
<div>
<dt className="text-[11px] font-semibold uppercase tracking-[0.12em] text-slate-500 dark:text-zinc-400">
View schema
</dt>
<dd className="mt-1 font-mono text-slate-800 dark:text-zinc-200">
{surface.view_schema}
</dd>
</div>
<div>
<dt className="text-[11px] font-semibold uppercase tracking-[0.12em] text-slate-500 dark:text-zinc-400">
Generated at
<dt className="text-xs font-semibold text-slate-500 dark:text-zinc-400">
{zh ? "发布时间" : "Published at"}
</dt>
<dd className="mt-1 text-slate-800 dark:text-zinc-200">
{surface.generated_at}
</dd>
</div>
<div>
<dt className="text-[11px] font-semibold uppercase tracking-[0.12em] text-slate-500 dark:text-zinc-400">
Extension revision
<dt className="text-xs font-semibold text-slate-500 dark:text-zinc-400">
{zh ? "原复核期限" : "Original review deadline"}
</dt>
<dd className="mt-1 font-mono text-slate-800 dark:text-zinc-200">
{surface.detail_ref.extension_revision}
</dd>
</div>
<div>
<dt className="text-[11px] font-semibold uppercase tracking-[0.12em] text-slate-500 dark:text-zinc-400">
Payload SHA-256
</dt>
<dd
className="mt-1 break-all font-mono text-xs text-slate-800 dark:text-zinc-200"
data-testid="research-detail-ref-hash"
>
{surface.detail_ref.payload_sha256}
<dd className="mt-1 text-slate-800 dark:text-zinc-200" data-testid="research-review-deadline">
{surface.review_due_at ?? unknown}
</dd>
</div>
</dl>
<details className="mt-4 text-sm" data-testid="research-reference-details">
<summary className="cursor-pointer font-medium">
{zh ? "来源与引用详情" : "Source and reference details"}
</summary>
<dl className="mt-3 grid gap-3 sm:grid-cols-2">
{references.map(([label, value]) => <div key={String(label)}>
<dt className="text-xs font-semibold text-slate-500 dark:text-zinc-400">{label}</dt>
<dd className="mt-1 break-all font-mono text-xs text-slate-800 dark:text-zinc-200">{value ?? unknown}</dd>
</div>)}
<div>
<dt className="text-xs font-semibold text-slate-500 dark:text-zinc-400">
Payload SHA-256
</dt>
<dd
className="mt-1 break-all font-mono text-xs text-slate-800 dark:text-zinc-200"
data-testid="research-detail-ref-hash"
>
{surface.detail_ref.payload_sha256}
</dd>
</div>
</dl>
</details>
</CardContent>
</Card>
);
Expand Down Expand Up @@ -318,12 +341,16 @@ export function DecisionResearchSurface({
view = null,
viewError = null,
viewLoading = false,
projection = null,
zh = false,
}: {
goals: RunGoal[];
surface: PresentationSurface;
view?: DecisionResearchView | null;
viewError?: string | null;
viewLoading?: boolean;
projection?: PresentationProjectionEnvelope | null;
zh?: boolean;
}) {
if (surface.state === "invalid") {
return (
Expand Down Expand Up @@ -416,7 +443,7 @@ export function DecisionResearchSurface({
Loading the full research view…
</p>
) : null}
<ProvenanceCard surface={surface} />
<ProvenanceCard surface={surface} projection={projection} zh={zh} />
</>
) : (
<>
Expand Down Expand Up @@ -837,7 +864,7 @@ export function DecisionResearchSurface({
</Card>
</div>

<ProvenanceCard surface={surface} />
<ProvenanceCard surface={surface} projection={projection} zh={zh} />
</>
)}
</div>
Expand Down
Loading