Phase 15: snapshots, forks, export/import, cognitive replay, experiments
- snapshots: checksummed JSON archive of every mind-scoped state table plus identity/personality/config and the event position; stored, downloadable - fork: restore into a new mind with all ids remapped; event references are kept so history before the fork is shared by reference - export/import: same archive; import is hostile-input validated (size, allow-listed tables, row caps, column names, checksum, forced mind_id) - compare: deterministic counts, belief diffs by proposition, personality and config diffs, event mix - replay: Runtime.Step for synchronous drivers; sandbox fork driven one stimulus at a time under a forbidden capability context; recorded model responses via a replaying router; steps labelled exact / approximate / diverged with recorded-vs-replayed digests - experiments: definition (overrides, stimuli, expectations, seed, duration), background run in a disabled subject mind, stored and exportable results - migration 0015, API, UI (Snapshots and Replay tabs, Experiments section, compare page), noemactl snapshots/compare/replay/experiments, docs
This commit is contained in:
@@ -227,3 +227,6 @@ canvas.graph { width: 100%; height: 560px; display: block; border-radius: 8px; b
|
||||
|
||||
/* Integrations */
|
||||
.badge.is-ok { background: var(--ok-soft); color: var(--ok); } .badge.is-stale { background: var(--warn-soft); color: var(--warn); } .badge.is-error { background: var(--danger-soft); color: var(--danger); } .badge.is-new, .badge.is-disabled { background: var(--surface-2); color: var(--muted); }
|
||||
|
||||
/* Study: replay & experiments */
|
||||
.replay-step { padding: .6rem 0; border-top: 1px solid var(--border); } .replay-step:first-child { border-top: 0; } .replay-step ul { margin: .2rem 0 0 1rem; padding: 0; } .strong { font-weight: 600; }
|
||||
|
||||
@@ -53,6 +53,9 @@
|
||||
<a href="/integrations" {{if hasPrefix .Path "/integrations"}}aria-current="page"{{end}}>Integrations</a>
|
||||
<a href="/policies" {{if hasPrefix .Path "/policies"}}aria-current="page"{{end}}>Policies</a>
|
||||
</div>
|
||||
<div class="navgroup"><div class="navhead">Study</div>
|
||||
<a href="/experiments" {{if hasPrefix .Path "/experiments"}}aria-current="page"{{end}}>Experiments</a>
|
||||
</div>
|
||||
<div class="navgroup"><div class="navhead">System</div>
|
||||
<a href="/system" {{if eq .Path "/system"}}aria-current="page"{{end}}>System</a>
|
||||
<a href="/audit" {{if eq .Path "/audit"}}aria-current="page"{{end}}>Audit</a>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{{define "content"}}
|
||||
{{$a := .Data.Mind}}{{$b := .Data.Other}}{{$c := .Data.C}}
|
||||
<div class="pagehead"><h1>{{$a.DisplayName}} <span class="muted">vs</span> {{$b.DisplayName}}</h1><p class="muted">Deterministic comparison of two minds. {{range $c.Notes}}{{.}} {{end}}</p><p class="small"><a href="/minds/{{$a.Slug}}">{{$a.Slug}}</a> · <a href="/minds/{{$b.Slug}}">{{$b.Slug}}</a> · <a href="/minds/{{$b.Slug}}/compare?with={{$a.Slug}}">swap</a></p></div>
|
||||
<section class="grid cols-2">
|
||||
<div class="card"><h2>State</h2><table class="table compact"><thead><tr><th></th><th class="num">{{$a.Slug}}</th><th class="num">{{$b.Slug}}</th></tr></thead><tbody>{{range $c.Stats}}<tr><td>{{.Name}}</td><td class="num">{{.A}}</td><td class="num {{if ne .A .B}}strong{{end}}">{{.B}}</td></tr>{{end}}</tbody></table></div>
|
||||
<div class="card"><h2>Configuration & personality</h2>
|
||||
{{if $c.Config}}<table class="table compact"><thead><tr><th>Field</th><th>{{$a.Slug}}</th><th>{{$b.Slug}}</th></tr></thead><tbody>{{range $c.Config}}<tr><td class="mono small">{{.Field}}</td><td class="small">{{.A}}</td><td class="small">{{.B}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted small">Same configuration.</p>{{end}}
|
||||
{{if $c.Personality}}<table class="table compact"><thead><tr><th>Personality</th><th>{{$a.Slug}}</th><th>{{$b.Slug}}</th></tr></thead><tbody>{{range $c.Personality}}<tr><td class="mono small">{{.Path}}</td><td class="small">{{.Old}}</td><td class="small">{{.New}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted small">Same personality.</p>{{end}}</div>
|
||||
</section>
|
||||
<div class="card"><h2>Beliefs <span class="muted small">{{index $c.Summary "beliefs_same"}} same · {{index $c.Summary "beliefs_changed"}} changed · {{index $c.Summary "beliefs_only_a"}} only in {{$a.Slug}} · {{index $c.Summary "beliefs_only_b"}} only in {{$b.Slug}}</span></h2>
|
||||
{{if $c.Beliefs}}<table class="table compact"><thead><tr><th>Proposition</th><th class="num">{{$a.Slug}}</th><th class="num">{{$b.Slug}}</th><th class="num">Δ</th></tr></thead><tbody>{{range $c.Beliefs}}<tr><td>{{.Proposition}}</td><td class="num">{{if .InA}}{{conf .ConfA}} <span class="small muted">{{.StatusA}}</span>{{else}}<span class="muted">—</span>{{end}}</td><td class="num">{{if .InB}}{{conf .ConfB}} <span class="small muted">{{.StatusB}}</span>{{else}}<span class="muted">—</span>{{end}}</td><td class="num">{{if and .InA .InB}}{{printf "%+.2f" .Delta}}{{end}}</td></tr>{{end}}</tbody></table>{{else}}<p class="muted">Belief sets are identical.</p>{{end}}</div>
|
||||
{{if $c.Events}}<div class="card"><h2>Event mix</h2><table class="table compact"><thead><tr><th>Type</th><th class="num">{{$a.Slug}}</th><th class="num">{{$b.Slug}}</th></tr></thead><tbody>{{range $c.Events}}<tr><td class="mono small">{{.Name}}</td><td class="num">{{.A}}</td><td class="num">{{.B}}</td></tr>{{end}}</tbody></table></div>{{end}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,21 @@
|
||||
{{define "content"}}
|
||||
{{$e := .Data.E}}{{$r := .Data.Results}}
|
||||
<div class="pagehead mindhead"><div><p class="muted small"><a href="/experiments">Experiments</a></p><h1>{{$e.Name}} <span class="badge {{if eq $e.Status "done"}}ok{{else if eq $e.Status "failed"}}danger{{else if eq $e.Status "running"}}warn{{end}}">{{$e.Status}}</span></h1>
|
||||
<p class="muted">{{$e.Hypothesis}}</p><p class="small">Baseline: <a href="/minds/{{.Data.Base.Slug}}">{{.Data.Base.DisplayName}}</a> snapshot <strong>{{.Data.Snapshot.Name}}</strong> (event {{.Data.Snapshot.EventSeq}}){{with .Data.Subject}} · Subject: <a href="/minds/{{.Slug}}">{{.Slug}}</a> · <a href="/minds/{{$.Data.Base.Slug}}/compare?with={{.Slug}}">compare with baseline</a>{{end}}{{with $e.Error}} · <span class="danger">{{.}}</span>{{end}}</p></div>
|
||||
<div class="row">{{if .Principal.User.Role.AtLeast "operator"}}<form method="post" action="/experiments/{{$e.ID}}/run"><input type="hidden" name="_csrf" value="{{.CSRF}}"><button class="btn btn-primary" {{if eq $e.Status "running"}}disabled{{end}}>{{if eq $e.Status "draft"}}Run{{else}}Run again{{end}}</button></form>{{end}}
|
||||
{{if ne $e.Status "draft"}}<a class="btn" href="/api/v1/experiments/{{$e.ID}}/export">Export results</a>{{end}}
|
||||
{{if .Principal.User.Role.AtLeast "administrator"}}<form method="post" action="/experiments/{{$e.ID}}/delete"><input type="hidden" name="_csrf" value="{{.CSRF}}"><button class="btn btn-danger" data-confirm="Delete this experiment? The subject mind is archived.">Delete</button></form>{{end}}</div></div>
|
||||
{{if eq $e.Status "running"}}<p class="muted small" data-live-refresh="5000">Running… this page refreshes.</p>{{end}}
|
||||
<section class="grid cols-2">
|
||||
<div class="card"><h2>Definition</h2><pre class="json">{{.Data.DefinitionJSON}}</pre></div>
|
||||
<div class="card"><h2>Results</h2>
|
||||
{{if eq $e.Status "done" "failed"}}
|
||||
<table class="kv-table"><tr><th>Duration</th><td>{{$r.DurationMS}} ms · {{$r.Cycles}} cycles{{if $r.TruncatedAfter}} · <span class="danger">stopped after stimulus {{$r.TruncatedAfter}} (duration cap)</span>{{end}}</td></tr><tr><th>Memories / goals</th><td>{{$r.Memories}} / {{$r.Goals}}</td></tr><tr><th>Model calls</th><td>{{index $r.LLMCalls "live"}} live · {{index $r.LLMCalls "replayed"}} replayed</td></tr></table>
|
||||
{{if $r.Expectations}}<h3>Expectations</h3><table class="table compact"><tbody>{{range $r.Expectations}}<tr><td><span class="badge {{if .Met}}ok{{else}}danger{{end}}">{{if .Met}}met{{else}}not met{{end}}</span></td><td class="mono small">{{.EventType}}{{with .Contains}} ∋ "{{.}}"{{end}}{{if .Absent}} (absent){{end}}</td><td class="small muted">{{trunc .Matched 90}}</td></tr>{{end}}</tbody></table>{{end}}
|
||||
{{if $r.EventsByType}}<h3>Events produced</h3><p class="small mono">{{range $t, $n := $r.EventsByType}}{{$t}} {{$n}} · {{end}}</p>{{end}}
|
||||
{{if $r.Beliefs}}<h3>Beliefs of the subject</h3><table class="table compact"><tbody>{{range $r.Beliefs}}<tr><td>{{.Proposition}}</td><td class="num">{{conf .Confidence}}</td><td class="small muted">{{.Status}} · {{.Source}}</td></tr>{{end}}</tbody></table>{{end}}
|
||||
{{else}}<p class="muted">Not run yet.</p>{{end}}
|
||||
</div>
|
||||
</section>
|
||||
{{if $r.Steps}}<div class="card"><h2>Stimuli</h2>{{range $r.Steps}}<div class="replay-step"><div><strong>{{.Index}}.</strong> <a href="/events/{{.EventID}}">{{.Text}}</a> <span class="small muted">· {{.Cycles}} cycle(s)</span></div><ul class="small mono">{{range .Produced}}<li>{{.}}</li>{{else}}<li class="muted">nothing produced</li>{{end}}</ul></div>{{end}}</div>{{end}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,22 @@
|
||||
{{define "content"}}
|
||||
<div class="pagehead"><h1>Experiments</h1><p class="muted">A controlled run: a snapshot, configuration or personality overrides, a stimulus script, expectations, a seed and a duration. The subject is a disabled fork that cannot act; results are stored and exportable, and the subject stays for comparison.</p></div>
|
||||
<section class="grid cols-3-1">
|
||||
<div class="card">
|
||||
{{if .Data.Experiments}}<table class="table compact"><thead><tr><th>Name</th><th>Status</th><th>Snapshot</th><th class="num">Stimuli</th><th>Expectations</th><th>Created</th></tr></thead><tbody>
|
||||
{{range .Data.Experiments}}<tr><td><a href="/experiments/{{.ID}}"><strong>{{.Name}}</strong></a>{{with .Hypothesis}}<div class="small muted">{{trunc . 100}}</div>{{end}}</td><td><span class="badge {{if eq .Status "done"}}ok{{else if eq .Status "failed"}}danger{{else if eq .Status "running"}}warn{{end}}">{{.Status}}</span></td><td class="small mono">{{trunc (printf "%s" .SnapshotID) 13}}</td><td class="num">{{len .Definition.Stimuli}}</td><td class="small">{{len .Definition.Expected}}</td><td class="small muted nowrap">{{ago .CreatedAt}} · {{.CreatedBy}}</td></tr>{{end}}</tbody></table>
|
||||
{{else}}<p class="muted">No experiments yet.</p>{{end}}
|
||||
</div>
|
||||
{{if .Principal.User.Role.AtLeast "operator"}}
|
||||
<div class="card"><h2>New experiment</h2>
|
||||
{{if not .Data.Snapshots}}<p class="muted small">Take a snapshot of a mind first.</p>{{else}}
|
||||
<form method="post" action="/experiments" class="stack"><input type="hidden" name="_csrf" value="{{.CSRF}}">
|
||||
<label>Name <input name="name" required maxlength="80"></label>
|
||||
<label>Hypothesis <input name="hypothesis" maxlength="300" placeholder="what you expect to see and why"></label>
|
||||
<label>Baseline snapshot <select name="snapshot_id">{{range .Data.Snapshots}}<option value="{{.ID}}">{{index $.Data.MindNames .MindID}} · {{.Name}}</option>{{end}}</select></label>
|
||||
<label>Definition (JSON) <textarea name="definition" rows="16" class="mono" required>{{.Data.Sample}}</textarea></label>
|
||||
<p class="small muted">config: CognitiveConfig fields by JSON name · personality: sections by JSON name · expected: event_type + optional contains / absent.</p>
|
||||
<div><button class="btn btn-primary">Create</button></div></form>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</section>
|
||||
{{end}}
|
||||
@@ -0,0 +1,15 @@
|
||||
{{define "content"}}
|
||||
{{$r := .Data.Replay}}{{$m := .Data.Mind}}
|
||||
<div class="pagehead mindhead"><div><p class="muted small"><a href="/minds/{{$m.Slug}}/replays">Replays</a> › {{$m.DisplayName}}</p><h1>Replay <span class="badge {{if eq $r.Status "done"}}ok{{else if eq $r.Status "failed"}}danger{{else}}warn{{end}}">{{$r.Status}}</span></h1>
|
||||
<p class="muted">From snapshot <strong>{{.Data.Snapshot.Name}}</strong> (event {{.Data.Snapshot.EventSeq}}), events {{$r.FromSeq}}–{{$r.ToSeq}} · {{$r.Summary.Steps}} steps · {{$r.Summary.Cycles}} cycles · model calls: {{$r.Summary.LLMReplayed}} replayed, {{$r.Summary.LLMLive}} live{{with $r.Error}} · <span class="danger">{{.}}</span>{{end}}</p>
|
||||
{{with .Data.Sandbox}}<p class="small">Sandbox mind: <a href="/minds/{{.Slug}}">{{.Slug}}</a> (disabled; kept for inspection) · <a href="/minds/{{$m.Slug}}/compare?with={{.Slug}}">compare with source</a></p>{{end}}</div>
|
||||
<div class="row"><span class="badge ok">{{$r.Summary.Exact}} exact</span> <span class="badge warn">{{$r.Summary.Approximate}} approximate</span> <span class="badge danger">{{$r.Summary.Diverged}} diverged</span></div></div>
|
||||
{{if eq $r.Status "running"}}<p class="muted small" data-live-refresh="5000">Running… this page refreshes.</p>{{end}}
|
||||
<div class="card">
|
||||
{{if .Data.Steps}}{{range .Data.Steps}}<div class="replay-step">
|
||||
<div class="row"><span class="badge {{if eq .Label "exact"}}ok{{else if eq .Label "approximate"}}warn{{else}}danger{{end}}">{{.Label}}</span> <strong>{{.Index}}.</strong> <a href="/events/{{.StimulusID}}">{{trunc .Summary 120}}</a> <span class="small muted">· {{.Cycles}} cycle(s){{if .LLMReplayed}} · {{.LLMReplayed}} recorded model response(s){{end}}{{if .LLMLive}} · {{.LLMLive}} live call(s){{end}}</span></div>
|
||||
{{with .Note}}<p class="small muted">{{.}}</p>{{end}}
|
||||
<div class="grid cols-2"><div><h3 class="small muted">Recorded</h3><ul class="small mono">{{range .Original}}<li>{{.}}</li>{{else}}<li class="muted">nothing</li>{{end}}</ul></div><div><h3 class="small muted">Replayed</h3><ul class="small mono">{{range .Replayed}}<li>{{.}}</li>{{else}}<li class="muted">nothing</li>{{end}}</ul></div></div>
|
||||
</div>{{end}}{{else}}<p class="muted">No steps recorded{{if eq $r.Status "running"}} yet{{end}}.</p>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,20 @@
|
||||
{{define "content"}}
|
||||
{{$m := .Data.Mind}}
|
||||
<div class="pagehead mindhead"><div><h1>{{$m.DisplayName}} <span class="muted small">cognitive replay</span></h1><p class="muted">Rebuild the mind from a snapshot, feed it the same perception events again, and compare what it produces with what was recorded. Steps are <strong>exact</strong> when the deterministic code produced the same events and any model calls were served from recordings, <strong>approximate</strong> when a live model call was needed, and <strong>diverged</strong> when a deterministic step differed. Replays run in a disabled sandbox mind and can never act.</p></div></div>
|
||||
{{template "mind_tabs" (dict "M" $m "Active" "replays")}}
|
||||
<section class="grid cols-3-1">
|
||||
<div class="card">
|
||||
{{if .Data.Replays}}<table class="table compact"><thead><tr><th>Started</th><th>Status</th><th class="num">Events</th><th class="num">Steps</th><th class="num">Exact</th><th class="num">Approx.</th><th class="num">Diverged</th><th class="num">Live model calls</th></tr></thead><tbody>
|
||||
{{range .Data.Replays}}<tr><td class="small muted nowrap"><a href="/replays/{{.ID}}">{{ago .CreatedAt}}</a> · {{.CreatedBy}}</td><td><span class="badge {{if eq .Status "done"}}ok{{else if eq .Status "failed"}}danger{{else}}warn{{end}}">{{.Status}}</span>{{with .Error}} <span class="small danger">{{.}}</span>{{end}}</td><td class="num small">{{.FromSeq}}–{{.ToSeq}}</td><td class="num">{{.Summary.Steps}}</td><td class="num">{{.Summary.Exact}}</td><td class="num">{{.Summary.Approximate}}</td><td class="num">{{.Summary.Diverged}}</td><td class="num">{{.Summary.LLMLive}}</td></tr>{{end}}</tbody></table>
|
||||
{{else}}<p class="muted">No replays yet.</p>{{end}}
|
||||
</div>
|
||||
<div class="card"><h2>Replay</h2>
|
||||
{{if not .Data.Snapshots}}<p class="muted small">Take a <a href="/minds/{{$m.Slug}}/snapshots">snapshot</a> first; replay starts from one.</p>
|
||||
{{else if .Principal.User.Role.AtLeast "operator"}}<form method="post" action="/minds/{{$m.Slug}}/replays" class="stack"><input type="hidden" name="_csrf" value="{{.CSRF}}">
|
||||
<label>Snapshot <select name="snapshot_id">{{range .Data.Snapshots}}<option value="{{.ID}}">{{.Name}} (event {{.EventSeq}})</option>{{end}}</select></label>
|
||||
<label>From event <input type="number" name="from_seq" value="0" min="0"> <span class="muted small">0 = snapshot position</span></label>
|
||||
<label>To event <input type="number" name="to_seq" value="{{.Data.LastSeq}}" min="0"> <span class="muted small">latest is {{.Data.LastSeq}}</span></label>
|
||||
<div><button class="btn btn-primary">Start replay</button></div></form>{{end}}
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
@@ -0,0 +1,23 @@
|
||||
{{define "content"}}
|
||||
{{$m := .Data.Mind}}
|
||||
<div class="pagehead mindhead"><div><h1>{{$m.DisplayName}} <span class="muted small">snapshots & forks</span></h1><p class="muted">A snapshot captures the mind's whole state (identity, personality, configuration, workspace, memories, beliefs, goals, relationships) at a point in its history. Forks start from a snapshot and share the history before it by reference.</p>
|
||||
{{with .Data.Parent}}<p class="small">Forked from <a href="/minds/{{.Slug}}">{{.DisplayName}}</a>{{with $m.ForkedFromSnapshot}} · <a href="/minds/{{$.Data.Parent.Slug}}/snapshots">snapshot</a>{{end}} · <a href="/minds/{{$m.Slug}}/compare?with={{.Slug}}">compare</a></p>{{end}}</div>
|
||||
<div class="row"><a class="btn" href="/api/v1/minds/{{$m.Slug}}/export">Export archive</a></div></div>
|
||||
{{template "mind_tabs" (dict "M" $m "Active" "snapshots")}}
|
||||
<section class="grid cols-3-1">
|
||||
<div class="card">
|
||||
{{if .Data.Snapshots}}<table class="table compact"><thead><tr><th>Name</th><th>Taken</th><th class="num">Event</th><th>Contents</th><th class="num">Size</th><th></th></tr></thead><tbody>
|
||||
{{range .Data.Snapshots}}<tr><td><strong>{{.Name}}</strong>{{with .Note}}<div class="small muted">{{.}}</div>{{end}}</td><td class="small muted nowrap">{{ago .CreatedAt}} · {{.CreatedBy}}</td><td class="num">{{.EventSeq}}</td>
|
||||
<td class="small muted">{{range $t, $n := .Counts}}{{$t}} {{$n}} · {{end}}</td><td class="num small">{{.SizeBytes}}</td>
|
||||
<td class="nowrap">{{if $.Principal.User.Role.AtLeast "operator"}}<a class="btn small" href="/api/v1/snapshots/{{.ID}}/archive">download</a>{{end}}
|
||||
{{if $.Principal.User.Role.AtLeast "administrator"}}<form method="post" action="/snapshots/{{.ID}}/fork" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input name="slug" placeholder="new-slug" size="12" pattern="[a-z0-9-]+" required> <button class="btn small">fork</button></form>
|
||||
<form method="post" action="/snapshots/{{.ID}}/delete" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><button class="btn small btn-danger" data-confirm="Delete this snapshot?">delete</button></form>{{end}}</td></tr>{{end}}</tbody></table>
|
||||
{{else}}<p class="muted">No snapshots yet.</p>{{end}}
|
||||
</div>
|
||||
<div class="stack">
|
||||
{{if .Principal.User.Role.AtLeast "operator"}}<div class="card"><h2>Take snapshot</h2><form method="post" action="/minds/{{$m.Slug}}/snapshots" class="stack"><input type="hidden" name="_csrf" value="{{.CSRF}}"><label>Name <input name="name" maxlength="80" placeholder="before experiment"></label><label>Note <input name="note" maxlength="200"></label><div><button class="btn btn-primary">Snapshot now</button></div></form></div>{{end}}
|
||||
<div class="card"><h2>Compare</h2><form method="get" action="/minds/{{$m.Slug}}/compare" class="row"><select name="with">{{range .Data.Others}}<option value="{{.Slug}}">{{.DisplayName}} ({{.Slug}})</option>{{end}}</select> <button class="btn">Compare</button></form><p class="small muted">Side-by-side counts, belief differences, personality and configuration diffs.</p></div>
|
||||
<div class="card"><h2>Replay</h2><p class="small">Re-run this mind's recorded cognition from a snapshot: <a href="/minds/{{$m.Slug}}/replays">replays</a>.</p></div>
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
@@ -3,6 +3,6 @@
|
||||
<a href="/minds/{{$m.Slug}}" {{if eq $a "overview"}}aria-current="page"{{end}}>Overview</a>
|
||||
<a href="/minds/{{$m.Slug}}/thoughts" {{if eq $a "thoughts"}}aria-current="page"{{end}}>Thoughts</a>
|
||||
<a href="/minds/{{$m.Slug}}/personality" {{if eq $a "personality"}}aria-current="page"{{end}}>Personality</a>
|
||||
<a href="/minds/{{$m.Slug}}/workspace" {{if eq $a "workspace"}}aria-current="page"{{end}}>Workspace</a><a href="/minds/{{$m.Slug}}/memory" {{if eq $a "memory"}}aria-current="page"{{end}}>Memory</a><a href="/minds/{{$m.Slug}}/beliefs" {{if eq $a "beliefs"}}aria-current="page"{{end}}>Beliefs</a><a href="/minds/{{$m.Slug}}/conversations" {{if eq $a "conversation"}}aria-current="page"{{end}}>Conversation</a><a href="/minds/{{$m.Slug}}/dreams" {{if eq $a "dreams"}}aria-current="page"{{end}}>Dreams</a>
|
||||
<a href="/minds/{{$m.Slug}}/workspace" {{if eq $a "workspace"}}aria-current="page"{{end}}>Workspace</a><a href="/minds/{{$m.Slug}}/memory" {{if eq $a "memory"}}aria-current="page"{{end}}>Memory</a><a href="/minds/{{$m.Slug}}/beliefs" {{if eq $a "beliefs"}}aria-current="page"{{end}}>Beliefs</a><a href="/minds/{{$m.Slug}}/conversations" {{if eq $a "conversation"}}aria-current="page"{{end}}>Conversation</a><a href="/minds/{{$m.Slug}}/dreams" {{if eq $a "dreams"}}aria-current="page"{{end}}>Dreams</a><a href="/minds/{{$m.Slug}}/snapshots" {{if eq $a "snapshots"}}aria-current="page"{{end}}>Snapshots</a><a href="/minds/{{$m.Slug}}/replays" {{if eq $a "replays"}}aria-current="page"{{end}}>Replay</a>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user