diff --git a/.claude/agent-memory/planner/MEMORY.md b/.claude/agent-memory/planner/MEMORY.md index 315b793..5203846 100644 --- a/.claude/agent-memory/planner/MEMORY.md +++ b/.claude/agent-memory/planner/MEMORY.md @@ -4,3 +4,4 @@ - [deploy_health_check.py HTTP-timeout comment missing](project_deploy_health_check_http_timeout_comment_missing.md) — HEALTH_CHECK_HTTP_TIMEOUT (line 26) has no comment directly above it, unlike its sibling STARTUP_TIMEOUT; distinct open fact, don't conflate the two - [artifact overview click-through bug: RESOLVED](project_artifact_overview_click_through_bug.md) — re-verified 2026-08-06, all 3 gaps fixed (data-artifact-id, #/artifacts/:id route, mountArtifactBrowser seeding); re-check before trusting "resolved" long-term - [org-strip click-through bug: 3 render sites, not 1](project_org_strip_click_through_bug.md) — renderOrgStrip (dashboard), v_task_detail's inline org-strip, and #/graph's renderOrgGraphTopology all share the same no-click gap; DAG/log/artifact display mechanisms already exist and must be reused, not rebuilt; no new backend endpoint needed +- [run-history click → missing run detail page](project_run_history_click_missing_detail_page.md) — gc_runs_list click only switches task selector, no #/runs/:run_id route exists (documented trade-off, not a bug regression); DAG-panel+log-viewer funcs are run-object-generic and reusable verbatim; backend needs one new by-run_id query since _work_nodes_for_run already exists keyed by run_id diff --git a/.claude/agent-memory/planner/project_run_history_click_missing_detail_page.md b/.claude/agent-memory/planner/project_run_history_click_missing_detail_page.md new file mode 100644 index 0000000..ff21adb --- /dev/null +++ b/.claude/agent-memory/planner/project_run_history_click_missing_detail_page.md @@ -0,0 +1,59 @@ +--- +name: project-run-history-click-missing-detail-page +description: Investigation findings for "运行历史(gc_runs_list)点击跳转错误 + 缺运行详情页" — root cause confirmation and exact reusable pieces, needed for SDD/Benchmark content of this task. +metadata: + type: project +--- + +Investigated 2026-08-06 for "控制台运行历史点击后跳转错误的 bug,并补上缺失的运行详情页" +(graph_manifest planned same session). Confirmed by reading the code (not by trusting the task's +framing): + +**Root cause, confirmed exactly as stated**: `mountArtifactsOverviewPanel()` +(`frontend/app.js:1530-1580`) renders `#gc_runs_list` from the real, working +`GET /api/work-graph-runs?project_id=` (`work_graph_run_query.list_work_graph_runs`). Row click only +calls `onSwitchToTask(li.dataset.runTaskId)` → `mountGraphConsole`'s `loadTaskAndRender`, which just +re-selects that task in `#gc_task_select`. There is genuinely no run-detail route today — the +existing inline comment at app.js:1524-1529 documents this as a deliberate FR-5 trade-off ("Graph +run rows are the one exception... since there's no separate 'run detail' route"), not an oversight +to silently work around. + +**What already exists and must be reused, not rebuilt**: +- `renderGraphVisualizerPanel(run, {enableNodeClick:true})` (app.js:981-1025) + `mountWorkNodeClickExpand` + (app.js:1053-1062) + `renderLogViewerHtml`/`mountLogViewer` (app.js:1078-1140) is the *entire* + "node list + per-node live/static log" mechanism already, used today per-task inside + `mountGraphConsole`. It takes one `run` object shaped `{run_id, org_node, graph_manifest, nodes: [...]}` + — nothing here is task-scoped internally except how the caller obtains that `run` object. A new + `#/runs/:run_id` page can call these same functions directly. +- `mountLogViewer` already implements exactly requirement #2 (live WS tail via `connectLogTailSocket` + → `/api/ws/work-nodes/:id/log` for non-terminal nodes, falls back to static `GET + /api/work-nodes/:id/log` on close/terminal status). Do not touch backend log write/push code. +- Router pattern to copy: hash routes with a dynamic segment already exist for `#/tasks/:task_id`, + `#/artifacts/:artifact_id`, `#/skills/:skill_id/audit` (app.js:77-106, regex-matched in `render()`, + not a full path router). `#/runs/:run_id` should follow the exact same `ROUTE_RE` + `render()` + branch pattern, not a new routing mechanism. + +**Backend gap, confirmed real**: no endpoint queries a single `work_graph_run` by `run_id` today. +`task_query.get_task` (`src/task_query.py:115-171`) builds `work_graph_runs` (each including +`nodes` via the already-existing private helper `_work_nodes_for_run(conn, run_id)`, +task_query.py:174-198) but only filtered `WHERE task_id = ?`. The cheapest correct fix: add a +sibling query function (e.g. in `work_graph_run_query.py`, which already exists and already owns +the "query work_graph_run across/outside a single task_id" responsibility per its own docstring) +that does `SELECT run_id, task_id, org_node, graph_manifest, status, started_at, completed_at FROM +work_graph_run WHERE run_id = ?` and calls the same `_work_nodes_for_run` (may need exporting from +task_query or duplicating the one query — duplicating a single small query is cheaper than +cross-module coupling here; Code Agent should pick based on how `_work_nodes_for_run` is +imported/underscored elsewhere). New route: `GET /api/work-graph-runs/:run_id`, 404 if missing +(mirrors `_handle_execution_log_get`'s NotFound handling). + +**How to apply**: when writing the SDD, scope this as (a) one new backend query fn + one new GET +route returning `{run_id, task_id, org_node, status, started_at, completed_at, nodes: [...]}`, +(b) one new hash route `#/runs/:run_id` composing the existing DAG-panel + log-viewer functions +verbatim, (c) a one-line change to `gc_runs_list`'s click handler +(`window.location.hash = "#/runs/" + li.dataset.runId` — note: needs `data-run-id` added next to +the existing `data-run-task-id`, since the run_id itself isn't currently a `data-*` attribute on +that `