superlocalmemory 4.0.6 → 4.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +128 -4
  2. package/README.md +6 -11
  3. package/package.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json +1 -1
  5. package/plugin/CLAUDE.md +3 -3
  6. package/plugin/agents/slm-governance-advisor.md +1 -1
  7. package/plugin/agents/slm-loop-runner.md +1 -1
  8. package/plugin/agents/slm-memory-advisor.md +1 -1
  9. package/plugin/agents/slm-optimize-advisor.md +1 -1
  10. package/plugin/requirements.txt +1 -1
  11. package/plugin/skills/slm-cache/SKILL.md +1 -1
  12. package/plugin/skills/slm-compress/SKILL.md +1 -1
  13. package/plugin/skills/slm-governance/SKILL.md +1 -1
  14. package/plugin/skills/slm-graph/SKILL.md +1 -1
  15. package/plugin/skills/slm-loop/SKILL.md +1 -1
  16. package/plugin/skills/slm-mesh/SKILL.md +1 -1
  17. package/plugin/skills/slm-profile/SKILL.md +1 -1
  18. package/plugin/skills/slm-recall/SKILL.md +1 -1
  19. package/plugin/skills/slm-remember/SKILL.md +1 -1
  20. package/plugin/skills/slm-scope/SKILL.md +1 -1
  21. package/plugin/skills/slm-session/SKILL.md +1 -1
  22. package/plugin/skills/slm-status/SKILL.md +1 -1
  23. package/plugin-src/rules/AGENTS.md +1 -1
  24. package/pyproject.toml +1 -1
  25. package/src/superlocalmemory/__init__.py +1 -1
  26. package/src/superlocalmemory/cli/commands.py +14 -0
  27. package/src/superlocalmemory/cli/main.py +9 -0
  28. package/src/superlocalmemory/cli/summary_cmd.py +215 -0
  29. package/src/superlocalmemory/code_graph/bridge/entity_resolver.py +26 -0
  30. package/src/superlocalmemory/code_graph/bridge/event_listeners.py +14 -3
  31. package/src/superlocalmemory/code_graph/bridge/maintenance.py +212 -0
  32. package/src/superlocalmemory/code_graph/config.py +65 -1
  33. package/src/superlocalmemory/core/consolidation_engine.py +14 -15
  34. package/src/superlocalmemory/core/fact_consolidator.py +24 -1
  35. package/src/superlocalmemory/core/maintenance.py +51 -1
  36. package/src/superlocalmemory/core/recall_worker.py +4 -0
  37. package/src/superlocalmemory/evolution/skill_evolver.py +16 -1
  38. package/src/superlocalmemory/hooks/hook_handlers.py +38 -11
  39. package/src/superlocalmemory/learning/pattern_miner.py +12 -7
  40. package/src/superlocalmemory/mcp/profiles.py +10 -3
  41. package/src/superlocalmemory/mcp/server.py +7 -0
  42. package/src/superlocalmemory/mcp/tools_code_graph.py +47 -3
  43. package/src/superlocalmemory/mcp/tools_summaries.py +147 -0
  44. package/src/superlocalmemory/server/consolidation_runner.py +140 -0
  45. package/src/superlocalmemory/server/recall_serializer.py +34 -2
  46. package/src/superlocalmemory/server/routes/agents.py +52 -8
  47. package/src/superlocalmemory/server/routes/brain.py +108 -0
  48. package/src/superlocalmemory/server/routes/memories.py +214 -0
  49. package/src/superlocalmemory/server/routes/v3_api.py +24 -46
  50. package/src/superlocalmemory/server/unified_daemon.py +107 -0
  51. package/src/superlocalmemory/storage/schema_code_graph.py +44 -1
  52. package/src/superlocalmemory/summaries/base.py +159 -0
  53. package/src/superlocalmemory/summaries/daily_reflection.py +55 -8
  54. package/src/superlocalmemory/summaries/project_work_log.py +23 -7
  55. package/src/superlocalmemory/summaries/session_summary.py +9 -5
  56. package/src/superlocalmemory/ui/index.html +10 -4
  57. package/src/superlocalmemory/ui/js/fact-detail.js +61 -0
  58. package/src/superlocalmemory/ui/js/od-boundedloops.js +324 -0
  59. package/src/superlocalmemory/ui/js/od-memories.js +337 -12
  60. package/src/superlocalmemory/ui/js/od-mesh.js +97 -5
  61. package/src/superlocalmemory/ui/js/od-operations.js +1 -150
  62. package/src/superlocalmemory/ui/js/od-optimize.js +36 -9
  63. package/src/superlocalmemory/ui/js/od-shell.js +10 -0
@@ -72,6 +72,67 @@ document.addEventListener('click', function(e) {
72
72
  body.appendChild(ent);
73
73
  }
74
74
 
75
+ // Code this memory refers to (4.0.7). Absent for anyone without a
76
+ // built code graph — the API returns [] rather than an error, so no
77
+ // section appears and nothing looks broken.
78
+ //
79
+ // textContent throughout, never innerHTML: every string here is a
80
+ // qualified name or file path read out of the indexed repository, so
81
+ // it is untrusted input as far as this panel is concerned.
82
+ if (data.code_links && data.code_links.length > 0) {
83
+ var codeWrap = document.createElement('div');
84
+ codeWrap.className = 'mt-2';
85
+
86
+ var codeLabel = document.createElement('strong');
87
+ codeLabel.textContent = 'Code referenced:';
88
+ codeWrap.appendChild(codeLabel);
89
+
90
+ var list = document.createElement('ul');
91
+ list.className = 'mb-0 ps-3';
92
+ list.style.listStyle = 'none';
93
+
94
+ data.code_links.forEach(function (link) {
95
+ var li = document.createElement('li');
96
+ li.className = 'mt-1';
97
+
98
+ var nameEl = document.createElement('code');
99
+ nameEl.textContent = link.qualified_name || link.name || '?';
100
+ li.appendChild(nameEl);
101
+
102
+ if (link.kind) {
103
+ var kind = document.createElement('span');
104
+ kind.className = 'text-muted';
105
+ kind.style.fontSize = '0.75rem';
106
+ kind.textContent = ' ' + link.kind;
107
+ li.appendChild(kind);
108
+ }
109
+
110
+ // A stale link points at code that has moved or gone. Saying
111
+ // so is the whole value of tracking staleness — silently
112
+ // showing it as current would be worse than not showing it.
113
+ if (link.is_stale) {
114
+ var stale = document.createElement('span');
115
+ stale.className = 'badge bg-warning text-dark ms-1';
116
+ stale.style.fontSize = '0.65rem';
117
+ stale.textContent = 'code changed';
118
+ li.appendChild(stale);
119
+ }
120
+
121
+ if (link.file_path) {
122
+ var loc = document.createElement('div');
123
+ loc.className = 'text-muted';
124
+ loc.style.fontSize = '0.7rem';
125
+ loc.textContent = link.file_path;
126
+ li.appendChild(loc);
127
+ }
128
+
129
+ list.appendChild(li);
130
+ });
131
+
132
+ codeWrap.appendChild(list);
133
+ body.appendChild(codeWrap);
134
+ }
135
+
75
136
  panel.appendChild(body);
76
137
  item.appendChild(panel);
77
138
  })
@@ -0,0 +1,324 @@
1
+ // Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ // Licensed under AGPL-3.0-or-later - see LICENSE file
3
+ // Part of SuperLocalMemory | https://qualixar.com
4
+ //
5
+ // Bounded Loops pane.
6
+ //
7
+ // Moved out of Governance in 4.0.8. Every other Governance tab governs SLM's
8
+ // OWN data — lifecycle, access, trust, compliance, ingestion. Bounded Loops
9
+ // governs none of it: it is a SEPARATE product that SLM optionally observes
10
+ // over the published contract bounded-loops.dev/slm-bridge/v1. Neither product
11
+ // depends on the other and installing either alone is complete, which makes
12
+ // this an integration, not a governance function.
13
+ //
14
+ // Reads GET /api/v3/bounded-loops/evidence for live bridge status and observed
15
+ // terminal runs. The guarantees are rendered FROM THE DATA rather than asserted
16
+ // in prose: every document carries eligible_for_learning=false, so the pane can
17
+ // show that SLM is not permitted to learn from these runs instead of merely
18
+ // promising that it does not.
19
+
20
+ (function () {
21
+ 'use strict';
22
+
23
+ function esc(s) {
24
+ return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
25
+ return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
26
+ });
27
+ }
28
+
29
+ function scaffold() {
30
+ return (
31
+
32
+ // ══════ BOUNDED LOOPS TAB ════════════════════════════════════════
33
+ // Static info card — no backend calls.
34
+ // A bounded loop advances only when an INDEPENDENT gate passes, not
35
+ // when the agent claims success. Laps are persisted as SLM memory
36
+ // (tag loop:<name>) so the full run history is queryable.
37
+ '<div>' +
38
+
39
+ // Live bridge status first: whether Bounded Loops is installed, the
40
+ // negotiated contract, and which runs were observed. The explanation
41
+ // below it is the same for everyone; this part is about YOUR install.
42
+ '<div class="card" style="margin-bottom:16px">' +
43
+ '<div class="card-head"><h3>Bridge status</h3>' +
44
+ '<span class="sub">observation only \u2014 never learns from these runs</span>' +
45
+ '</div>' +
46
+ '<div class="card-pad" id="od-bl-evidence">' +
47
+ '<div style="font-size:13px;color:var(--fg-2)">Checking bridge\u2026</div>' +
48
+ '</div>' +
49
+ '</div>' +
50
+
51
+ '<div class="page-head" style="margin-bottom:16px">' +
52
+ '<h2 style="font-size:20px;margin-bottom:6px">Bounded Loops</h2>' +
53
+ '<p style="font-size:13.5px">An iteration control pattern for agentic ' +
54
+ 'frameworks &mdash; the loop advances only when an <strong>independent ' +
55
+ 'gate</strong> passes, not when the agent claims success. Prevents ' +
56
+ 'rationalisation: the model cannot self-certify completion.</p>' +
57
+ '</div>' +
58
+
59
+ // Two-column: concept + CLI reference
60
+ '<div class="grid" style="grid-template-columns:1fr 1fr;align-items:start;margin-bottom:16px">' +
61
+
62
+ '<div class="card">' +
63
+ '<div class="card-head"><h3>How it works</h3></div>' +
64
+ '<div class="card-pad">' +
65
+ '<p style="font-size:13px;line-height:1.6;margin-bottom:12px">' +
66
+ 'Standard agentic loops let the model declare itself done &mdash; ' +
67
+ 'a known failure mode when the model rationalises instead of verifying. ' +
68
+ 'A bounded loop separates <em>execution</em> (the agent) from ' +
69
+ '<em>verification</em> (an independent gate such as a test suite, ' +
70
+ 'linter, or judge LLM).' +
71
+ '</p>' +
72
+ '<p style="font-size:13px;line-height:1.6;margin-bottom:12px">' +
73
+ 'The loop terminates only when the gate returns <code>DONE</code>, ' +
74
+ 'or when a hard lap cap is reached (<code>HALT</code>). Each lap is ' +
75
+ 'persisted as a queryable SLM memory tagged ' +
76
+ '<code>loop:&lt;name&gt;</code>.' +
77
+ '</p>' +
78
+ '<div style="background:var(--card-2);border-radius:var(--r-md);' +
79
+ 'padding:10px 14px;font-size:12.5px;line-height:1.7">' +
80
+ '<div><span class="badge ok" style="margin-right:8px">DONE</span>' +
81
+ 'Gate passed &mdash; loop succeeded cleanly</div>' +
82
+ '<div style="margin-top:6px"><span class="badge warn" style="margin-right:8px">HALT</span>' +
83
+ 'Lap cap reached &mdash; hard stop applied</div>' +
84
+ '<div style="margin-top:6px"><span class="badge cyan" style="margin-right:8px">PAUSE</span>' +
85
+ 'Awaiting external input or approval</div>' +
86
+ '<div style="margin-top:6px"><span class="badge danger" style="margin-right:8px">KILLED</span>' +
87
+ 'Manually stopped by the operator</div>' +
88
+ '<div style="margin-top:6px"><span class="badge neutral" style="margin-right:8px">ERROR</span>' +
89
+ 'Unrecoverable failure during a lap</div>' +
90
+ '</div>' +
91
+ '</div>' +
92
+ '</div>' +
93
+
94
+ '<div class="card">' +
95
+ '<div class="card-head"><h3>Run it: CLI &middot; command &middot; MCP</h3></div>' +
96
+ '<div class="card-pad">' +
97
+ '<p style="font-size:13px;margin-bottom:14px">' +
98
+ 'Bounded loops ship on three surfaces &mdash; the same engine and ' +
99
+ 'the same queryable ledger behind each.' +
100
+ '</p>' +
101
+
102
+ '<div style="font-size:12px;font-weight:600;color:var(--fg-2);margin-bottom:6px">CLI</div>' +
103
+ '<div style="display:flex;flex-direction:column;gap:8px;margin-bottom:14px">' +
104
+ '<div class="list-row">' +
105
+ '<span class="mono" style="min-width:190px;font-size:13px">slm loop demo</span>' +
106
+ '<span style="font-size:12.5px;color:var(--fg-2)">Run a live demo bounded loop</span>' +
107
+ '</div>' +
108
+ '<div class="list-row">' +
109
+ '<span class="mono" style="min-width:190px;font-size:13px">slm loop history</span>' +
110
+ '<span style="font-size:12.5px;color:var(--fg-2)">List loop runs for this profile</span>' +
111
+ '</div>' +
112
+ '<div class="list-row">' +
113
+ '<span class="mono" style="min-width:190px;font-size:13px">slm loop show &lt;run_id&gt;</span>' +
114
+ '<span style="font-size:12.5px;color:var(--fg-2)">Inspect a run lap-by-lap</span>' +
115
+ '</div>' +
116
+ '</div>' +
117
+
118
+ '<div style="font-size:12px;font-weight:600;color:var(--fg-2);margin-bottom:6px">Command (Claude Code / Codex)</div>' +
119
+ '<div style="display:flex;flex-direction:column;gap:8px;margin-bottom:14px">' +
120
+ '<div class="list-row">' +
121
+ '<span class="mono" style="min-width:190px;font-size:13px">/slm-loop</span>' +
122
+ '<span style="font-size:12.5px;color:var(--fg-2)">Slash command bound to the slm-loop skill + runner agent</span>' +
123
+ '</div>' +
124
+ '</div>' +
125
+
126
+ '<div style="font-size:12px;font-weight:600;color:var(--fg-2);margin-bottom:6px">MCP tools (code / full / power profiles)</div>' +
127
+ '<div style="display:flex;flex-direction:column;gap:8px">' +
128
+ '<div class="list-row">' +
129
+ '<span class="mono" style="min-width:190px;font-size:13px">slm_loop_run</span>' +
130
+ '<span style="font-size:12.5px;color:var(--fg-2)">Run a gated loop &mdash; the gate is an independent SLM recall</span>' +
131
+ '</div>' +
132
+ '<div class="list-row">' +
133
+ '<span class="mono" style="min-width:190px;font-size:13px">slm_loop_history</span>' +
134
+ '<span style="font-size:12.5px;color:var(--fg-2)">List runs (read-only)</span>' +
135
+ '</div>' +
136
+ '<div class="list-row">' +
137
+ '<span class="mono" style="min-width:190px;font-size:13px">slm_loop_show</span>' +
138
+ '<span style="font-size:12.5px;color:var(--fg-2)">Show a run lap-by-lap (read-only)</span>' +
139
+ '</div>' +
140
+ '</div>' +
141
+ '<div style="margin-top:18px;padding:10px 14px;background:var(--card-2);' +
142
+ 'border-radius:var(--r-md);font-size:12.5px;line-height:1.6">' +
143
+ '<b>Memory tagging:</b> each lap is stored with tag ' +
144
+ '<code>loop:&lt;name&gt;</code>. Recall the full history with ' +
145
+ '<br><code>slm recall --tag loop:my-loop-name</code>' +
146
+ '</div>' +
147
+ '</div>' +
148
+ '</div>' +
149
+
150
+ '</div>' + // end two-column grid
151
+
152
+ // Framework adapters note
153
+ '<div class="card">' +
154
+ '<div class="card-head"><h3>Framework adapters</h3></div>' +
155
+ '<div class="card-pad">' +
156
+ '<p style="font-size:13px;line-height:1.6;margin-bottom:14px">' +
157
+ 'Bounded loops integrate with any agentic framework that supports ' +
158
+ 'tool-call round-trips. The gate is an ordinary SLM memory check &mdash; ' +
159
+ 'no special framework wiring required. Each framework stamps ' +
160
+ '<code>SLM_AGENT_ID</code> so lap history is attribution-aware.' +
161
+ '</p>' +
162
+ '<div style="display:flex;flex-wrap:wrap;gap:8px">' +
163
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
164
+ 'background:var(--card-2);color:var(--fg-2)">CrewAI</span>' +
165
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
166
+ 'background:var(--card-2);color:var(--fg-2)">LangChain</span>' +
167
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
168
+ 'background:var(--card-2);color:var(--fg-2)">LangGraph</span>' +
169
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
170
+ 'background:var(--card-2);color:var(--fg-2)">Semantic Kernel</span>' +
171
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
172
+ 'background:var(--card-2);color:var(--fg-2)">LlamaIndex</span>' +
173
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
174
+ 'background:var(--card-2);color:var(--fg-2)">Microsoft Agent Framework</span>' +
175
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
176
+ 'background:var(--card-2);color:var(--fg-2)">AutoGen</span>' +
177
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
178
+ 'background:var(--card-2);color:var(--fg-2)">Google ADK</span>' +
179
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
180
+ 'background:var(--card-2);color:var(--fg-2)">OpenAI Agents</span>' +
181
+ '<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
182
+ 'background:var(--card-2);color:var(--fg-2)">Any MCP-compatible agent</span>' +
183
+ '</div>' +
184
+ '<p style="margin-top:12px;font-size:12.5px;color:var(--fg-2)">' +
185
+ 'Learn the full pattern: run <code>/slm-loop</code> (the slm-loop skill) ' +
186
+ 'inside Claude Code for an interactive walkthrough with live examples.' +
187
+ '</p>' +
188
+ '</div>' +
189
+ '</div>' +
190
+
191
+ '</div>'
192
+ );
193
+ }
194
+
195
+ /* Bounded Loops bridge status + observed runs.
196
+ *
197
+ * Until 4.0.8 this tab was a static essay about loop gating. It said nothing
198
+ * about the bridge, which is the part that concerns SLM: Bounded Loops is a
199
+ * SEPARATE product, and what SLM does with it is import read-only evidence
200
+ * over the published contract bounded-loops.dev/slm-bridge/v1.
201
+ *
202
+ * The guarantees are the point, and they must be stated from the DATA rather
203
+ * than asserted in prose — `eligible_for_learning` is a hard field in every
204
+ * document, always false in v1, so the pane can show that SLM is not allowed
205
+ * to learn from these runs rather than merely promising it doesn't.
206
+ */
207
+ var _blLoaded = false;
208
+ function loadEvidence() {
209
+ var box = document.getElementById('od-bl-evidence');
210
+ if (!box || _blLoaded) return;
211
+ _blLoaded = true;
212
+
213
+ fetch('/internal/token', { credentials: 'same-origin' })
214
+ .then(function (r) { return r.ok ? r.json() : null; })
215
+ .catch(function () { return null; })
216
+ .then(function (t) {
217
+ var tok = t && (t.token || t.install_token);
218
+ return fetch('/api/v3/bounded-loops/evidence', {
219
+ credentials: 'same-origin',
220
+ headers: tok ? { 'X-Install-Token': tok } : {},
221
+ });
222
+ })
223
+ .then(function (r) { return r.ok ? r.json() : null; })
224
+ .then(function (d) {
225
+ if (!d) { box.innerHTML = _blNote('Bridge status unavailable.'); return; }
226
+ box.innerHTML = renderBoundedLoopsEvidence(d);
227
+ })
228
+ .catch(function () {
229
+ _blLoaded = false; // allow a retry on the next tab visit
230
+ box.innerHTML = _blNote('Could not read bridge status.');
231
+ });
232
+ }
233
+
234
+ function _blNote(msg) {
235
+ return '<div style="font-size:13px;color:var(--fg-2)">' + esc(msg) + '</div>';
236
+ }
237
+
238
+ function renderBoundedLoopsEvidence(d) {
239
+ var installed = d.installed;
240
+ var runs = d.runs || [];
241
+ var badge = installed === true
242
+ ? '<span class="badge ok">installed</span>'
243
+ : (installed === false
244
+ ? '<span class="badge neutral">not installed</span>'
245
+ : '<span class="badge warn">unknown</span>');
246
+
247
+ var head =
248
+ '<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px">' +
249
+ badge +
250
+ '<span class="mono" style="font-size:12px;color:var(--fg-2)">' +
251
+ esc(d.contract || '') + '</span>' +
252
+ '</div>';
253
+
254
+ // Optional by design — "not installed" is a normal, complete state.
255
+ if (installed === false) {
256
+ return head + '<p style="font-size:13px;line-height:1.6;color:var(--fg-2)">' +
257
+ 'Bounded Loops is a separate product and SuperLocalMemory does not ' +
258
+ 'require it. Install it and SLM can import read-only evidence about ' +
259
+ 'finished runs — it never sends anything back.</p>';
260
+ }
261
+
262
+ var guarantees =
263
+ '<div style="background:var(--card-2);border-radius:var(--r-md);' +
264
+ 'padding:12px 14px;font-size:12.5px;line-height:1.75;margin-bottom:12px">' +
265
+ '<div><strong>Observation only.</strong> Evidence records what a run did. ' +
266
+ 'It never authorises SLM to learn from it, re-rank memories, or change ' +
267
+ 'routing — every document carries <code>eligible_for_learning: false</code>.</div>' +
268
+ '<div style="margin-top:6px"><strong>No paths leave the workspace.</strong> ' +
269
+ 'Locations travel as digests, so a project directory name never reaches ' +
270
+ 'the memory store. Gate text and artifact contents are excluded at source.</div>' +
271
+ '<div style="margin-top:6px"><strong>Tamper-evident, not verified.</strong> ' +
272
+ 'Receipts form a local append-only hash chain (<code>local_hash_chain_only</code>). ' +
273
+ 'That makes edits detectable — it is not authentication or independent audit.</div>' +
274
+ '</div>';
275
+
276
+ if (!runs.length) {
277
+ return head + guarantees +
278
+ '<p style="font-size:13px;color:var(--fg-2)">' +
279
+ 'No runs observed yet. Import one with the <code>observe_bounded_loop_evidence</code> ' +
280
+ 'tool — nothing is imported automatically.</p>';
281
+ }
282
+
283
+ var demoNote = d.demonstration_count
284
+ ? '<div style="font-size:12px;color:var(--fg-3);margin-bottom:8px">' +
285
+ esc(String(d.demonstration_count)) + ' of ' + esc(String(d.total)) +
286
+ ' observed run' + (d.total === 1 ? '' : 's') + ' ' +
287
+ (d.demonstration_count === 1 ? 'is a demonstration' : 'are demonstrations') +
288
+ ' — wiring proofs, not real work.</div>'
289
+ : '';
290
+
291
+ var rows = runs.map(function (r) {
292
+ var cls = r.outcome === 'SUCCEEDED' ? 'ok'
293
+ : (r.outcome === 'CANCELLED' ? 'neutral' : 'danger');
294
+ // run_state, not just outcome: HALTED (budget/policy stop) and FAILED
295
+ // (gate rejected the work) are different events and both map to FAILED.
296
+ var state = r.run_state && r.run_state !== r.outcome
297
+ ? ' <span style="color:var(--fg-3)">(' + esc(r.run_state) + ')</span>' : '';
298
+ // min-width:0 + ellipsis: a run ref is a single unbroken token, and in a
299
+ // narrow pane `flex:1` alone shreds it one character per line
300
+ // ("demo / — / proo / f"). Truncate with the full value in the tooltip.
301
+ return '<div class="list-row" style="align-items:baseline;gap:8px">' +
302
+ '<span class="mono" title="' + esc(r.run_ref || r.run_id) + '" ' +
303
+ 'style="flex:1;min-width:0;font-size:12.5px;overflow:hidden;' +
304
+ 'text-overflow:ellipsis;white-space:nowrap">' +
305
+ esc(r.run_ref || r.run_id) + '</span>' +
306
+ (r.demonstration
307
+ ? '<span class="badge neutral" style="margin-right:8px">demo</span>' : '') +
308
+ '<span class="badge ' + cls + '" style="margin-right:8px">' + esc(r.outcome) + '</span>' +
309
+ '<span style="font-size:12px;color:var(--fg-2)">' + state + '</span>' +
310
+ '<span style="font-size:12px;color:var(--fg-3);margin-left:10px">seq ' +
311
+ esc(String(r.receipt_sequence)) + '</span>' +
312
+ '</div>';
313
+ }).join('');
314
+
315
+ return head + guarantees + demoNote + rows;
316
+ }
317
+
318
+ window.odRenderLoops = function (container) {
319
+ if (!container) return;
320
+ container.innerHTML = scaffold();
321
+ _blLoaded = false;
322
+ loadEvidence();
323
+ };
324
+ })();