diff --git a/docs/workspace-panel-todo.md b/docs/workspace-panel-todo.md
index cf73d5d8..f9e657fb 100644
--- a/docs/workspace-panel-todo.md
+++ b/docs/workspace-panel-todo.md
@@ -481,17 +481,31 @@ work disagree permanently about the roster, with neither told — a direct contr
and `refetchOnWindowFocus` gated on a module-level in-flight count plus a 2 s quiet period.
- [ ] **Preserve sibling sizes on split.** `splitInner`/`insertPanel` redistribute evenly
(`100 / newChildren.length`), so one split discards carefully tuned proportions.
-- [ ] **`tpl-N` panel ids collide across dashboards.** `DashboardPreview.tsx:25-26` —
+- [x] **`tpl-N` panel ids collide across dashboards.** `DashboardPreview.tsx:25-26` —
`let tplCounter = 0; const tplUid = () => \`tpl-${++tplCounter}\`` — module-level, no entropy,
resets every page load. Two dashboards created from templates after a reload hold panels with
**identical ids**, and panel id keys `terminal-conn-${panelId}`and`file-viewer:${panelId}`.
- Use the core's `uid()` (`layout-utils.ts:4`), which stamps `p-${Date.now()}-${n}`. One line.
+ **Resolved `6fd60e5`** — the templates now call the core's `uid()`, which is exported from the
+ Workspace barrel for the first time so that there is exactly one way to mint a panel id. The
+ duplicate minter is deleted rather than fixed: a second implementation of "make me an id" is how
+ this happened, and the collision was no longer only a settings mix-up — `agent_panels` addresses a
+ panel by `(dashboardId, panelId)`, so two dashboards built from templates in the same page load
+ could hand two different agents the same address.
### 5.6 Registry
-- [ ] **No duplicate-key guard.** `metasToRegistry` is `Object.fromEntries`
- (`state/src/useAppRegistry.ts:19`) — a collision silently last-wins and one app just disappears.
- All 44 keys are unique today. Throw in dev.
+- [x] **No duplicate-key guard.** `metasToRegistry` is `Object.fromEntries`
+ (`AppRegistry/useAppRegistry/useAppRegistry.ts:19`) — a collision silently last-wins and one app
+ just disappears.
+ **Resolved `6fd60e5`**, but not by throwing, which is what this item asked for. A duplicate key is
+ an authoring mistake made at edit time; throwing punishes the owner at runtime for it, by taking
+ down every dashboard rather than one app. So it is a **test over the real meta list**
+ (`AppRegistry.test.ts`) plus a `console.error` at runtime — the mistake is caught before it ships
+ and named if it somehow does. Confirmed: all 44 keys are unique today, and the test says so.
+ Getting the real list into a test needed one thing beyond exporting it: `test-setup.ts` was not
+ providing `localStorage`, and `MusicPlayer/useLyricsOpen.ts` reads it at *import* time, so the
+ whole app graph was unimportable from a test. That is now fixed, which unblocks testing anything
+ else that pulls in a panel app.
- [ ] **Seeding depends on undocumented mount ordering.** Three call sites call `useAppRegistry()` with
no argument, defaulting to `[]`. It works only because `` sits at `frontend.tsx:32`,
above `` at `:34`. Mount a `WorkspaceView` above that and every panel renders empty.