From f06fccb98a544c8c65461acb48697cf9407ae11a Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Sun, 8 Nov 2026 04:11:58 +0800 Subject: [PATCH] Distinguish prepared research input from analyzer judgments Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../smoke/research-analysis-read-smoke.mjs | 7 +++--- .../goal-research-analysis.tsx | 3 ++- .../goal-research-progress.tsx | 22 +++++++++++++------ .../research-history.mjs | 11 +++++++++- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/apps/presentation/dashboard/smoke/research-analysis-read-smoke.mjs b/apps/presentation/dashboard/smoke/research-analysis-read-smoke.mjs index cfb660366f..4e08f6317b 100644 --- a/apps/presentation/dashboard/smoke/research-analysis-read-smoke.mjs +++ b/apps/presentation/dashboard/smoke/research-analysis-read-smoke.mjs @@ -11,7 +11,7 @@ import {resolveTestPython} from "../../../../scripts/test-python.mjs"; import {readAnalysisSnapshot, ResearchAnalysisPanel} from "../src/features/personal-workspace/goal-research-analysis.tsx"; import {ResearchCaseStudy} from "../src/features/personal-workspace/research-case-study.tsx"; import {fetchExploreResults} from "../src/data/chat.ts"; -import {ResearchGoalNavigation} from "../src/features/personal-workspace/goal-research-workbench.tsx"; +import {GoalResearchWorkbench} from "../src/features/personal-workspace/goal-research-workbench.tsx"; // The persisted directory can organize explicitly linked physical sources. // Navigation must not turn organization into shared evidence or execution. @@ -30,8 +30,9 @@ const goals = [ {goalId:'child',parentGoalId:'parent',title:'Local case'}, {goalId:'foreign',parentGoalId:'parent',title:'Foreign case',sourceAccess:{namespace:'other',label:'Other',readOnly:true}}, ]; -const navigate = goalId => renderToStaticMarkup(createElement(ResearchGoalNavigation, { - goalId, goals, zh:false, onSelect: () => {throw Error('read navigated');}, +const navigate = goalId => renderToStaticMarkup(createElement(GoalResearchWorkbench, { + goalId, goals, zh: false, active: true, refreshRevision: 0, + statusNotice: createElement("p", null, "Loading status"), onSelectGoal: () => {throw Error('read navigated');}, })); assert.ok(navigate('parent').includes('Local case')); assert.ok(navigate('parent').includes('Foreign case')); diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-analysis.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-analysis.tsx index 211cb001b7..085f9f582b 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-analysis.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-analysis.tsx @@ -572,7 +572,8 @@ export function ResearchAnalysisPanel({analysis: a, onHistory}: {analysis: Contr : benefit?.unknown ? (zh ? "原目标收益的检验情况仍未知" : "Original-objective utility testing remains unknown") : benefit?.yes ? (zh ? "模型记录了收益检验;这不等于收益为正" : "The model identified a utility test; this does not imply positive utility") : (zh ? "此方法未覆盖原目标收益检验" : "This method does not cover original-objective utility testing"); - const scope = p ? <>{p.episode_count} {zh ? "个研究片段" : "research episodes"} · {p.selection.exhaustive ? (zh ? "固定范围" : "Fixed scope") : (zh ? "定向抽样" : "Purposive sample")} : null; + const episodeLabel = result ? (zh ? "个研究片段" : "research episodes") : (zh ? "个输入片段" : "input episodes"); + const scope = p ? <>{p.episode_count} {episodeLabel} · {p.selection.exhaustive ? (zh ? "固定范围" : "Fixed scope") : (zh ? "定向抽样" : "Purposive sample")} : null; const scopeHelp = p ? {result?.synthesis ?

{zh ? "Agent 分析 · 未校准" : "Agent analysis · uncalibrated"}

: null}

{p.selection.exhaustive ? (zh ? "覆盖此固定分析范围;不等于覆盖全部研究。" : "Covers this fixed analysis scope, not necessarily the whole research history.") : (zh ? "定向抽样,仅分析选定研究片段;范围外或未捕获的工作未纳入,不代表全程。" : "Purposive sample of selected episodes; work outside this scope or not captured is excluded. It does not represent the whole run.")}

diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.tsx index b80de116f5..62b963b118 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.tsx @@ -281,19 +281,27 @@ export function ResearchProgress({pack: p, packs, selectedId, zh, onSelect, onCi const incomplete = usage?.incomplete_turn; const coverage = usage?.request_coverage; const number = (n: number) => new Intl.NumberFormat(zh ? "zh-CN" : "en", {maximumFractionDigits: 1}).format(n); + const scope = p ? `${number(p.episode_count)} ${zh ? "个片段" : "episodes"} · ${p.selection.exhaustive ? (zh ? "固定范围" : "Fixed scope") : (zh ? "定向抽样" : "Purposive sample")}` : ""; const duration = (s: number) => `${number(s / 60)} ${zh ? "分钟" : "min"}`; const stamp = (s: string) => new Intl.DateTimeFormat(zh ? "zh-CN" : "en", {timeZone: "UTC", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false}).format(new Date(s)); return

{p?.native_outcomes ? (zh ? "研究结果" : "Research results") : (zh ? "研究效果判读" : "Research effectiveness")}

{historical ?

{zh ? "来源或方法已变化;历史捕获与判读不能当作当前结果。" : "The source or method changed; historical captures and judgments cannot establish current results."}

: null} - {p && !result ?

{zh ? "可先浏览已采集的测量与记录;深度分析需单独点击,不会随浏览自动运行。" : "Captured measurements and records remain readable. Deep analysis requires a separate action and never runs while browsing."}

: null} + {p && !result ? <> +

{zh ? "分析输入" : "Analysis input"} · {scope}

+

{zh ? "可先浏览已采集的测量与记录;深度分析需单独点击,不会随浏览自动运行。" : "Captured measurements and records remain readable. Deep analysis requires a separate action and never runs while browsing."}

+ : null} {p ?

{p.title}

: null} -

{zh ? "分析器判读未校准,仅限所选片段;范围外或未捕获的工作未纳入。数字为“是 / 适用片段”,未知仍在分母中;点选指标可看依据。" : "Uncalibrated judgments of selected episodes only; work outside this scope or not captured is excluded. Counts are yes / eligible episodes, including unknowns. Select a metric for evidence."}

-

{zh ? "不适用来自原始片段的明确取样规则,不算“否”或“未知”;展开指标可看被排除的片段。" : "Ineligible episodes follow the source's explicit selection, not a no or unknown judgment. Expand a metric to identify them."}

-

{zh ? "收益检验的“是”不保证收益为正;无益重复的“是”表示发现问题。六项分别判断,不合成总分、成功率或原生收益。" : "Yes for benefit testing does not imply positive utility; yes for repetition identifies a problem. The six judgments are not a total score, success rate or native utility."}

-

{p?.selection.exhaustive ? (zh ? "仅覆盖声明的固定分析范围,不等于覆盖全部研究。" : "Covers the declared fixed analysis scope, not necessarily the whole study.") : p?.selection.population_episodes == null ? (zh ? "定向抽样,全程覆盖未知。" : "Purposive sample; whole-run coverage is unknown.") : (zh ? "定向抽样,不代表全程。" : "Purposive sample, not the whole run.")}

-

{zh ? "原生测量缺失时保持未采集;分析器判读不能补成实际收益。完整取样规则与版本在“来源与用量”中。" : "Missing native measurements remain uncaptured; judgments cannot supply realized utility. Full selection rules and revisions are under Sources and usage."}

-
{p ?

{number(p.episode_count)} {zh ? "个片段" : "episodes"} · {p.selection.exhaustive ? (zh ? "固定范围" : "Fixed scope") : (zh ? "定向抽样" : "Purposive sample")}{result ? (zh ? " · 未校准" : " · Uncalibrated") : ""}

: null}
{packs.length > 1 ? : null}
+ {result ? <> +

{zh ? "分析器判读未校准,仅限所选片段;范围外或未捕获的工作未纳入。数字为“是 / 适用片段”,未知仍在分母中;点选指标可看依据。" : "Uncalibrated judgments of selected episodes only; work outside this scope or not captured is excluded. Counts are yes / eligible episodes, including unknowns. Select a metric for evidence."}

+

{zh ? "不适用来自原始片段的明确取样规则,不算“否”或“未知”;展开指标可看被排除的片段。" : "Ineligible episodes follow the source's explicit selection, not a no or unknown judgment. Expand a metric to identify them."}

+

{zh ? "收益检验的“是”不保证收益为正;无益重复的“是”表示发现问题。六项分别判断,不合成总分、成功率或原生收益。" : "Yes for benefit testing does not imply positive utility; yes for repetition identifies a problem. The six judgments are not a total score, success rate or native utility."}

+ : null} + {p ? <> +

{p.selection.exhaustive ? (zh ? "仅覆盖声明的固定分析范围,不等于覆盖全部研究。" : "Covers the declared fixed analysis scope, not necessarily the whole study.") : p.selection.population_episodes == null ? (zh ? "定向抽样,全程覆盖未知。" : "Purposive sample; whole-run coverage is unknown.") : (zh ? "定向抽样,不代表全程。" : "Purposive sample, not the whole run.")}

+

{zh ? "原生测量缺失时保持未采集;分析器判读不能补成实际收益。完整取样规则与版本在“来源与用量”中。" : "Missing native measurements remain uncaptured; judgments cannot supply realized utility. Full selection rules and revisions are under Sources and usage."}

+ :

{zh ? "当前没有可读的分析范围。" : "No analysis scope is available."}

} + {p ?

{result ? `${scope}${zh ? " · 未校准" : " · Uncalibrated"}` : (zh ? "尚无分析器判读" : "No analyzer judgments yet")}

: null}{packs.length > 1 ? : null} {p ? <> {p.native_outcomes ? : null} {p.native_outcomes ? : null} diff --git a/examples/personal-workspace-browser/research-history.mjs b/examples/personal-workspace-browser/research-history.mjs index 21981ce0f3..a960c11f7b 100644 --- a/examples/personal-workspace-browser/research-history.mjs +++ b/examples/personal-workspace-browser/research-history.mjs @@ -364,7 +364,12 @@ export const researchHistoryScenario = { assert.doesNotMatch(await native.locator('.research-native-scope').innerText(), /T00:00:00Z/); assert.equal(await native.locator('.research-native-basis').evaluate(e => e.open), false); assert.equal(await nativePage.locator('.research-progress-basis').evaluate(e => e.open), false); - assert.equal(await nativePage.getByRole('button',{name:'所选范围尚未分析',exact:true}).isVisible(), true, 'Measured progress remains readable before model analysis; the unknown analysis is contextual help'); + const pendingScope = nativePage.locator('.research-progress-scope'); + assert.equal(await pendingScope.innerText(), '尚无分析器判读', 'Prepared input counts cannot be mistaken for completed judgments'); + const pendingHelp = nativePage.getByRole('button', {name: '所选范围尚未分析', exact: true}); + await pendingHelp.focus(); + assert.match(await nativePage.getByRole('tooltip').innerText(), /分析输入 · \d+ 个片段/); + await nativePage.keyboard.press('Escape'); assert.equal(await nativePage.locator('.research-progress > header .context-help-trigger').count(), 1, 'Result state and interpretation share one heading help entry'); await nativePage.screenshot({path: resolve(outputDir, 'research-native-default-desktop.png'), animations: 'disabled'}); await native.getByRole('button', {name: /首次提交.*查看原始证据/}).click(); @@ -1675,6 +1680,7 @@ Path(sys.argv[1]).write_text(json.dumps({'goal_id':goal,'display_name':'Syntheti await progress.getByRole("combobox").focus(); await progress.getByRole("combobox").selectOption("c".repeat(64)); await progress.locator(`.research-progress-scope[data-pack-id="${"c".repeat(64)}"]`).waitFor(); + assert.match(await progress.locator('.research-progress-scope').innerText(), /1 个片段 · .*未校准/, 'Stored judgments retain their visible coverage and calibration boundary'); assert.equal(await progress.locator('.research-progress-basis > summary').getByRole('button',{name:/完整成本未知/}).isVisible(),true,'The cost boundary remains discoverable with the details closed'); await progress.locator('.research-progress-basis > summary').click(); assert.match(await progress.locator('.research-usage-heading').first().innerText(), /未采集|Not captured/); @@ -1713,6 +1719,7 @@ Path(sys.argv[1]).write_text(json.dumps({'goal_id':goal,'display_name':'Syntheti assert(await unfinishedHelp.evaluate(element => element === document.activeElement)); await progress.locator('.research-progress-basis > summary').click(); assert.equal(await progress.getByRole('button',{name:'所选范围尚未分析',exact:true}).isVisible(),true); + assert.equal(await progress.locator('.research-progress-scope').innerText(), '尚无分析器判读'); assert.equal(await nativeResults.isVisible(), true, 'An unanalysed pack still exposes its recorded outcomes with cost details closed'); assert.equal(await progress.locator('.research-effect-metrics').count(), 0, 'An unanalysed cost pack never borrows another pack’s judgments'); assert.equal(await analysis.count(), 0, 'Choosing an unanalysed source never requests analysis'); @@ -1730,7 +1737,9 @@ Path(sys.argv[1]).write_text(json.dumps({'goal_id':goal,'display_name':'Syntheti await analysis.getByRole('button', {name: '重试读取', exact: true}).click(); await analysis.getByRole('alert').waitFor({state: 'hidden'}); assert(await analysis.getByRole('button', {name: '分析所选范围', exact: true}).isEnabled(), 'A successful owner read restores the existing explicit analysis action'); + assert.match(await analysis.locator('.analysis-scope').innerText(), /\d+ 个输入片段/); await analysis.press('Escape'); + await analysis.waitFor({state: 'hidden'}); await basis.locator(':scope > summary').click(); assert.equal(await nativeResults.locator('[data-native-point="retained"] strong').textContent(), '3', 'Native facts do not require an analyzer result'); assert.match(await nativeResults.innerText(), /部分窗口 · 起点未知/);