address #3787 Codex round 3: coverage keys, RA attach limitation

Coverage rows now resolve the de-collided module-qualified key introduced
for same-named statics. RA boards: document that --attach requires a
debugger-booted target - the C_DEBUGEN gate exists because an unguarded
TRCKCR write bricks standalone boots (hardware-proven), so the limitation
is documented rather than the guard weakened; a debugger-side TRCKCR hook
can lift it later once re-verified on hardware. The stm32n6 board-gating
suggestion is not taken: N6 trace pins are AF0-fixed chip-level, the same
family-wide pattern as stm32h7.
diff --git a/.claude/skills/etm-trace/boards.md b/.claude/skills/etm-trace/boards.md
index ccebefb..4a5f297 100644
--- a/.claude/skills/etm-trace/boards.md
+++ b/.claude/skills/etm-trace/boards.md
@@ -91,6 +91,11 @@
   caused intermittent "Failed to initialize DAP" — the reference runs 4 MHz.
   ISR entry: `--isr tusb_int_handler,dcd_int_handler` (FSP's
   usbfs_interrupt_handler symbol never actually executes).
+- **ra6m5_ek / ra8m1_ek — `--attach` needs a debugger-booted target**: the
+  firmware TRCKCR setup is gated on DHCSR.C_DEBUGEN (an unguarded write
+  wedges a standalone boot un-attachable until power-cycle), so a board
+  booted WITHOUT a debugger has no trace clock and an `--attach` capture
+  reads silence. Reflash/reset through the capture default flow first.
 - **ra8m1_ek**: **J9 must be closed** (holds the on-board J-Link OB in
   reset — open = SWD contention, intermittent "Failed to initialize DAP",
   even an apparent brick recoverable only by power-cycle/J16 boot mode).
diff --git a/.claude/skills/etm-trace/scripts/etm_profile.py b/.claude/skills/etm-trace/scripts/etm_profile.py
index b859328..8568bc6 100644
--- a/.claude/skills/etm-trace/scripts/etm_profile.py
+++ b/.claude/skills/etm-trace/scripts/etm_profile.py
@@ -73,8 +73,13 @@
         if name == "Total" and m_inst and m_src:
             totals["src_cov"] = num(m_src.group(1)), num(m_src.group(2))
             totals["inst_cov"] = num(m_inst.group(1)), num(m_inst.group(2))
-        elif name in funcs and m_inst:
-            funcs[name]["inst_pct"] = float(m_inst.group(3))
+        elif m_inst:
+            # match the de-collided key when a same-named static from another
+            # module was renamed during the profile pass
+            key = name if (name in funcs and funcs[name]["module"] == module) \
+                else f"{name} [{module}]"
+            if key in funcs:
+                funcs[key]["inst_pct"] = float(m_inst.group(3))
     return funcs, totals