From 8a735722bf3214122461170065393c0e31d0bb6a Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:45:43 +0800 Subject: [PATCH 1/3] refactor(workspace): converge information hierarchy on overview and research history Refactor Goal overview worker readback and research history execution observation to follow the 'Explanations On Demand' contract in design.md: - Fold multi-paragraph worker readback notes and execution observation disclaimers into contextual Help glyphs (ContextHelp) rather than cluttering primary reading space. - Default-collapse worker observation technical metadata (Agent, obligation, turn, time). - Add responsive grid rules for 390px narrow viewports on worker status and research controls. - Extend personal workspace contract assertions to verify structured help and collapsed states. Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../personal-workspace/goal-overview.css | 26 ++++++---- .../personal-workspace/goal-overview.tsx | 47 ++++++++++++------- .../goal-research-history.css | 2 +- .../goal-research-history.tsx | 4 +- .../personal-workspace-contract.test.mjs | 15 ++++++ 5 files changed, 65 insertions(+), 29 deletions(-) diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.css b/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.css index fc1718ae03..5f3a3c6cc0 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.css +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.css @@ -204,14 +204,24 @@ .goal-outcome-requirement > summary > small { grid-column: 2; max-width: none; padding-top: 0; } } /* Driver observations remain separate from the Goal's activation and acceptance. */ -.goal-overview-worker-state { display: grid; gap: 8px; padding-block: 16px; border-block: 1px solid var(--pw-line); } -.goal-overview-worker-state h3 { color: var(--pw-muted); font-size: var(--text-caption); font-weight: 500; line-height: var(--leading-caption); } -.goal-overview-worker-state p { margin-block: .5rem; overflow-wrap: anywhere; } -.goal-overview-worker-state details { margin-block: .5rem; } -.goal-overview-worker-state dl { color: var(--pw-muted); font-size: var(--text-caption); line-height: var(--leading-caption); } -.goal-overview-worker-state dt { font-weight: 600; } -.goal-overview-worker-state dd { margin: .2rem 0 .6rem; overflow-wrap: anywhere; } -.goal-overview-worker-state > button { justify-self: start; } +.goal-overview-worker-state { display: grid; gap: 8px; padding-block: 14px; border-block: 1px solid var(--pw-line); } +.goal-overview-worker-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; } +.goal-overview-worker-header h3 { color: var(--pw-muted); font-size: var(--text-caption); font-weight: 500; line-height: var(--leading-caption); margin: 0; } +.goal-overview-worker-row { display: grid; gap: 6px; } +.goal-overview-worker-status { display: flex; align-items: center; gap: 8px; } +.goal-overview-worker-status strong { color: var(--pw-text); font-size: var(--text-body); font-weight: 500; line-height: var(--leading-body); } +.goal-overview-worker-details { margin-block: 4px; } +.goal-overview-worker-details summary { cursor: pointer; color: var(--pw-muted); font-size: var(--text-caption); line-height: var(--leading-caption); } +.goal-overview-worker-details summary:hover { color: var(--pw-text); } +.goal-overview-worker-state dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 12px; margin-top: 6px; color: var(--pw-muted); font-size: var(--text-caption); line-height: var(--leading-caption); } +.goal-overview-worker-state dt { font-weight: 500; color: var(--pw-faint); } +.goal-overview-worker-state dd { margin: 0; overflow-wrap: anywhere; } +.goal-overview-worker-action { justify-self: start; min-height: 28px; padding: 4px 8px; border: 1px solid var(--pw-line); border-radius: 6px; background: transparent; color: var(--pw-muted); font-size: var(--text-caption); cursor: pointer; transition: background-color var(--motion-fast) var(--ease-standard), color var(--motion-fast) var(--ease-standard); } +.goal-overview-worker-action:hover { background: var(--pw-hover); color: var(--pw-text); } +@media (max-width: 640px) { + .goal-overview-worker-state dl { grid-template-columns: minmax(0, 1fr); } + .goal-overview-worker-status { flex-wrap: wrap; } +} .goal-overview-evidence { margin-block: 20px; border-top: 1px solid var(--pw-line); } .goal-overview-evidence > summary { display: flex; align-items: center; gap: 12px; min-height: 44px; padding-block: 12px; cursor: pointer; font-weight: 600; } .goal-overview-evidence > summary::before { content: "▸"; color: var(--pw-muted); } diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.tsx index 471ee39d26..7b507bd32f 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.tsx @@ -139,26 +139,37 @@ function WorkerReadback({goal, zh, onViewTasks}: {goal: WorkspaceGoal; zh: boole const source = goal.workerObservations; if (!source) return null; return
-

{zh ? "执行器最近的观察" : "Last worker observation"}

- {source.read_status !== "read" ?

{zh - ? "执行器记录缺失有效来源或与当前目标配置不一致。当前执行结果未知。" - : "The worker record is unreadable or does not match the current Goal configuration. Its current outcome is unknown."}

: null} - {source.rows.map(row =>
-

{row.status === "replan_paused" ? (zh ? "需要方向,已暂停复核" : "Direction needed; review paused") - : (zh ? "原请求结果未知,已暂停新派发" : "Original outcome unknown; new dispatch paused")}

-

{row.status === "replan_paused" ? (zh +

+

{zh ? "执行器观察" : "Worker observation"}

+ {source.read_status !== "read" ?

{zh + ? "执行器记录缺失有效来源或与当前目标配置不一致。当前执行结果未知。" + : "The worker record is unreadable or does not match the current Goal configuration. Its current outcome is unknown."}

: null} +
+ {source.rows.map(row => { + const statusTitle = row.status === "replan_paused" + ? (zh ? "需要方向,已暂停复核" : "Direction needed; review paused") + : (zh ? "原请求结果未知,已暂停新派发" : "Original outcome unknown; new dispatch paused"); + const statusExplanation = row.status === "replan_paused" ? (zh ? "本次复核没有解决原义务。补充具体任务或相关结果,再重启执行器以检查当前准入。" : "The review did not resolve its original obligation. Add concrete work or relevant results, then restart the worker to check current admission.") : (zh ? "先核实原操作及其结算。已发送请求仍可能产生费用;新事件或重启不能证明原请求未执行。" - : "Check the original operation and its settlement first. An issued request may still incur costs; a new event or restart cannot prove it did not run.")}

-

{zh ? "此记录不证明 Goal 达成;完整费用未知。" : "This record does not establish Goal completion. Complete costs are unknown."}

-
{zh ? "原工作与观察时间" : "Original work and observation time"} -
Agent
{row.agent_id}
-
{zh ? "义务" : "Obligation"}
{row.obligation_id}
-
{zh ? "原回合" : "Original Turn"}
{row.turn_instance_id ?? (zh ? "未绑定,需原 Host 核实" : "Unbound; check the original Host")}
-
{zh ? "记录时间" : "Observed at"}
-
-
)} - + : "Check the original operation and its settlement first. An issued request may still incur costs; a new event or restart cannot prove it did not run."); + return
+
+ {statusTitle} + +

{statusExplanation}

+

{zh ? "此记录不证明 Goal 达成;完整费用未知。" : "This record does not establish Goal completion. Complete costs are unknown."}

+
+
+
{zh ? "原工作与观察时间" : "Original work and observation time"} +
Agent
{row.agent_id}
+
{zh ? "义务" : "Obligation"}
{row.obligation_id}
+
{zh ? "原回合" : "Original Turn"}
{row.turn_instance_id ?? (zh ? "未绑定,需原 Host 核实" : "Unbound; check the original Host")}
+
{zh ? "记录时间" : "Observed at"}
+
+
; + })} +
; } diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.css b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.css index ced97b1564..4bc3a3cec5 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.css +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.css @@ -90,7 +90,7 @@ .research-history-read-error > button:hover, .research-history-empty > button:hover { background: var(--pw-hover); } .research-history-error { color: var(--pw-red); font-size: var(--text-body); overflow-wrap: anywhere; } .research-history button:focus-visible, .research-history-canvas:focus-visible, .research-history-detail:focus-visible { outline: 2px solid var(--pw-blue); outline-offset: -2px; } -@media (max-width: 640px) { .research-history-heading { flex-wrap: wrap; gap: 12px; } .research-history-controls { --research-control-height: 44px; width: 100%; column-gap: 4px; } .research-history-canvas { max-height: 350px; } .research-history-detail { padding: 20px; } .research-history-read-error, .research-history-empty { flex-wrap: wrap; } .research-history-read-error > button, .research-history-empty > button { margin-inline-start: 30px; } } +@media (max-width: 640px) { .research-history-heading { flex-wrap: wrap; gap: 12px; } .research-history-controls { --research-control-height: 44px; width: 100%; column-gap: 4px; } .research-history-canvas { max-height: 350px; } .research-history-detail { padding: 16px; } .research-history-read-error, .research-history-empty { flex-wrap: wrap; } .research-history-read-error > button, .research-history-empty > button { margin-inline-start: 0; margin-top: 8px; } } @media (prefers-reduced-motion: reduce) { .research-history * { animation: none !important; transition: none !important; scroll-behavior: auto !important; } } .research-history-edges text { fill: var(--pw-muted); font-size: var(--text-caption); paint-order: stroke; stroke: var(--pw-hover); stroke-width: 4px; } diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx index 2d06208282..57e2c221a4 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx @@ -143,7 +143,7 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re const findingBody = (finding: ExploreHistoryEvent) => { const execution = finding.execution_observation; const measured = (value: unknown) => value === null ? (zh ? "未测量" : "Not measured") : String(value); - return <>{finding.measurements ? : null}{findingNarrative(finding)}{execution ?
{zh ? "实际执行与资源观测" : "Execution and resource observations"} + return <>{finding.measurements ? : null}{findingNarrative(finding)}{execution ?
{zh ? "实际执行与资源观测" : "Execution and resource observations"}

{zh ? "来源记录的执行观测;不等于方法有效或当前候选已通过验收。" : "Source-recorded execution observations; not proof of method benefit or current-candidate acceptance."}

{zh ? "计划方法 → 实际方法" : "Planned → executed method"}
{String(execution.planned_method_id)} → {String(execution.executed_method_id)}
{execution.change_reason ? <>
{zh ? "改向原因" : "Reason for change"}
{String(execution.change_reason)}
: null} @@ -152,7 +152,7 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
{zh ? "结束原因" : "Termination reason"}
{measured(execution.stop_reason)}
{zh ? "执行候选 SHA-256" : "Executed candidate SHA-256"}
{String(execution.candidate_sha256)}
{execution.result_ref ? <>
{zh ? "该候选的结果引用" : "Result for this candidate"}
{String(execution.result_ref)}
: null} -

{zh ? "来源记录的执行观测;不等于方法有效或当前候选已通过验收。" : "Source-recorded execution observations; not proof of method benefit or current-candidate acceptance."}

+
: null}; }; const findingMeta = (finding: ExploreHistoryEvent) => {timeLabel(finding.recorded_at)} · {stateLabel(finding.status)}{finding.confidence !== null ? ` · ${zh ? "来源自报置信度" : "Source-reported confidence"} ${finding.confidence}` : ""}; diff --git a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs index 1ff069e483..d0e55e5634 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs +++ b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs @@ -722,3 +722,18 @@ assert.match(source("./context-drawer.tsx"), /selection\.item\.gate\?\.kind === for (const key of ["proposal.agentGate.title", "proposal.agentGate.summary", "proposal.agentGate.reason.executable_not_found", "proposal.agentGate.reason.api_key_not_set", "proposal.agentGate.reason.unregistered", "proposal.agentGate.reason.unavailable", "proposal.agentGate.nextAction", "proposal.agentGate.impact"]) { assert.equal((i18n.match(new RegExp(`"${key.replace(/\./g, "\\.")}":`, "g")) ?? []).length, 2, `${key} exists in both locales`); } + +// S5/S11 Design hierarchy convergence & Explanations on Demand: +// Layout explains itself; auxiliary explanation prose in overview & research history +// is collapsed into ContextHelp instead of cluttering the primary reading view. +const overview = source("./goal-overview.tsx"); +const overviewCss = source("./goal-overview.css"); +const researchHistory = source("./goal-research-history.tsx"); +const researchHistoryCss = source("./goal-research-history.css"); + +assert.match(overview, /goal-overview-worker-status[\s\S]*? Date: Tue, 3 Nov 2026 13:29:19 +0800 Subject: [PATCH 2/3] fix(workspace): align worker readback context help label with browser smoke contract Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../dashboard/src/features/personal-workspace/goal-overview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.tsx index 7b507bd32f..54f97a3148 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-overview.tsx @@ -157,7 +157,7 @@ function WorkerReadback({goal, zh, onViewTasks}: {goal: WorkspaceGoal; zh: boole return
{statusTitle} - +

{statusExplanation}

{zh ? "此记录不证明 Goal 达成;完整费用未知。" : "This record does not establish Goal completion. Complete costs are unknown."}

From 238bc58bb4e974cc4c089db1d65c0935000c7fa6 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Tue, 3 Nov 2026 14:05:29 +0800 Subject: [PATCH 3/3] fix(workspace): wrap execution observations summary text in span for browser test locator Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../src/features/personal-workspace/goal-research-history.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx index 57e2c221a4..5d797a2d6b 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx @@ -143,7 +143,7 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re const findingBody = (finding: ExploreHistoryEvent) => { const execution = finding.execution_observation; const measured = (value: unknown) => value === null ? (zh ? "未测量" : "Not measured") : String(value); - return <>{finding.measurements ? : null}{findingNarrative(finding)}{execution ?
{zh ? "实际执行与资源观测" : "Execution and resource observations"}

{zh ? "来源记录的执行观测;不等于方法有效或当前候选已通过验收。" : "Source-recorded execution observations; not proof of method benefit or current-candidate acceptance."}

+ return <>{finding.measurements ? : null}{findingNarrative(finding)}{execution ?
{zh ? "实际执行与资源观测" : "Execution and resource observations"}

{zh ? "来源记录的执行观测;不等于方法有效或当前候选已通过验收。" : "Source-recorded execution observations; not proof of method benefit or current-candidate acceptance."}

{zh ? "计划方法 → 实际方法" : "Planned → executed method"}
{String(execution.planned_method_id)} → {String(execution.executed_method_id)}
{execution.change_reason ? <>
{zh ? "改向原因" : "Reason for change"}
{String(execution.change_reason)}
: null}