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
8 changes: 7 additions & 1 deletion emrg/gui/renderer/css/components.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -1050,7 +1050,7 @@ dialog::backdrop {
.task-run-head,
.task-run-row {
display: grid;
grid-template-columns: 72px 1fr 92px;
grid-template-columns: 72px 1fr 92px 1fr;
gap: 6px;
font-size: var(--fs-aux);
min-width: 0;
Expand All@@ -1072,6 +1072,12 @@ dialog::backdrop {
white-space: nowrap;
color: var(--text-2);
}
.task-run-reason {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text-3);
}
.task-run-flag {
display: flex;
gap: 4px;
Expand Down
13 changes: 8 additions & 5 deletions emrg/gui/renderer/js/dialogs.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -714,6 +714,7 @@ const Dialogs = (() => {
head.appendChild(el("span", {}, _t("app.taskRunsColTime")));
head.appendChild(el("span", {}, _t("app.taskRunsColDone")));
head.appendChild(el("span", {}, _t("app.taskRunsColThrottle")));
head.appendChild(el("span", {}, _t("app.taskRunsColReason")));
wrap.appendChild(head);
for (const r of runs) {
const row = el("div", { class: "task-run-row" });
Expand All@@ -727,6 +728,9 @@ const Dialogs = (() => {
flagCell.appendChild(el("span", { class: "task-badge task-run-badge-idle" }, _t("app.taskRunIdle")));
}
row.appendChild(flagCell);
// rant 2026-08-19T18:25:14:原因列 —— 降频/空转判断的自然语言理由(vibe check reason)。
let reason = (typeof r.reason === "string" && r.reason) ? r.reason : "";
row.appendChild(el("span", { class: "task-run-reason" }, reason || "-"));
wrap.appendChild(row);
}
return wrap;
Expand DownExpand Up@@ -772,15 +776,14 @@ const Dialogs = (() => {
if (t.interval != null) hints.push(_t("app.taskInterval", { n: t.interval ?? "-" }));
if (t.enabled === false) hints.push(_t("app.taskDisabled"));
row.appendChild(el("span", { class: "task-hint" }, hints.join(" · ")));
// rant 2026-08-18T10:45:52:上次执行维度 —— 最近一次运行时间 + 摘要 + 降频(saturation)标识。
// 数据来自 daemon list_tasks → handler.status()(last_run_at/last_cycle_summary/saturation)。
// rant 2026-08-18T10:45:52:上次执行维度 —— 最近一次运行时间 + 降频(saturation)标识。
// rant 2026-08-19T18:25:14:一级列表不再显示"干了什么"(last_cycle_summary)——
// 执行记录(时间/工作/降频建议/原因)统一由点击展开的二级列表(buildTaskRunDetail)呈现。
// 数据来自 daemon list_tasks → handler.status()(last_run_at/saturation/recent_runs)。
const meta = el("div", { class: "task-meta" });
if (t.last_run_at) {
const runTxt = el("span", { class: "task-meta-item" }, _t("app.taskLastRun", { n: formatRelativeTime(t.last_run_at) }));
meta.appendChild(runTxt);
if (t.last_cycle_summary) {
meta.appendChild(el("span", { class: "task-meta-item task-meta-summary" }, _t("app.taskLastRunSummary", { n: t.last_cycle_summary })));
}
} else {
meta.appendChild(el("span", { class: "task-meta-item" }, _t("app.taskNoRunYet")));
}
Expand Down
4 changes: 2 additions & 2 deletions emrg/gui/renderer/js/i18n.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -409,13 +409,13 @@ const I18N = (() => {
"app.taskRunning": "任务 {n} 正在运行中(无法重复触发)",
"app.taskRunningBadge": "运行中",
"app.taskLastRun": "上次运行:{n}",
"app.taskLastRunSummary": "干了:{n}",
"app.taskNoRunYet": "尚未运行",
"app.taskSaturation": "空转 {n} 轮,降频至 {m}s heartbeat",
"app.taskRunsEmpty": "暂无运行记录",
"app.taskRunsColTime": "时间",
"app.taskRunsColDone": "干了什么",
"app.taskRunsColThrottle": "降频",
"app.taskRunsColReason": "原因",
"app.taskRunThrottle": "建议降频",
"app.taskRunIdle": "空转",
"app.deletedSwitch": "这个对话已被删除,已帮你切到最近的对话。",
Expand DownExpand Up@@ -839,13 +839,13 @@ const I18N = (() => {
"app.taskRunning": "Task {n} is running (cannot re-trigger)",
"app.taskRunningBadge": "running",
"app.taskLastRun": "last run: {n}",
"app.taskLastRunSummary": "did: {n}",
"app.taskNoRunYet": "never ran",
"app.taskSaturation": "{n} empty cycles, throttled to {m}s heartbeat",
"app.taskRunsEmpty": "No run records yet",
"app.taskRunsColTime": "Time",
"app.taskRunsColDone": "What was done",
"app.taskRunsColThrottle": "Throttle",
"app.taskRunsColReason": "Reason",
"app.taskRunThrottle": "slowdown advised",
"app.taskRunIdle": "idle",
"app.deletedSwitch": "This conversation was deleted — switched to the most recent one.",
Expand Down
19 changes: 14 additions & 5 deletions emrg/gui/test/renderer.smoke.test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2655,13 +2655,13 @@ test("rant 21:32:32:任务卡点击展开最近运行子表(时间/干了什
recent_runs: [
{ timestamp: "2026-08-18T10:00:00", summary: "修了双实例根因,提交 PR #854",
impact: ["cycle-ts-complete", "tools-executed=26"], meaningful: true,
recommend_slowdown: false, tool_count: 26 },
recommend_slowdown: false, tool_count: 26, reason: "meaningful work" },
{ timestamp: "2026-08-18T09:00:00", summary: "",
impact: ["cycle-ts-complete", "tools-executed=3"], meaningful: false,
recommend_slowdown: false, tool_count: 3 },
recommend_slowdown: false, tool_count: 3, reason: "" },
{ timestamp: "2026-08-18T08:00:00", summary: "NTE",
impact: ["cycle-ts-complete"], meaningful: false,
recommend_slowdown: true, tool_count: 0 },
recommend_slowdown: true, tool_count: 0, reason: "empty cycles, no value" },
],
},
{ name: "fresh-task", type: "sync", config: { project: "docs" }, interval: 3600, enabled: true },
Expand DownExpand Up@@ -2691,6 +2691,14 @@ test("rant 21:32:32:任务卡点击展开最近运行子表(时间/干了什
// 无 summary → 显示 "-"(rant 2026-08-19T07:06:45 宿主定稿:不再 fallback impact 机器串)
const doneTexts = vm.runInContext(`Array.from(document.getElementById("task-list").children[0].querySelectorAll(".task-run-done")).map((n) => n.textContent)`, ctx);
assert.strictEqual(doneTexts[1], "-", `无 summary 应显示 "-",实际: ${doneTexts[1]}`);
// rant 2026-08-19T18:25:14:原因列 —— 有 reason 显示原文,无 reason 显示 "-"
const reasonTexts = vm.runInContext(`Array.from(document.getElementById("task-list").children[0].querySelectorAll(".task-run-reason")).map((n) => n.textContent)`, ctx);
assert.strictEqual(reasonTexts[0], "meaningful work", `原因列应显示 reason,实际: ${reasonTexts[0]}`);
assert.strictEqual(reasonTexts[1], "-", `无 reason 应显示 "-",实际: ${reasonTexts[1]}`);
assert.strictEqual(reasonTexts[2], "empty cycles, no value", `原因列应显示降频原因,实际: ${reasonTexts[2]}`);
// rant 2026-08-19T18:25:14:一级列表不再显示"干了什么"(last_cycle_summary)
const primarySummary = vm.runInContext(`document.getElementById("task-list").children[0].querySelectorAll(".task-meta-summary").length`, ctx);
assert.strictEqual(primarySummary, 0, `一级列表不应显示 last_cycle_summary,实际数量: ${primarySummary}`);
// 再次点击 → 折叠
await vm.runInContext(`document.getElementById("task-list").children[0].click()`, ctx);
await tick();
Expand DownExpand Up@@ -2894,7 +2902,7 @@ test("rant 10:36:39:倒计时生命周期 —— 渲染启动 interval、离
assert.strictEqual(win2._intervalCount || 0, s2, "全 running 无倒计时 → 不启动 interval");
});

test("rant 10:45:52:任务行显示上次执行元信息 + 降频标识", async () => {
test("rant 10:45:52:任务行显示上次执行元信息 + 降频标识(rant 18:25:14:不再显示摘要)", async () => {
const nowMs = 1_700_000_000_000;
const { ctx, win } = makeSandbox({
listTasks: async () => [
Expand All@@ -2921,7 +2929,8 @@ test("rant 10:45:52:任务行显示上次执行元信息 + 降频标识", asyn
return name + " => " + metaText + " ## " + satText;
})`, ctx);
assert.ok(rows[0].includes("上次运行:5m ago"), `运行时间相对显示:${rows[0]}`);
assert.ok(rows[0].includes("干了:tools-executed=24, cycle-complete"), `执行摘要:${rows[0]}`);
// rant 2026-08-19T18:25:14:一级列表不再显示"干了什么"(last_cycle_summary),移入点击展开的二级列表
assert.ok(!rows[0].includes("干了:"), `一级列表不应显示摘要:${rows[0]}`);
assert.ok(rows[0].includes("空转 3 轮"), `降频徽标:${rows[0]}`);
assert.ok(rows[1].includes("尚未运行"), `未运行提示:${rows[1]}`);
assert.ok(!rows[1].includes("空转"), `无降频时无徽标:${rows[1]}`);
Expand Down
4 changes: 4 additions & 0 deletions emrg/protocol.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -133,6 +133,10 @@ class EvolutionLog:
summary: str = ""
meaningful: Optional[bool] = None
recommend_slowdown: bool = False
# rant 2026-08-19T18:25:14: the vibe check's natural-language reason for
# the meaningful/slowdown judgment, surfaced in the GUI task recent-runs
# secondary list (时间/工作/降频建议/原因).
reason: str = ""
tool_count: int = 0


Expand Down
7 changes: 7 additions & 0 deletions emrg/server/scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -454,6 +454,7 @@ def status(self) -> dict:
"impact": list(log.impact),
"meaningful": log.meaningful,
"recommend_slowdown": log.recommend_slowdown,
"reason": log.reason,
"tool_count": log.tool_count,
})
saturation = {
Expand DownExpand Up@@ -775,10 +776,15 @@ async def _run_evolution_cycle(self) -> None:
summary = ""
meaningful = None
recommend = False
reason = ""
if vibe_result is not None:
summary = str(vibe_result.get("done") or "")[:500]
meaningful = vibe_result.get("meaningful")
recommend = bool(vibe_result.get("recommend_slowdown"))
# rant 2026-08-19T18:25:14: keep the vibe check's reason for the
# meaningful/slowdown judgment so the GUI secondary list can show
# 原因 (time/work/throttle/reason).
reason = str(vibe_result.get("reason") or "")[:300]

log = EvolutionLog(
timestamp=cycle_ts,
Expand All@@ -788,6 +794,7 @@ async def _run_evolution_cycle(self) -> None:
summary=summary,
meaningful=meaningful,
recommend_slowdown=recommend,
reason=reason,
tool_count=tool_count,
)
# Rant 2026-08-19T14:18:40 — no disk archival: the evolution log lives
Expand Down
3 changes: 3 additions & 0 deletions tests/test_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -570,6 +570,7 @@ def test_evolution_handler_status_last_run_fields():
assert r0["impact"] == ["tools-executed=24", "cycle-complete"]
assert r0["meaningful"] is None
assert r0["recommend_slowdown"] is False
assert r0["reason"] == ""
assert r0["tool_count"] == 0
# agent summary preferred over machine impact tags
handler.evolutions.append(EvolutionLog(
Expand All@@ -580,13 +581,15 @@ def test_evolution_handler_status_last_run_fields():
summary="修了 stop_all 双实例根因,提交 PR #854",
meaningful=True,
recommend_slowdown=False,
reason="meaningful work done",
tool_count=5,
))
st = handler.status()
assert st["last_cycle_summary"] == "修了 stop_all 双实例根因,提交 PR #854"
assert len(st["recent_runs"]) == 2, "recent_runs holds last 5 runs"
assert st["recent_runs"][1]["summary"] == "修了 stop_all 双实例根因,提交 PR #854"
assert st["recent_runs"][1]["meaningful"] is True
assert st["recent_runs"][1]["reason"] == "meaningful work done"
assert st["recent_runs"][1]["tool_count"] == 5
assert st["recent_runs"][0]["meaningful"] is None

Expand Down
Loading