diff --git a/apps/presentation/dashboard/smoke/projection-localization-smoke.ts b/apps/presentation/dashboard/smoke/projection-localization-smoke.ts index abf0dca94b..9e3e34ae51 100644 --- a/apps/presentation/dashboard/smoke/projection-localization-smoke.ts +++ b/apps/presentation/dashboard/smoke/projection-localization-smoke.ts @@ -1,7 +1,6 @@ import { agentStatusSentence, projectionSentence, - runEvidenceCopy, } from "../src/features/personal-workspace/projection-localization.js"; type Values = Record; @@ -14,19 +13,11 @@ const english = { "projection.agentStopped": "Stopped by you; history, Todos, and evidence are preserved", "projection.agentWaitingExternal": "Waiting for an external condition", "projection.confirmAgentDecision": "Confirm the permission or decision the Agent needs next", - "projection.events24h": "{count} events in the last 24 hours", "projection.firstReadOnlyAdapterCheck": "Run the first read-only adapter check and save progress", - "projection.goalVerified": "Goal state, Todos, and registration information verified", - "projection.latestRun": "Latest run", - "projection.latestValidation": "Latest validation", "projection.nextUpdatePending": "Waiting for LoopX to update the next step", - "projection.publicSafeProjection": "Public-safe status projection", "projection.refreshState": "Refresh LoopX status and confirm the current progress is still valid", - "projection.runEvidenceAvailable": "Run evidence is available", - "projection.runRecorded": "The latest LoopX run is recorded", "projection.statusRefreshNeeded": "LoopX status needs to be refreshed", "projection.todoStatusUpdated": "Todo status updated; confirming the next step", - "projection.validationRecorded": "The latest validation is recorded", } as const; const chinese = { @@ -97,16 +88,4 @@ equal( "English stopped status", ); -const latestRun = runEvidenceCopy({ eventCount: 0, hasArtifact: true, hasLatestValidation: false }, en); -equal(latestRun.label, "Latest run", "English latest-run label"); -equal(latestRun.metadata, "Run evidence is available", "English run-evidence metadata"); - -const latestValidation = runEvidenceCopy({ eventCount: 3, hasArtifact: false, hasLatestValidation: true }, en); -equal(latestValidation.label, "Latest validation", "English latest-validation label"); -equal(latestValidation.metadata, "3 events in the last 24 hours", "English event-count metadata"); - -const chineseRun = runEvidenceCopy({ eventCount: 0, hasArtifact: false, hasLatestValidation: false }, zhCN); -equal(chineseRun.label, "最近运行", "Chinese latest-run label"); -equal(chineseRun.metadata, "公开安全状态投影", "Chinese public-safe metadata"); - console.log("projection localization smoke passed"); diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-team-results.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-team-results.tsx index 07b0f8c9f2..dc2ab35fa1 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-team-results.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-team-results.tsx @@ -7,20 +7,22 @@ import {GoalTeamLineage} from "./goal-team-lineage"; type Selection = {operationId: string; ref?: string; sha256?: string}; const readableRows = (page: DelegationInventory) => page.items.filter(row => row.operation_id && row.status === "accepted" && !row.recovery_required && row.artifacts?.length); +const AUTO_DISCOVERY_PAGES = 3; +const MAX_INSPECTED_PAGES = 10; /** Read-only entry in the original conversation, using the same scoped delegation API. */ export function GoalTeamResults({sessionId, zh, refreshKey}: {sessionId: string; zh: boolean; refreshKey: string}) { - const [page, setPage] = useState(null); + const [pages, setPages] = useState([]); const [selection, setSelection] = useState<{result: DelegationReadback; artifact: TeamArtifact} | null>(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const generation = useRef(0); const chosen = useRef(null); - const cursor = useRef(undefined); + const inspectedPages = useRef(1); const focusReport = useRef(false); const report = useRef(null); - useEffect(() => {chosen.current = null; cursor.current = undefined;}, [sessionId]); - useEffect(() => {void list(cursor.current); return () => {generation.current++;};}, [sessionId, refreshKey]); + useEffect(() => {chosen.current = null; inspectedPages.current = 1;}, [sessionId]); + useEffect(() => {void list(); return () => {generation.current++;};}, [sessionId, refreshKey]); useEffect(() => { if (selection && focusReport.current) {report.current?.focus(); focusReport.current = false;} }, [selection]); @@ -39,21 +41,33 @@ export function GoalTeamResults({sessionId, zh, refreshKey}: {sessionId: string; setSelection({result: value, artifact}); } } - async function list(nextCursor?: string, nextPage = false) { + async function list(nextPage = false) { const current = ++generation.current; if (nextPage) chosen.current = null; - cursor.current = nextCursor; + const minimumPages = Math.min(MAX_INSPECTED_PAGES, inspectedPages.current + (nextPage ? 1 : 0)); focusReport.current = false; - setBusy(true); setError(""); setPage(null); setSelection(null); + setBusy(true); setError(""); setPages([]); setSelection(null); try { - const value = await fetchLoopXTeamWork(sessionId, nextCursor); - if (current !== generation.current) return; - setPage(value); + const observed: DelegationInventory[] = []; + let nextCursor: string | undefined; + while (observed.length < minimumPages || + (!chosen.current && !observed.some(page => readableRows(page).length) && observed.length < AUTO_DISCOVERY_PAGES)) { + const value = await fetchLoopXTeamWork(sessionId, nextCursor); + if (current !== generation.current) return; + observed.push(value); + if (!value.has_more) break; + if (!value.next_cursor || (nextCursor && value.next_cursor <= nextCursor)) { + throw new Error(zh ? "执行分页无法继续核验;请重新读取。" : "Execution paging cannot be verified; refresh the list."); + } + nextCursor = value.next_cursor; + } + inspectedPages.current = observed.length; + setPages(observed); // Keep the reader's exact version. A refresh must never silently replace // it with a newer artifact or move to a different member's result. let target = chosen.current; if (!target) { - const first = readableRows(value)[0]; + const first = observed.flatMap(readableRows)[0]; const artifact = first?.artifacts?.find(row => isMarkdownArtifact(row.ref)) ?? first?.artifacts?.[0]; if (first?.operation_id && artifact) target = {operationId: first.operation_id, ...artifact}; } @@ -74,7 +88,8 @@ export function GoalTeamResults({sessionId, zh, refreshKey}: {sessionId: string; catch (failure) {if (current === generation.current) setError(`${zh ? "无法核验,已清除上次报告。" : "Cannot verify; previous report cleared."} ${String(failure)}`);} finally {if (current === generation.current) setBusy(false);} } - const rows = page ? readableRows(page) : []; + const rows = pages.flatMap(readableRows); + const lastPage = pages.at(-1); const adoptions = selection?.result.adoptions ?? []; const currentAdoptions = adoptions.filter(row => row.state === "current").length; const unavailableAdoptions = adoptions.length - currentAdoptions; @@ -89,18 +104,21 @@ export function GoalTeamResults({sessionId, zh, refreshKey}: {sessionId: string; : (zh ? "尚无采用记录 · 查看验收与版本依据" : "No adoption recorded · inspect acceptance and versions"); return

{zh ? "团队成果" : "Team results"}

-
+ {busy ?

{zh ? "正在核验产物…" : "Verifying artifacts…"}

: null} {error ?

{error}

: null} - {page && !page.page_readback_complete ?

{zh ? "部分工作无法核验,请在团队执行中检查。" : "Some work cannot be verified; inspect Team execution."}

: null} + {pages.some(page => !page.page_readback_complete) ?

{zh ? "已检查的工作中有无法核验的记录;当前报告只代表它自己的验收,不代表整个团队。" : "Some inspected work cannot be verified. This report reflects only its own acceptance, not the whole team."}

: null}
- {page ?