project_artifact_overview_click_through_bug.md 3.41 KB

name: project-artifact-overview-click-through-bug description: "RESOLVED 2026-08-06 (re-verified by direct file read): all 3 gaps fixed — data-artifact-id, #/artifacts/:id route, and mountArtifactBrowser(initialArtifactId) all present." metadata:

type: project

RESOLVED as of 2026-08-06 (re-verified by reading frontend/app.js directly while investigating the unrelated Org Graph click-through bug, see [[project-org-strip-click-through-bug]]): all three gaps below have been fixed. renderArtifactSummaryList/renderArtifactSummaryList's <li> now carries data-artifact-id (app.js:1360), ARTIFACT_DETAIL_RE (#/artifacts/:id) is a real dynamic route wired in render() (app.js:99,125-129), and mountArtifactBrowser(initialArtifactId) (app.js:1603) seeds selectedArtifactId from it and calls the existing showPreview() flow. Don't re-flag this as open without re-reading the file first — but do keep re-verifying, per CLAUDE.md's general warning that autonomous self-hosting runs can fix or re-break things between sessions.

Original investigation (2026-08-04), kept for history — root cause was real and narrower than it first looked, three separate gaps, not one:

  1. renderArtifactSummaryList() (frontend/app.js:1344) renders bare <li> with no data-artifact-id and no click handler — confirmed, matches the task's stated root cause.
  2. The router (frontend/app.js:77-132, routes object + render()) does exact-string lookup on window.location.hash (routes[hash]), with only two regex-based dynamic routes today: TASK_DETAIL_RE (#/tasks/:id) and SKILL_AUDIT_RE (#/skills/:id/audit). There is no query-string parsing anywhere in the router — a hash like #/artifacts?artifact_id=xxx would fail routes[hash] and both regexes and fall through to "Not found". Fixing this bug requires extending routing (either a new #/artifacts/:id-style dynamic route matched the same way as TASK_DETAIL_RE, or adding query-string stripping/parsing to render() before route lookup) — it is not just a matter of adding an href.
  3. mountArtifactBrowser() (frontend/app.js:1582) already has the exact mechanism needed to display a specific artifact once you have its id — showPreview(artifactId) sets selectedArtifactId and fetches /api/artifacts/:id — but it is only ever invoked from a click on li[data-artifact-id] inside refreshList(), never from a URL param. The comment at app.js:1353-1356 ("v_artifact_browser and v_task_board don't parse URL query params today ... out of scope") was written about the "查看全部" link specifically (intentionally unfiltered) — it is not evidence that per-item deep-linking is already supported or was rejected; don't misread it as covering this case.

How to apply: when writing the SDD for this task, the technical design must cover (a) adding data-artifact-id + click → hash navigation in renderArtifactSummaryList, (b) a route-level change (new dynamic route or hash query-parsing) so #/artifacts can carry an artifact id, and (c) mountArtifactBrowser reading that id on mount and calling the existing showPreview()/refreshList() flow to preselect it — reusing c_artifact_get / GET /api/artifacts/:artifact_id as-is, no new content-preview logic. The "查看全部" links (app.js:1387-1390) must keep pointing at plain #/artifacts / #/tasks — do not add the id param to those.