name: project-nav-current-project-context-prd description: PRD "导航栏『当前项目』上下文(项目名+任务状态摘要)" reviewed 2026-08-06 for internal contradictions — none found metadata:
type: project
Reviewed the Planner-stage PRD titled "导航栏『当前项目』上下文(项目名 + 任务状态摘要)"
(nav bar current-project display FR-1/2, clickable task-status-count summary FR-3/4,
query-string parsing fix in frontend/app.js's render() FR-5, refresh-on-route-change
FR-6; explicitly scoped out a new backend aggregation endpoint and nav-bar WebSocket live
refresh) for internally contradictory requirements. Found none — the backend-endpoint
scope-out is reasoned (existing no-pagination precedent + documented revisit threshold)
and the route-change-only refresh (FR-6) doesn't conflict with skipping WebSocket live
refresh (discrete-refresh vs push-refresh are different, compatible mechanisms).
Why: worth recording because a sibling PRD in the same pipeline (the v_graph_console
Graph page PRD, see frontend/.graphlemon/runs/7400d876-a797-4d87-bcff-4e59a3befba1/prd.md)
did have a real self-contradiction in its v1 draft (claimed both "shared render code with
v_task_detail" and "v_task_detail's click behavior unaffected" with no reconciliation
mechanism) that got caught by this same consistency-review step and fixed in v2 via an
opt-in enableNodeClick parameter — so this check is not pro forma, it has caught a real
issue before in this codebase.
How to apply: existing baseline in docs/sdd/02-V-界面与交互.md §nav only documents
"当前项目名 + 切换项目链接" — no task-status-count summary yet. That's an SDD gap this PRD
fills, not a contradiction. If this PRD proceeds to SDD stage, frontend/app.js's actual
render() (line 118) only does hash-path regex routing (TASK_DETAIL_RE etc.) — no
query-string (?key=value) parsing exists there at all today, so FR-5 ("fix") is really
"add"; flag that wording precisely if it recurs at SDD/Coder stage (accuracy issue, not a
contradiction, so it wasn't in-scope for this consistency review).
2026-08-07 re-confirmed while planning a Work Graph for the same goal (nav re-read fresh:
frontend/index.html's <nav> is still 8 static <a>s, no widget). Reconfirmed facts, not
yet superseded:
render()(app.js:118) is still the single right place to refresh the nav widget — both existingsetCurrentProjectId()call sites (mountProjectList's "选择" button, app.js:230;mountProjectSetup's create-project success handler, app.js:353) already immediately setwindow.location.hash, which fireshashchange→render()on its own; no new event wiring needed for "switch project → nav updates".- Precedent for the "no new backend aggregation endpoint" call already exists in shipped code,
not just reasoning:
mountBudgetSettings(app.js ~2423-2429) already doesfetch("/api/tasks?project_id=..&status=blocked_pending_human")and uses.lengthfor a count. Generalizing this to all statuses = one unfilteredGET /api/tasks?project_id=...fetch + client-side reduce overTASK_STATUSES(app.js:844-847) — single round trip, not 9.task_query.list_taskshas no LIMIT/OFFSET anywhere (checkedsrc/task_query.py), so "no pagination" is the whole codebase's existing convention, not a gap being introduced here. - New open decision surfaced this pass that the earlier PRD review didn't record: the user's
example "done 12 · running 2 · pending 3" implies grouping graphlemon's 10 raw task
statuses into ~3-4 display buckets, not a literal 10-number readout — PRD must pick and
spell out the bucket→raw-status mapping explicitly (product decision), and must also decide
the click target when a bucket maps to >1 raw status, since
task_query.list_tasks'sstatusparam only accepts one value at a time. render()'s nava.activetoggle (app.js:159-161) and itsroutes[hash]/regex-table lookup both do exact/full-hash matching — once a nav link produces#/tasks?status=done,render()must split path from query before route dispatch, or the lookup breaks. This is a requiredrender()change, called out explicitly so SDD doesn't file it as a frontend-only add unrelated to routing.