Link learning trajectories to their conversation

The trajectory detail panel showed the conversation id as plain text, so
reviewing an example meant copying the room id and hunting for it in the
inbox — with the evidence panel open on one screen and the conversation on
another.

Render it as a link instead. The id has to travel as an owned id
(accountId~conversationId): once account runtimes exist, findConversation
rejects a bare room id outright, so a plain link would 404 for every
multi-account install.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Jamie BruntonandClaude Opus 5 committed 2026-08-12 08:58:04 +01:00
1 parent 142378df33
commit 3f60f7f665
3 files changed
+33 -2

No files matched your search

@@ -1,5 +1,7 @@
import { useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { Link } from 'react-router-dom';
import { createOwnedConversationId } from '../conversations/conversation-identity.js';
import { learningApi } from './learning-api.js';
import './learning.css';
@@ -13,7 +15,19 @@ function TrajectoryDetail({ id }: { id: string }) {
return (
<div className="trajectory-detail">
<dl className="trajectory-facts">
<div><dt>Conversation</dt><dd>{trajectory.conversationId}</dd></div>
<div>
<dt>Conversation</dt>
<dd>
{/* The conversations route resolves owned ids only, so the account
that produced the trajectory has to travel with the room id. */}
<Link
className="trajectory-conversation-link"
to={`/conversations/${encodeURIComponent(createOwnedConversationId(trajectory.accountId, trajectory.conversationId))}`}
>
{trajectory.conversationId}
</Link>
</dd>
</div>
<div><dt>Policy mode</dt><dd>{trajectory.policyMode ?? 'Unknown'}</dd></div>
<div><dt>Outcome</dt><dd>{trajectory.outcomeType.replaceAll('-', ' ')}</dd></div>
<div><dt>Matures</dt><dd>{new Date(trajectory.maturityAt).toLocaleString()}</dd></div>
@@ -14,6 +14,7 @@
.trajectory-facts { display: flex; flex-wrap: wrap; gap: .35rem 1.4rem; margin: 0; }
.trajectory-facts dt { color: var(--muted); font-size: .72rem; text-transform: uppercase; letter-spacing: .04em; }
.trajectory-facts dd { margin: 0; font-size: .85rem; word-break: break-all; }
.trajectory-conversation-link { color: var(--accent); text-decoration: underline; text-underline-offset: .15em; }
.decision-list { display: grid; gap: .75rem; list-style: none; margin: 0; padding: 0; }
.decision-card { border: 1px solid var(--border); border-radius: .6rem; display: grid; gap: .5rem; padding: .7rem .8rem; }
.decision-heading { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; }