answer
+{{ first.answer_text }}
+ diff --git a/arborist_viz/routes.py b/arborist_viz/routes.py
index 10c1a32..4a99ee0 100644
--- a/arborist_viz/routes.py
+++ b/arborist_viz/routes.py
@@ -59,9 +59,14 @@ def includeme(config):
config.add_route("sessions_list", "/sessions")
config.add_route("sessions_find", "/sessions/find")
config.add_route("sessions_tree", "/sessions/{sid}")
+ config.add_route("cache_key_page", "/cache/{cache_key}")
config.add_route("api_sessions_list", "/api/sessions")
config.add_route("api_sessions_find", "/api/sessions/find")
config.add_route("api_sessions_tree", "/api/sessions/{sid}")
+ # Sessions-shard view of a providence cache_key (every session node
+ # referencing it). Distinct from /api/cache/ which targets the
+ # providence_cache row itself (ticket #000069 §13 §6.7).
+ config.add_route("api_session_cache_key", "/api/cache-key/{cache_key}")
# SSE — §3-corrected replaces WS routes with text/event-stream.
config.add_route("sse_audit_live", "/sse/audit/live")
diff --git a/arborist_viz/static/css/viz.css b/arborist_viz/static/css/viz.css
index 9a96e25..a83048a 100644
--- a/arborist_viz/static/css/viz.css
+++ b/arborist_viz/static/css/viz.css
@@ -673,3 +673,70 @@ code, pre { font-family: var(--mono); font-size: 12px; }
.cmd-cite { font-size: 0.75em; opacity: 0.7;
padding: 0.05em 0.4em; border-radius: 0.2em;
background: rgba(255,255,255,0.06); }
+
+/* ---- node cards + answer + metadata (richer tree views) -------------- */
+
+.cmd-panel-wide { max-width: none; padding: 1.5em 2em; }
+
+.cmd-hash-block { word-break: break-all; font-size: 0.85em; opacity: 0.85; }
+
+.cmd-node-card {
+ background: rgba(255,255,255,0.03);
+ border: 1px solid rgba(255,255,255,0.08);
+ border-radius: 0.3em;
+ padding: 0.8em 1em;
+ margin: 0.4em 0;
+}
+.cmd-node-card-head {
+ display: flex; gap: 0.6em; align-items: baseline; flex-wrap: wrap;
+ margin-bottom: 0.4em;
+}
+.cmd-node-q {
+ font-size: 1.05em; font-weight: 500;
+ margin: 0.3em 0 0.5em 0;
+}
+.cmd-current-pill, .cmd-branch-pill {
+ font-size: 0.7em; padding: 0.05em 0.5em;
+ border-radius: 0.2em; font-weight: 600;
+ text-transform: uppercase; letter-spacing: 0.05em;
+}
+.cmd-current-pill { background: rgba(120, 200, 255, 0.25); color: #8af; }
+.cmd-branch-pill { background: rgba(220, 180, 80, 0.25); color: #db8; }
+
+.cmd-node-answer summary,
+.cmd-node-meta summary {
+ cursor: pointer; opacity: 0.7; font-size: 0.85em;
+ padding: 0.3em 0; user-select: none;
+}
+.cmd-node-answer[open] summary,
+.cmd-node-meta[open] summary { opacity: 1; }
+.cmd-answer-pre {
+ background: rgba(0,0,0,0.25);
+ padding: 0.8em 1em;
+ border-left: 2px solid rgba(120, 200, 255, 0.4);
+ border-radius: 0.2em;
+ font-size: 0.85em;
+ line-height: 1.5;
+ white-space: pre-wrap;
+ word-break: break-word;
+ overflow-x: auto;
+ max-height: 600px;
+ overflow-y: auto;
+}
+
+.cmd-meta-dl {
+ display: grid;
+ grid-template-columns: max-content 1fr;
+ gap: 0.2em 1em;
+ font-size: 0.85em;
+ padding: 0.5em 0;
+}
+.cmd-meta-dl dt { opacity: 0.65; }
+.cmd-meta-dl dd { margin: 0; word-break: break-all; }
+.cmd-meta-dl code { font-size: 0.95em; }
+
+.cmd-tree-current > .cmd-node-card { border-color: rgba(120, 200, 255, 0.5); }
+.cmd-tree-branch > .cmd-node-card { border-color: rgba(220, 180, 80, 0.5); }
+
+/* Override the tree-line styles (old layout) — cards replace them. */
+.cmd-tree-current > .cmd-tree-line::after { content: none; }
diff --git a/arborist_viz/templates/cache_key.jinja2 b/arborist_viz/templates/cache_key.jinja2
new file mode 100644
index 0000000..a555117
--- /dev/null
+++ b/arborist_viz/templates/cache_key.jinja2
@@ -0,0 +1,52 @@
+{% extends "base.jinja2" %}
+{% block body %}
+
+ {{ hits|length }} session node(s) reference this providence cache entry —
+ a cache hit reused across {{ hits|map(attribute='sid')|unique|list|length }} session(s).
+ no session nodes reference this cache_keycache_key
+ {{ cache_key }}answer
+ {{ first.answer_text }}
+ Referenced by
+
+
+
+ {% else %}
+
+
+
+ {% for n in hits %}
+ bates session audit created
+
+ {% endfor %}
+
+
+ {{ n.bates }}
+ {{ n.sid }}{{ n.audit_mode }}
+ {{ n.created_at }}
+
{{ node.bates }}
- {{ node.audit_mode }}
- {% if node.sid != this_sid %}[from {{ node.sid }}]{% endif %}
- {% if node.label %}{{ '{' + node.label + '}' }}{% endif %}
- {{ (node.question or '')[:120] }}
- {{ node.bates }}
+ {{ node.audit_mode }}
+ {% if node.sid != this_sid %}[from {{ node.sid }}]{% endif %}
+ {% if node.label %}{{ '{' + node.label + '}' }}{% endif %}
+ {% if node.bates == current_bates %}current{% endif %}
+ {% if node.bates in branch_set %}branch{% endif %}
+ {{ node.answer_text }}
+ {{ session.sid }}{{ session.sid }}{% if session.label %} {{ '{' + session.label + '}' }}{% endif %}
{{ session.n_nodes }} node(s) · {{ branches|length }} branch point(s) ·
- root={{ session.session_root }} ·
- current → {{ session.current_bates }}
+ current → {{ session.current_bates }}
root {{ session.session_root }}
empty session
+empty session — no turns yet. make session SID={{ session.sid }} to continue.