···172172 assert "chat had trouble" in html
173173174174175175-def test_chat_day_marks_talent_summary_for_markdown_bootstrap(
175175+def test_chat_day_emits_raw_talent_markdown_source_for_bootstrap(
176176 journal_copy, monkeypatch
177177):
178178 day = "20990102"
···212212 '<div class="chat-talent-card-detail '
213213 'chat-talent-card-detail--markdown" data-markdown="1">**bad args**</div>'
214214 ) in html
215215+ assert "<strong>done</strong>" not in html
216216+ assert "<strong>bad args</strong>" not in html
215217216218217219def test_chat_event_anchor_ids_are_stable(journal_copy, monkeypatch):
+21-6
apps/chat/workspace.html
···74747575 insertTimeSeparators(transcript);
7676 decorateBubbles(transcript);
7777- bootstrapMarkdownDetails(transcript);
7777+ document.addEventListener('DOMContentLoaded', () => {
7878+ bootstrapMarkdownDetails(transcript);
7979+ }, { once: true });
78807981 if (isToday && window.appEvents) {
8082 chatEventsCleanup = window.appEvents.listen('chat', {
···170172 }
171173172174 function bootstrapMarkdownDetails(list) {
173173- list.querySelectorAll('[data-markdown="1"]').forEach((node) => {
174174- const text = node.textContent || '';
175175- node.innerHTML = window.AppServices.renderMarkdown(text);
176176- node.removeAttribute('data-markdown');
175175+ const nodes = Array.from(list.querySelectorAll('[data-markdown="1"]'));
176176+ nodes.forEach((node) => {
177177+ renderTalentMarkdownInto(node, node.textContent || '');
177178 });
179179+ const remaining = list.querySelectorAll('[data-markdown="1"]');
180180+ if (remaining.length) {
181181+ throw new Error('unrendered talent markdown details remain');
182182+ }
183183+ }
184184+185185+ function renderTalentMarkdownInto(node, source) {
186186+ // Single path from markdown source to rendered HTML for talent card details.
187187+ // SSR initial-transcript bootstrap and live-append flow both route here.
188188+ if (!window.AppServices || typeof window.AppServices.renderMarkdown !== 'function') {
189189+ throw new Error('talent markdown renderer is unavailable');
190190+ }
191191+ node.innerHTML = window.AppServices.renderMarkdown(source || '');
192192+ node.removeAttribute('data-markdown');
178193 }
179194180195 function appendEventFromLive(msg, list) {
···295310 if (status === 'finished' || status === 'errored') {
296311 const detailNode = document.createElement('div');
297312 detailNode.className = 'chat-talent-card-detail chat-talent-card-detail--markdown';
298298- detailNode.innerHTML = window.AppServices.renderMarkdown(detail || '');
313313+ renderTalentMarkdownInto(detailNode, detail || '');
299314 card.appendChild(detailNode);
300315 } else {
301316 const detailNode = document.createElement('span');