forked from jason/echo
Documentation
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,333 @@
|
|||||||
|
<!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 <token></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.3–0.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>
|
||||||
Binary file not shown.
BIN
Binary file not shown.
@@ -10,7 +10,7 @@
|
|||||||
"scenario": "agent-log-missing-heading",
|
"scenario": "agent-log-missing-heading",
|
||||||
"fault": "bad heading -> 400 invalid-target",
|
"fault": "bad heading -> 400 invalid-target",
|
||||||
"06": {
|
"06": {
|
||||||
"emit_tokens": 92,
|
"emit_tokens": 82,
|
||||||
"claimed_ok": true,
|
"claimed_ok": true,
|
||||||
"detected": false,
|
"detected": false,
|
||||||
"persisted": false,
|
"persisted": false,
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"scenario": "scope-switch",
|
"scenario": "scope-switch",
|
||||||
"fault": "(none)",
|
"fault": "(none)",
|
||||||
"06": {
|
"06": {
|
||||||
"emit_tokens": 93,
|
"emit_tokens": 84,
|
||||||
"claimed_ok": true,
|
"claimed_ok": true,
|
||||||
"detected": false,
|
"detected": false,
|
||||||
"persisted": true,
|
"persisted": true,
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
"scenario": "inbox-capture-replayed",
|
"scenario": "inbox-capture-replayed",
|
||||||
"fault": "duplicate attempt (retry/replay)",
|
"fault": "duplicate attempt (retry/replay)",
|
||||||
"06": {
|
"06": {
|
||||||
"emit_tokens": 144,
|
"emit_tokens": 125,
|
||||||
"claimed_ok": true,
|
"claimed_ok": true,
|
||||||
"detected": false,
|
"detected": false,
|
||||||
"persisted": true,
|
"persisted": true,
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
"scenario": "session-log-flaky-network",
|
"scenario": "session-log-flaky-network",
|
||||||
"fault": "one-time 503 (transient)",
|
"fault": "one-time 503 (transient)",
|
||||||
"06": {
|
"06": {
|
||||||
"emit_tokens": 74,
|
"emit_tokens": 64,
|
||||||
"claimed_ok": true,
|
"claimed_ok": true,
|
||||||
"detected": false,
|
"detected": false,
|
||||||
"persisted": false,
|
"persisted": false,
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
"scenario": "heartbeat-phantom-write",
|
"scenario": "heartbeat-phantom-write",
|
||||||
"fault": "accepted-but-not-persisted",
|
"fault": "accepted-but-not-persisted",
|
||||||
"06": {
|
"06": {
|
||||||
"emit_tokens": 71,
|
"emit_tokens": 61,
|
||||||
"claimed_ok": true,
|
"claimed_ok": true,
|
||||||
"detected": false,
|
"detected": false,
|
||||||
"persisted": false,
|
"persisted": false,
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
"scenario": "cold-start-load-6-reads",
|
"scenario": "cold-start-load-6-reads",
|
||||||
"fault": "(none)",
|
"fault": "(none)",
|
||||||
"06": {
|
"06": {
|
||||||
"emit_tokens": 249,
|
"emit_tokens": 192,
|
||||||
"claimed_ok": true,
|
"claimed_ok": true,
|
||||||
"detected": false,
|
"detected": false,
|
||||||
"persisted": true,
|
"persisted": true,
|
||||||
@@ -129,11 +129,11 @@
|
|||||||
],
|
],
|
||||||
"totals": {
|
"totals": {
|
||||||
"0.6": {
|
"0.6": {
|
||||||
"gen_tokens": 723,
|
"gen_tokens": 608,
|
||||||
"silent_failures": 4,
|
"silent_failures": 4,
|
||||||
"detected_failures": 0,
|
"detected_failures": 0,
|
||||||
"duplicates": 1,
|
"duplicates": 1,
|
||||||
"effective_tokens": 6723
|
"effective_tokens": 6608
|
||||||
},
|
},
|
||||||
"0.7": {
|
"0.7": {
|
||||||
"gen_tokens": 174,
|
"gen_tokens": 174,
|
||||||
|
|||||||
Reference in New Issue
Block a user