---
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
`
` 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 `` 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.