Two fixes to fox feedback ("rarely like stuff collapsed · why does it
need to be a different screen than the other node types?"):
1. <details open> on answer + metadata panels in sessions_tree.jinja2.
Expanded by default — fox rarely wants stuff collapsed; clicks to
collapse cost nothing and discoverability of the field is what
matters on first read.
2. /proof/{hash} is now THE dispatcher — drop ANY hash here and the
page resolves the right view:
- corpus document_root / leaf / merkle interior → proof.jinja2
(existing 3D Merkle lattice + inclusion proof + leaf-list)
- providence cache_key with session references → cache_key.jinja2
(every session node that hit this cache entry)
- session node_hash / subtree_hash → sessions_tree.jinja2
(the session's conversation tree)
- session_root (= seq=0 node's subtree_hash) → sessions_tree.jinja2
Aligns with ticket #000069 §13 discipline: "never mint new
kind-named URLs." /sessions/{sid} and /cache/{cache_key} stay as
working aliases — but the resolver, the metadata-panel links, and
the hash-drop bar all route through /proof/{hash} now.
Sessions are still tree-shaped (parent/child) where corpus roots are
flat leaf lists, so the IN-PAGE view differs. That's a topology fact,
not a URL choice — sessions and documents live in the same hash
universe under one route. A future six.js tree widget could share
the proof.jinja2 3D stage; not in this commit.
Resolver:
- /api/resolve/{hash} → "url": "/proof/{hash}" for cache_keys and
session node hashes (was /cache/<key> and /sessions/<sid>).
Templates:
- sessions_tree node-card cache_key + node_hash + subtree_hash are
now clickable → each opens /proof/<hash>.
- <details> on answer + hash panels default-open.
Smoke verified all four dispatch paths:
/proof/<cache_key> → cache_key.jinja2
/proof/<session_root> → sessions_tree.jinja2
/proof/<node_hash> → sessions_tree.jinja2
/proof/<unknown> → proof.jinja2 (corpus fallback)
84 lines
3.7 KiB
Django/Jinja
84 lines
3.7 KiB
Django/Jinja
{% extends "base.jinja2" %}
|
|
|
|
{% macro render_node(node, kids_of, branch_set, current_bates, this_sid, depth=0) -%}
|
|
<li class="cmd-tree-node {% if node.bates == current_bates %}cmd-tree-current{% endif %} {% if node.bates in branch_set %}cmd-tree-branch{% endif %}">
|
|
<article class="cmd-node-card">
|
|
<header class="cmd-node-card-head">
|
|
<code class="cmd-tree-bates">{{ node.bates }}</code>
|
|
<span class="cmd-tree-audit cmd-audit-{{ node.audit_mode|lower }}">{{ node.audit_mode }}</span>
|
|
{% if node.sid != this_sid %}<span class="cmd-tree-from">[from {{ node.sid }}]</span>{% endif %}
|
|
{% if node.label %}<span class="cmd-tree-label">{{ '{' + node.label + '}' }}</span>{% endif %}
|
|
{% if node.bates == current_bates %}<span class="cmd-current-pill">current</span>{% endif %}
|
|
{% if node.bates in branch_set %}<span class="cmd-branch-pill">branch</span>{% endif %}
|
|
</header>
|
|
|
|
<div class="cmd-node-q">{{ node.question }}</div>
|
|
|
|
{% if node.cited_titles %}
|
|
<div class="cmd-tree-cites">
|
|
{% for t in node.cited_titles %}<span class="cmd-cite">{{ t }}</span>{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if node.answer_text %}
|
|
<details class="cmd-node-answer" open>
|
|
<summary>answer ({{ node.answer_text|length }} chars)</summary>
|
|
<pre class="cmd-answer-pre">{{ node.answer_text }}</pre>
|
|
</details>
|
|
{% endif %}
|
|
|
|
<details class="cmd-node-meta" open>
|
|
<summary>hashes / metadata</summary>
|
|
<dl class="cmd-meta-dl">
|
|
<dt>cache_key</dt>
|
|
<dd>{% if node.cache_key %}<a href="/proof/{{ node.cache_key }}"><code class="cmd-hash">{{ node.cache_key }}</code></a>{% else %}<em>(none — canonical_projection)</em>{% endif %}</dd>
|
|
<dt>node_hash</dt>
|
|
<dd><a href="/proof/{{ node.node_hash }}"><code class="cmd-hash">{{ node.node_hash }}</code></a></dd>
|
|
<dt>subtree_hash</dt>
|
|
<dd><a href="/proof/{{ node.subtree_hash }}"><code class="cmd-hash">{{ node.subtree_hash }}</code></a></dd>
|
|
<dt>created</dt>
|
|
<dd>{{ node.created_at }}</dd>
|
|
{% if node.parent_bates %}
|
|
<dt>parent</dt>
|
|
<dd><code>{{ node.parent_bates }}</code></dd>
|
|
{% endif %}
|
|
</dl>
|
|
</details>
|
|
</article>
|
|
|
|
{% set kids = kids_of.get(node.bates, []) %}
|
|
{% if kids %}
|
|
<ul class="cmd-tree-children">
|
|
{% for c in kids %}
|
|
{{ render_node(c, kids_of, branch_set, current_bates, this_sid, depth + 1) }}
|
|
{% endfor %}
|
|
</ul>
|
|
{% endif %}
|
|
</li>
|
|
{%- endmacro %}
|
|
|
|
{% block body %}
|
|
<section class="cmd-panel cmd-panel-wide">
|
|
<header class="cmd-panel-head">
|
|
<h1>session <code>{{ session.sid }}</code>{% if session.label %} <span class="cmd-tree-label">{{ '{' + session.label + '}' }}</span>{% endif %}</h1>
|
|
<p class="cmd-panel-sub">
|
|
{{ session.n_nodes }} node(s) · {{ branches|length }} branch point(s) ·
|
|
current → <a href="#node-{{ session.current_bates }}"><code>{{ session.current_bates }}</code></a>
|
|
</p>
|
|
<p class="cmd-hash-block">root <code class="cmd-hash">{{ session.session_root }}</code></p>
|
|
<p><a href="/sessions">← all sessions</a> · <a href="/api/sessions/{{ session.sid }}">JSON</a></p>
|
|
</header>
|
|
|
|
{% set kids_of = children %}
|
|
{% set root_node = nodes[0] if nodes and nodes[0].seq == 0 else None %}
|
|
{% if root_node and kids_of.get(root_node.bates) %}
|
|
<ul class="cmd-tree">
|
|
{% for child in kids_of.get(root_node.bates, []) %}
|
|
{{ render_node(child, kids_of, branches, session.current_bates, session.sid) }}
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
<p class="cmd-empty">empty session — no turns yet. <code>make session SID={{ session.sid }}</code> to continue.</p>
|
|
{% endif %}
|
|
</section>
|
|
{% endblock %}
|