This commit is contained in:
jason
2026-06-22 23:55:19 -05:00
parent 151b962662
commit d34fbf7626
15 changed files with 571 additions and 55 deletions
+222
View File
@@ -0,0 +1,222 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ECHO Memory — Performance Update</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);
}
.hero{
background:linear-gradient(135deg,#101a3a 0%,#1b3a8f 55%,#2f6df6 100%);
color:#fff; padding:48px 56px 40px; position:relative;
}
.hero .kicker{font-size:12px; letter-spacing:.18em; text-transform:uppercase; opacity:.82; margin:0 0 14px;}
.hero h1{font-size:38px; line-height:1.08; margin:0 0 10px; font-weight:750; letter-spacing:-.5px;}
.hero .sub{font-size:16.5px; opacity:.92; max-width:640px; 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:16px;}
.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; margin-right:6px;}
.body{padding:8px 56px 0;}
section{padding:28px 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:22px; font-weight:700; letter-spacing:-.3px; margin:0 0 16px; color:var(--ink);}
h3{font-size:15.5px; margin:20px 0 7px; font-weight:680;}
p{margin:0 0 13px; color:var(--ink-soft);}
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{font-size:16.5px; color:var(--ink); background:var(--accent-soft); border-left:4px solid var(--accent); padding:15px 19px; border-radius:0 10px 10px 0; margin:0;}
.metrics{display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin:4px 0 6px;}
.metric{background:var(--panel); border:1px solid var(--line); border-radius:var(--radius); padding:16px 13px; 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;}
.grid{display:grid; gap:13px;}
.g2{grid-template-columns:repeat(2,1fr);}
.card{background:var(--panel); border:1px solid var(--line); border-radius:var(--radius); padding:15px 16px;}
.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;}
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);}
.delta{font-weight:700; color:var(--good);}
.muted-row td{color:var(--muted);}
/* bar chart */
.bars{margin:14px 0 4px;}
.bar{display:flex; align-items:center; gap:12px; margin:0 0 9px;}
.bar .name{flex:0 0 168px; font-size:12.5px; color:var(--ink-soft); text-align:right;}
.bar .track{flex:1; background:var(--panel-2); border-radius:7px; height:26px; position:relative; overflow:hidden;}
.bar .fill{height:100%; border-radius:7px; background:linear-gradient(90deg,#2f6df6,#1b3a8f); display:flex; align-items:center; justify-content:flex-end; padding-right:9px; color:#fff; font-size:12px; font-weight:700;}
.bar .fill.old{background:linear-gradient(90deg,#e87f7f,#d64545);}
.bar .fill.best{background:linear-gradient(90deg,#23b783,#0f9d6b);}
.note{background:var(--good-soft); border:1px solid #bfe6d4; border-radius:10px; padding:12px 16px; font-size:12.5px; color:#0c5c43; margin:14px 0 0;}
.warn{background:#fff6e6; border:1px solid #f0d9a8; color:#7a5712;}
.foot{padding:22px 56px 0; color:var(--muted); font-size:11.5px; line-height:1.5;}
.foot b{color:var(--ink-soft);}
@media print{
body{background:#fff;}
.page{margin:0; box-shadow:none; border-radius:0; max-width:none; padding-bottom:24px;}
@page{ size:A4; margin:13mm; }
section,.metrics,.bars,table,.grid{break-inside:avoid;}
.hero{border-radius:0;}
}
</style>
</head>
<body>
<div class="page">
<div class="hero">
<p class="kicker">ECHO Memory · Performance Update</p>
<h1>Full-vault operations: from timing out to sub-second</h1>
<p class="sub">Releases v1.1.0 and v1.2.0 rebuilt the vault's network layer and entity
resolution. Long operations that used to exceed the agent tool timeout and drop the
session now complete in under a second — and shortened entity names resolve instead of
spawning duplicates.</p>
<div class="meta">
<span><span class="pill">v1.1.0</span><span class="pill">v1.2.0</span></span>
<span><b>Prepared</b> 2026-06-22</span>
<span><b>Vault</b> 186 notes · 119 entities</span>
<span><b>Endpoint</b> echoapi.alwisp.com</span>
</div>
</div>
<div class="body">
<section>
<p class="lede">Scripts that sweep the whole vault — <code>sweep.py</code>,
<code>vault_lint.py</code>, recall rebuild — were making hundreds of <em>serial</em>
requests, each opening a fresh TLS connection, and re-reading every note 23 times.
On the constrained agent sandbox that pushed a single pass past the ~120-second tool
timeout, so the process was killed mid-run and the session/thread was dropped. The
fix attacks the cause on three axes; the headline change is reliability:
<strong>a full-vault pass that used to fail now finishes in well under a second.</strong></p>
</section>
<section>
<h2>At a glance</h2>
<div class="metrics">
<div class="metric"><div class="num">4.5&times;</div><div class="lbl">faster per request<br>(connection reuse)</div></div>
<div class="metric"><div class="num">~2&times;</div><div class="lbl">added by concurrency<br>(full-vault scale)</div></div>
<div class="metric"><div class="num">23&times;</div><div class="lbl">fewer requests<br>(single-pass cache)</div></div>
<div class="metric"><div class="num good">&lt;1 s</div><div class="lbl">full-vault pass<br>(was: timeout)</div></div>
</div>
<p style="margin-top:6px">These factors compound. A full-vault pass pays
<strong>4.5&times;</strong> less per request, issues <strong>~23&times;</strong> fewer
of them, and runs the rest <strong>~2&times;</strong> in parallel — a modeled
<strong>~1827&times;</strong> reduction in total request-time, before counting the
higher connection latency of the production sandbox where the old path actually failed.</p>
</section>
<section>
<h2>What changed</h2>
<div class="h2sub">Three structural fixes, one resolution overhaul</div>
<div class="grid g2">
<div class="card"><h4>Connection reuse (keep-alive)</h4><p>One persistent
connection per thread, reused across every request — instead of a fresh TCP+TLS
handshake per file. The single biggest win; benefits every operation
(load, capture, recall) for free.</p></div>
<div class="card"><h4>Concurrent bulk reads</h4><p>A new <code>read_many()</code>
fans GETs across a thread pool (8 workers). I/O-bound work, so it parallelizes
cleanly; the win grows with vault size and network latency.</p></div>
<div class="card"><h4>Single-pass shared cache</h4><p>Each note is fetched
<em>once</em> and reused across all passes — eliminating the 23&times; re-reads and
the per-link-target re-fetch storm in <code>sweep</code>.</p></div>
<div class="card"><h4>Entity resolution (v1.2.0)</h4><p>Shortened names now resolve
via aliases + a fuzzy fallback, so a mention like &ldquo;echo memory&rdquo; finds the
existing project instead of silently creating a duplicate note.</p></div>
</div>
</section>
<section>
<h2>Measured — per request</h2>
<div class="h2sub">Connection reuse is the dominant win</div>
<p>Micro-benchmark: 25 real notes, best of 3 runs, against the live endpoint.</p>
<div class="bars">
<div class="bar"><div class="name">Fresh connection / GET <em>(old)</em></div>
<div class="track"><div class="fill old" style="width:100%">30.3 ms</div></div></div>
<div class="bar"><div class="name">Pooled keep-alive (serial)</div>
<div class="track"><div class="fill" style="width:21.8%">6.6 ms</div></div></div>
<div class="bar"><div class="name">Pooled + concurrent <em>(new)</em></div>
<div class="track"><div class="fill best" style="width:18.8%">5.7 ms</div></div></div>
</div>
<table>
<tr><th>Mode</th><th class="n">ms / request</th><th class="n">vs old</th></tr>
<tr><td>Fresh connection per GET (old)</td><td class="n">30.3</td><td class="n">1.0&times;</td></tr>
<tr><td>Pooled keep-alive, serial</td><td class="n">6.6</td><td class="n delta">4.5&times;</td></tr>
<tr><td>Pooled + concurrent (new default)</td><td class="n">5.7</td><td class="n delta">5.3&times;</td></tr>
</table>
</section>
<section>
<h2>Measured — full vault (186 notes)</h2>
<div class="h2sub">Serial vs concurrent, both pooled</div>
<table>
<tr><th>Operation</th><th class="n">serial (1 worker)</th><th class="n">concurrent (8)</th><th class="n">speedup</th></tr>
<tr><td>vault_lint.py</td><td class="n">1.77 s</td><td class="n">0.90 s</td><td class="n delta">2.0&times;</td></tr>
<tr><td>sweep.py (plan)</td><td class="n">1.64 s</td><td class="n">0.85 s</td><td class="n delta">1.9&times;</td></tr>
<tr><td>sweep.py --apply</td><td class="n">&mdash;</td><td class="n">1.07 s</td><td class="n">&mdash;</td></tr>
<tr class="muted-row"><td>single GET · doctor</td><td class="n">&mdash;</td><td class="n">0.28 s · 0.21 s</td><td class="n">&mdash;</td></tr>
</table>
<div class="note warn"><strong>Before the fix:</strong> the same passes exceeded the
~120-second agent tool ceiling and were terminated mid-run, dropping the session.
The numbers above are the <em>new</em> floor; the practical change is
<strong>fails&nbsp;&rarr;&nbsp;completes</strong>.</div>
</section>
<section>
<h2>Efficiency — entity resolution (v1.2.0)</h2>
<div class="h2sub">Fewer duplicates, less wasted work</div>
<p>Resolution was exact-match only, so a shortened or expanded name returned
<em>nothing</em> — and the writer would create a parallel note. The active project
slugged <code>echo</code> was invisible to the mention &ldquo;echo memory.&rdquo; Now:</p>
<table>
<tr><th>Lookup</th><th>Before</th><th>After</th></tr>
<tr><td>echo memory · echo plugin · echo-memory</td><td><span class="delta" style="color:var(--bad)">no match</span></td><td class="delta">&rarr; projects/active/echo.md</td></tr>
<tr><td>non-aliased shortened name</td><td><span style="color:var(--bad);font-weight:700">no match</span></td><td class="delta">ranked candidates</td></tr>
<tr><td>update under a new name</td><td>second slug, same note</td><td class="delta">canonical slug + alias learned</td></tr>
</table>
<p style="margin-top:10px">Aliases are auto-derived from titles, learned from mentions on
update, and stored in note frontmatter (folded back into the index on sweep) — so the
graph gets <em>better</em> at resolution over time, while exact-match safety prevents
wrong auto-merges. The payoff is avoided duplicate entities and the cleanup/merge passes
they trigger.</p>
</section>
</div>
<div class="foot">
<p><b>Methodology.</b> Measured on the maintainer's workstation against the live
<code>echoapi.alwisp.com</code> endpoint (low latency). The production agent sandbox has
higher per-request connection latency, so the connection-reuse gain — and the reliability
fix — is larger there than the local figures show. The ~1827&times; compound is a model of
the three measured factors (4.5&times; reuse &times; ~2&times; concurrency &times; ~23&times; fewer reads),
not a single end-to-end timing. Stdlib-only; no new dependencies.</p>
<p><b>Artifacts.</b> echo-memory v1.2.0 &mdash; <code>echo-memory-1.2.0.plugin</code>.
33 automated tests pass; vault-health linter clean of regressions.</p>
</div>
</div>
</body>
</html>