Files
echo/echo-memory-stakeholder-brief.html
T
Jason Stedwell 151b962662 Documentation
2026-06-22 17:44:37 -05:00

334 lines
25 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ECHO Memory — Stakeholder Brief</title>
<style>
:root{
--ink:#16181d; --ink-soft:#3a3f4a; --muted:#6b7280;
--line:#e4e7ec; --line-soft:#eef1f4;
--bg:#ffffff; --panel:#f7f8fa; --panel-2:#f0f4f9;
--accent:#2f6df6; --accent-deep:#1b3a8f; --accent-soft:#e8f0ff;
--good:#0f9d6b; --good-soft:#e6f6ef; --bad:#d64545; --bad-soft:#fdecec;
--warn:#b7791f; --code-bg:#0f1320; --code-ink:#e6e9f2;
--radius:14px;
}
*{box-sizing:border-box;}
html{-webkit-print-color-adjust:exact; print-color-adjust:exact;}
body{
margin:0; color:var(--ink); background:#eceef1;
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
line-height:1.55; font-size:15px;
}
.page{
max-width:880px; margin:32px auto; background:var(--bg);
padding:0 0 56px; border-radius:18px; overflow:hidden;
box-shadow:0 10px 40px rgba(20,24,35,.10);
}
/* ---- cover / hero ---- */
.hero{
background:linear-gradient(135deg,#101a3a 0%,#1b3a8f 55%,#2f6df6 100%);
color:#fff; padding:48px 56px 42px; position:relative;
}
.hero .kicker{font-size:12px; letter-spacing:.18em; text-transform:uppercase; opacity:.8; margin:0 0 14px;}
.hero h1{font-size:40px; line-height:1.08; margin:0 0 10px; font-weight:750; letter-spacing:-.5px;}
.hero .sub{font-size:17px; opacity:.92; max-width:620px; margin:0 0 22px;}
.hero .meta{display:flex; flex-wrap:wrap; gap:10px 26px; font-size:13px; opacity:.92; border-top:1px solid rgba(255,255,255,.18); padding-top:18px;}
.hero .meta b{font-weight:650;}
.pill{display:inline-block; background:rgba(255,255,255,.16); border:1px solid rgba(255,255,255,.28); padding:3px 11px; border-radius:999px; font-size:12px; font-weight:600;}
/* ---- body ---- */
.body{padding:8px 56px 0;}
section{padding:30px 0; border-bottom:1px solid var(--line-soft);}
section:last-child{border-bottom:none;}
h2{font-size:13px; letter-spacing:.14em; text-transform:uppercase; color:var(--accent-deep); margin:0 0 4px; font-weight:700;}
.h2sub{font-size:23px; font-weight:700; letter-spacing:-.3px; margin:0 0 18px; color:var(--ink);}
h3{font-size:16px; margin:22px 0 8px; font-weight:680;}
p{margin:0 0 13px; color:var(--ink-soft);}
a{color:var(--accent); text-decoration:none;}
strong{color:var(--ink);}
code{background:var(--panel-2); padding:1px 6px; border-radius:5px; font-family:"SF Mono",ui-monospace,Menlo,Consolas,monospace; font-size:12.5px; color:var(--accent-deep);}
/* lede */
.lede{font-size:17px; color:var(--ink); background:var(--accent-soft); border-left:4px solid var(--accent); padding:16px 20px; border-radius:0 10px 10px 0; margin:0 0 4px;}
/* value grid */
.grid{display:grid; gap:14px;}
.g3{grid-template-columns:repeat(3,1fr);}
.g2{grid-template-columns:repeat(2,1fr);}
.card{background:var(--panel); border:1px solid var(--line); border-radius:var(--radius); padding:16px 17px;}
.card .ic{width:30px; height:30px; border-radius:9px; margin-bottom:10px; display:block; background:linear-gradient(135deg,#2f6df6,#1b3a8f);}
.card h4{margin:0 0 5px; font-size:14.5px; font-weight:680;}
.card p{margin:0; font-size:13px; color:var(--muted); line-height:1.5;}
/* metric cards */
.metrics{display:grid; grid-template-columns:repeat(4,1fr); gap:13px; margin:6px 0 20px;}
.metric{background:var(--panel); border:1px solid var(--line); border-radius:var(--radius); padding:16px 14px; text-align:center;}
.metric .num{font-size:30px; font-weight:780; letter-spacing:-1px; line-height:1; color:var(--accent-deep);}
.metric .num.good{color:var(--good);}
.metric .lbl{font-size:11.5px; color:var(--muted); margin-top:7px; line-height:1.35;}
/* table */
table{width:100%; border-collapse:collapse; font-size:13px; margin:8px 0 6px;}
th,td{text-align:left; padding:9px 11px; border-bottom:1px solid var(--line);}
th{background:var(--panel-2); font-size:11px; letter-spacing:.05em; text-transform:uppercase; color:var(--ink-soft); font-weight:680;}
td.n{font-variant-numeric:tabular-nums; text-align:right; white-space:nowrap;}
tr td:first-child{font-weight:600; color:var(--ink);}
.tag-good{color:var(--good); font-weight:700;}
.tag-bad{color:var(--bad); font-weight:700;}
.delta{font-weight:700; color:var(--good);}
/* code block */
pre{background:var(--code-bg); color:var(--code-ink); border-radius:12px; padding:16px 18px; overflow:auto; font-size:12.5px; line-height:1.6; font-family:"SF Mono",ui-monospace,Menlo,Consolas,monospace; margin:10px 0 14px;}
pre .c{color:#7d8aa5;} pre .k{color:#8fd0ff;} pre .s{color:#a5e6b0;}
/* command list */
.cmds{display:grid; grid-template-columns:repeat(2,1fr); gap:10px; margin:8px 0 4px;}
.cmd{display:flex; gap:11px; align-items:flex-start; background:var(--panel); border:1px solid var(--line); border-radius:10px; padding:11px 13px;}
.cmd code{background:var(--code-bg); color:#8fd0ff; font-weight:600; white-space:nowrap;}
.cmd span{font-size:12.5px; color:var(--muted);}
/* steps */
.step{display:flex; gap:14px; margin:0 0 14px; align-items:flex-start;}
.step .no{flex:0 0 28px; height:28px; width:28px; border-radius:50%; background:var(--accent); color:#fff; font-weight:700; font-size:13px; display:flex; align-items:center; justify-content:center;}
.step .tx{flex:1;} .step .tx b{display:block; margin-bottom:2px;}
.step .tx p{margin:0; font-size:13px; color:var(--muted);}
/* callout */
.note{background:var(--good-soft); border:1px solid #bfe6d4; border-radius:10px; padding:13px 16px; font-size:13px; color:#0c5c43; margin:14px 0;}
.note.warn{background:#fff7e6; border-color:#f0dca8; color:#7a5a12;}
.caption{font-size:11.5px; color:var(--muted); margin-top:6px; font-style:italic;}
/* svg wrap */
.diagram{background:var(--panel); border:1px solid var(--line); border-radius:var(--radius); padding:18px; margin:6px 0;}
.footer{padding:26px 56px 4px; color:var(--muted); font-size:12px;}
ul.clean{margin:6px 0 13px; padding-left:18px;} ul.clean li{margin:0 0 6px; color:var(--ink-soft);}
@media print{
body{background:#fff;} .page{box-shadow:none; margin:0; max-width:none; border-radius:0;}
section{page-break-inside:avoid;} .hero{border-radius:0;}
.diagram, .metrics, table, .cmds, .grid{page-break-inside:avoid;}
}
@media (max-width:680px){
.g3,.g2,.metrics,.cmds{grid-template-columns:1fr;} .hero,.body,.footer{padding-left:26px; padding-right:26px;} .hero h1{font-size:30px;}
}
</style>
</head>
<body>
<div class="page">
<!-- ========== HERO ========== -->
<div class="hero">
<p class="kicker">Internal Tooling Brief · CoWork / Claude Plugin</p>
<h1>ECHO Memory</h1>
<p class="sub">Persistent, cross-session memory for Claude and CoWork. The assistant loads your context, decisions, and projects at the start of every session. No re-explaining.</p>
<div class="meta">
<span class="pill">v1.0.0 · Schema 4</span>
<span><b>Owner:</b> Jason Stedwell</span>
<span><b>Stack:</b> Pure Python · Obsidian REST API</span>
<span><b>Platforms:</b> Windows · macOS · Linux</span>
</div>
</div>
<div class="body">
<!-- ========== TL;DR ========== -->
<section>
<h2>The one-liner</h2>
<p class="lede">Claude forgets everything when a chat closes. ECHO fixes that. It gives the assistant a structured Obsidian vault that it reads at session start and writes to as you work. The result: <strong>memory you can trust and retrieve</strong>, running identically on every machine.</p>
</section>
<!-- ========== WHY ========== -->
<section>
<h2>Why it matters</h2>
<div class="h2sub">The problem it solves</div>
<p>Every session starts cold. You re-explain who you are, what you're building, and what was decided last week. Notes scatter, duplicate, or land in the wrong place. Worse, writes fail silently — you think a note saved when it didn't. ECHO closes all four gaps.</p>
<div class="grid g3">
<div class="card"><span class="ic"></span><h4>Remembers across sessions</h4><p>Profile, active scope, recent sessions, and open projects load automatically at session start. No re-briefing.</p></div>
<div class="card"><span class="ic"></span><h4>Routes itself</h4><p>One <code>capture</code> call files a note, stamps it, indexes it, and cross-links it. No manual filing.</p></div>
<div class="card"><span class="ic"></span><h4>Recalls neighbourhoods</h4><p>Ask about a topic. Get the note plus everything linked to it — people, decisions, meetings. Not one fragment.</p></div>
</div>
</section>
<!-- ========== HOW TO USE ========== -->
<section>
<h2>Getting started</h2>
<div class="h2sub">How to use it</div>
<p>Most of the time you do nothing. The skill fires on phrases like <em>"remember that,"</em> <em>"what do you know about me,"</em> and <em>"log this decision,"</em> and loads your context at the start of real work. For direct control, use the eight slash commands:</p>
<div class="cmds">
<div class="cmd"><code>/echo-load</code><span>Cold-start read — profile, scope, recent sessions, inbox.</span></div>
<div class="cmd"><code>/echo-save</code><span>Route and persist a memory to its correct home in one call.</span></div>
<div class="cmd"><code>/echo-recall</code><span>Pull a topic plus its linked neighbourhood.</span></div>
<div class="cmd"><code>/echo-reflect</code><span>Extract durable takeaways from the session for one-tap capture.</span></div>
<div class="cmd"><code>/echo-triage</code><span>Drain the inbox — route aging captures to their homes.</span></div>
<div class="cmd"><code>/echo-health</code><span>Run the linter — surface drift, broken links, orphans.</span></div>
<div class="cmd"><code>/echo-sweep</code><span>Bring an upgraded vault up to spec — index plus links.</span></div>
<div class="cmd"><code>/echo-doctor</code><span>Readiness check — Python, reachability, auth, schema.</span></div>
</div>
<h3>A typical session</h3>
<div class="step"><div class="no">1</div><div class="tx"><b>Start working</b><p>ECHO loads your profile, active scope, last session, today's note, and inbox. It confirms scope before doing anything.</p></div></div>
<div class="step"><div class="no">2</div><div class="tx"><b>Just talk</b><p>Mention a decision, a project, or a preference. Say "remember that" and it's filed and cross-linked.</p></div></div>
<div class="step"><div class="no">3</div><div class="tx"><b>Recall on demand</b><p>"What do we know about the ALABAMA wISP rollout?" returns the project note and its linked people, decisions, and meetings.</p></div></div>
<div class="step"><div class="no">4</div><div class="tx"><b>Session ends</b><p>ECHO writes a one-line Agent-Log entry and a session log, then drops a heartbeat pointer to resume from next time.</p></div></div>
<div class="note">Setup is two steps: run Obsidian with the Local REST API plugin, and set an API key (<code>echo.py write-key &lt;token&gt;</code> or the env var). Point the plugin at an empty vault and it builds the full structure itself.</div>
</section>
<!-- ========== ARCHITECTURE ========== -->
<section>
<h2>Under the hood</h2>
<div class="h2sub">Architecture</div>
<p>One principle drives the design: <strong>the plugin is the single source of truth.</strong> All behavior — bootstrap logic, the operating contract, taxonomy, routing rules, and note templates — ships <em>inside</em> the plugin. The vault holds <strong>data only</strong>. That makes the system self-bootstrapping, updatable in one place, and portable across machines.</p>
<div class="diagram">
<svg viewBox="0 0 820 400" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,Segoe UI,Roboto,sans-serif" width="100%">
<!-- lanes -->
<text x="20" y="26" font-size="12" font-weight="700" fill="#1b3a8f" letter-spacing="1">CLIENT / SESSION</text>
<text x="305" y="26" font-size="12" font-weight="700" fill="#1b3a8f" letter-spacing="1">PLUGIN (logic + tooling)</text>
<text x="650" y="26" font-size="12" font-weight="700" fill="#1b3a8f" letter-spacing="1">VAULT (data only)</text>
<!-- client box -->
<rect x="20" y="44" width="210" height="120" rx="12" fill="#e8f0ff" stroke="#2f6df6"/>
<text x="125" y="78" font-size="14" font-weight="700" fill="#16181d" text-anchor="middle">Claude / CoWork</text>
<text x="125" y="100" font-size="11.5" fill="#3a3f4a" text-anchor="middle">slash commands</text>
<text x="125" y="118" font-size="11.5" fill="#3a3f4a" text-anchor="middle">+ auto skill triggers</text>
<text x="125" y="142" font-size="11.5" fill="#6b7280" text-anchor="middle">"remember that…"</text>
<!-- plugin box -->
<rect x="288" y="44" width="290" height="300" rx="12" fill="#f7f8fa" stroke="#c9d2e0"/>
<rect x="305" y="62" width="256" height="52" rx="9" fill="#fff" stroke="#2f6df6"/>
<text x="433" y="83" font-size="12.5" font-weight="700" fill="#1b3a8f" text-anchor="middle">echo.py — validated client</text>
<text x="433" y="101" font-size="10.5" fill="#6b7280" text-anchor="middle">auth · status-check · retry · read-back verify</text>
<rect x="305" y="124" width="123" height="50" rx="8" fill="#fff" stroke="#c9d2e0"/>
<text x="366" y="145" font-size="11" font-weight="600" fill="#16181d" text-anchor="middle">capture / recall</text>
<text x="366" y="161" font-size="10" fill="#6b7280" text-anchor="middle">route · link · ops</text>
<rect x="438" y="124" width="123" height="50" rx="8" fill="#fff" stroke="#c9d2e0"/>
<text x="499" y="145" font-size="11" font-weight="600" fill="#16181d" text-anchor="middle">entity index</text>
<text x="499" y="161" font-size="10" fill="#6b7280" text-anchor="middle">O(1) resolve</text>
<rect x="305" y="184" width="123" height="50" rx="8" fill="#fff" stroke="#c9d2e0"/>
<text x="366" y="205" font-size="11" font-weight="600" fill="#16181d" text-anchor="middle">offline queue</text>
<text x="366" y="221" font-size="10" fill="#6b7280" text-anchor="middle">WAL + read cache</text>
<rect x="438" y="184" width="123" height="50" rx="8" fill="#fff" stroke="#c9d2e0"/>
<text x="499" y="205" font-size="11" font-weight="600" fill="#16181d" text-anchor="middle">vault lock</text>
<text x="499" y="221" font-size="10" fill="#6b7280" text-anchor="middle">concurrency</text>
<rect x="305" y="244" width="256" height="44" rx="8" fill="#fff" stroke="#c9d2e0"/>
<text x="433" y="263" font-size="11" font-weight="600" fill="#16181d" text-anchor="middle">routing.json · vault_lint.py</text>
<text x="433" y="279" font-size="10" fill="#6b7280" text-anchor="middle">linter-enforced rules + graph health</text>
<rect x="305" y="298" width="256" height="34" rx="8" fill="#fff" stroke="#c9d2e0"/>
<text x="433" y="319" font-size="11" font-weight="600" fill="#16181d" text-anchor="middle">scaffold/ — bootstrap templates</text>
<!-- vault box -->
<rect x="636" y="44" width="164" height="300" rx="12" fill="#e6f6ef" stroke="#0f9d6b"/>
<text x="718" y="70" font-size="12" font-weight="700" fill="#0c5c43" text-anchor="middle">Obsidian vault</text>
<text x="718" y="86" font-size="10" fill="#0c5c43" text-anchor="middle">via Local REST API</text>
<g font-size="10.5" fill="#16181d" text-anchor="start">
<rect x="652" y="108" width="9" height="9" rx="2" fill="#0f9d6b"/><text x="668" y="116">inbox/</text>
<rect x="652" y="132" width="9" height="9" rx="2" fill="#0f9d6b"/><text x="668" y="140">journal/</text>
<rect x="652" y="156" width="9" height="9" rx="2" fill="#0f9d6b"/><text x="668" y="164">projects/</text>
<rect x="652" y="180" width="9" height="9" rx="2" fill="#0f9d6b"/><text x="668" y="188">areas/</text>
<rect x="652" y="204" width="9" height="9" rx="2" fill="#0f9d6b"/><text x="668" y="212">resources/</text>
<rect x="652" y="228" width="9" height="9" rx="2" fill="#0f9d6b"/><text x="668" y="236">decisions/</text>
<rect x="652" y="252" width="9" height="9" rx="2" fill="#1b3a8f"/><text x="668" y="260">_agent/memory</text>
<rect x="652" y="276" width="9" height="9" rx="2" fill="#1b3a8f"/><text x="668" y="284">_agent/index</text>
<rect x="652" y="300" width="9" height="9" rx="2" fill="#1b3a8f"/><text x="668" y="308">_agent/locks</text>
<rect x="652" y="324" width="9" height="9" rx="2" fill="#1b3a8f"/><text x="668" y="332">echo-vault.md</text>
</g>
<!-- arrows -->
<defs><marker id="ar" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#1b3a8f"/></marker></defs>
<line x1="230" y1="104" x2="286" y2="104" stroke="#1b3a8f" stroke-width="2" marker-end="url(#ar)"/>
<line x1="579" y1="190" x2="634" y2="190" stroke="#0f9d6b" stroke-width="2" marker-end="url(#ar)"/>
<line x1="634" y1="210" x2="579" y2="210" stroke="#0f9d6b" stroke-width="2" marker-end="url(#ar)"/>
</svg>
<p class="caption">Sessions talk to the plugin. The plugin owns all logic and is the only thing that touches the vault, through one validated client. The vault stores structured markdown — nothing else.</p>
</div>
<h3>Key components</h3>
<table>
<thead><tr><th>Component</th><th>What it does</th></tr></thead>
<tbody>
<tr><td>echo.py</td><td>The validated client and CLI. Every read and write injects auth, checks HTTP status, retries transient errors, and read-back-verifies the result. A failed write fails loud, not silent.</td></tr>
<tr><td>capture / recall / resolve / link</td><td>The do-it-for-me layer. <code>capture</code> routes, stamps, indexes, auto-links, and logs in one call. <code>recall</code> returns a topic's linked neighbourhood.</td></tr>
<tr><td>Entity index</td><td>A slug→{path, kind, aliases} registry. Turns routing into an O(1), alias-aware lookup instead of a fuzzy search.</td></tr>
<tr><td>Hybrid recall</td><td>Local BM25 over note bodies, fused with decayed graph expansion. Keyword relevance and link structure, together.</td></tr>
<tr><td>Offline queue + cache</td><td>Vault unreachable? Writes queue to a local write-ahead log and replay idempotently later. Reads fall back to a last-known-good cache.</td></tr>
<tr><td>routing.json + linter</td><td>A machine-readable manifest of what writes where, enforced by a read-only checker — drift, broken links, orphans, frontmatter integrity.</td></tr>
<tr><td>scaffold/ + bootstrap</td><td>Templates and seeds that stand up an empty vault deterministically and idempotently.</td></tr>
</tbody>
</table>
<h3>How memory is organized</h3>
<p>ECHO mirrors a working memory model: <strong>working</strong> (transient state), <strong>episodic</strong> (what happened, when), <strong>semantic</strong> (durable facts and preferences), and <strong>context</strong> (the active focus). Daily content lives in plain folders — <code>projects/</code>, <code>journal/</code>, <code>decisions/</code>, <code>resources/</code>. The agent's bookkeeping sits under <code>_agent/</code>.</p>
</section>
<!-- ========== METRICS ========== -->
<section>
<h2>Does it actually help?</h2>
<div class="h2sub">Efficiency and reliability metrics</div>
<p>A credential-free A/B harness compares the plugin <strong>before</strong> (hand-built REST calls from the old skill docs) and <strong>after</strong> (the shipped validated client) across six representative operations, with deterministic fault injection. It measures the plumbing: token cost of the I/O layer and the rate of silent write failures.</p>
<div class="metrics">
<div class="metric"><div class="num good">71%</div><div class="lbl">fewer tokens generated for the I/O layer</div></div>
<div class="metric"><div class="num good">4→0</div><div class="lbl">silent write failures eliminated</div></div>
<div class="metric"><div class="num good">5/5</div><div class="lbl">silent-error-free operations (was 1/5)</div></div>
<div class="metric"><div class="num good">3×</div><div class="lbl">platforms supported — Win / macOS / Linux</div></div>
</div>
<table>
<thead><tr><th>Metric</th><th class="n">Before (0.6)</th><th class="n">After (0.7+)</th><th class="n">Delta</th></tr></thead>
<tbody>
<tr><td>Generated tokens (I/O layer)</td><td class="n">608</td><td class="n">174</td><td class="n delta">71%</td></tr>
<tr><td>Silent failures</td><td class="n tag-bad">4</td><td class="n tag-good">0</td><td class="n delta">4</td></tr>
<tr><td>Duplicate lines written</td><td class="n tag-bad">1</td><td class="n tag-good">0</td><td class="n delta">1</td></tr>
<tr><td>Silent-error-free ops</td><td class="n">1 / 5</td><td class="n">5 / 5</td><td class="n delta">+4</td></tr>
<tr><td>Effective tokens (incl. recovery)</td><td class="n">6,608</td><td class="n">334</td><td class="n delta">95%</td></tr>
</tbody>
</table>
<p class="caption">Source: <code>eval/run_eval.py</code> (mock OLRAPI, chars/token = 4.0). Generated-token counts are a proxy for the I/O layer only. "Effective tokens" applies a tunable recovery penalty on top of the hard silent-failure count.</p>
<div class="note warn"><strong>What this measures:</strong> deterministic mechanics, not model reasoning (routing choices or prose quality). The "4 → 0 silent failures" figure is a hard count from ground truth. The recovery-weighted number is a model on top of it. CI runs the offline, reflect, and patch-semantics suites across Windows/macOS/Linux × Python 3.10/3.12.</div>
<h3>Why "silent failures" are the real win</h3>
<p>A failed write that looks like success is the worst outcome for a memory system. You trust a note that never saved. The old approach hid four across six operations. The validated client surfaces every one — non-zero exit, retry, read-back verify — so the assistant recovers instead of moving on. That is the line between memory and memory you can trust.</p>
</section>
<!-- ========== RELIABILITY ========== -->
<section>
<h2>Built to be safe</h2>
<div class="h2sub">Reliability and safety guarantees</div>
<div class="grid g2">
<div class="card"><h4>Search-first writes</h4><p>Before creating a note, ECHO searches the whole vault for the slug and title. No duplicates scattered across folders.</p></div>
<div class="card"><h4>Idempotent appends</h4><p>Reads before appending, skips on an exact whole-line match. A retry never duplicates an entry.</p></div>
<div class="card"><h4>Concurrency-safe</h4><p>A cooperative lock plus re-read-merge closes the race when Claude Code and CoWork write at once.</p></div>
<div class="card"><h4>Never fabricates</h4><p>The operating contract forbids inventing facts or decisions, mass restructuring, and storing secrets in notes.</p></div>
<div class="card"><h4>Graceful offline</h4><p>Vault down? ECHO says so once, queues writes, and proceeds. No retry storms.</p></div>
<div class="card"><h4>Self-healing schema</h4><p>Deterministic bootstrap, migration, and sweep scripts keep any vault on the current spec.</p></div>
</div>
</section>
<!-- ========== JOURNEY ========== -->
<section>
<h2>How it got here</h2>
<div class="h2sub">Maturity at a glance</div>
<table>
<thead><tr><th>Version</th><th>Milestone</th></tr></thead>
<tbody>
<tr><td>0.30.4</td><td>Source promoted to a tracked tree. Search-first writes, project lifecycle, scope switching, monthly vault-health.</td></tr>
<tr><td>0.5</td><td>Self-bootstrap. The plugin becomes the single source of truth; vault migrated to data-only.</td></tr>
<tr><td>0.7</td><td>Validated client (<code>echo.py</code>), routing manifest plus linter, A/B eval harness: 71% I/O tokens, 4→0 silent failures.</td></tr>
<tr><td>0.8</td><td>Toolchain ported to pure Python. Runs identically on Windows, macOS, Linux.</td></tr>
<tr><td>0.9</td><td>Entity index, hybrid recall, bidirectional auto-linking, graph-health checks.</td></tr>
<tr><td><strong>1.0</strong></td><td><strong>"Memory you can trust and retrieve."</strong> Hybrid BM25+graph recall, offline durability, concurrency hardening, reflection capture, secret hardening. CI across 3 OSes × 2 Python versions.</td></tr>
</tbody>
</table>
</section>
</div>
<div class="footer">
<p style="margin:0 0 4px;"><strong>echo-memory v1.0.0</strong> · Internal stakeholder brief · Generated June 22, 2026</p>
<p style="margin:0;">Personal plugin built and maintained by Jason Stedwell (MPM / ALABAMA wISP). Endpoint and credentials are private. Not for external distribution.</p>
</div>
</div>
</body>
</html>