profit ac01fffd9a checkpoint: matrix-agent-validated (2026-04-25)
Architectural snapshot of the lakehouse codebase at the point where the
full matrix-driven agent loop with Mem0 versioning + deletion was
validated end-to-end.

WHAT THIS REPO IS
A clean single-commit snapshot of the lakehouse code. Heavy test data
(.parquet datasets, vector indexes) excluded — see REPLICATION.md for
regen path. Full lakehouse history at git.agentview.dev/profit/lakehouse.

WHAT WAS PROVEN
- Vector retrieval across multi-corpora matrix (chicago_permits + entity
  briefs + sec_tickers + distilled procedural + llm_team runs)
- Observer hand-review (cloud + heuristic fallback) gating each candidate
- Local-model agent loop (qwen3.5:latest) with tool use + scratchpad
- Playbook seal on success → next-iter retrieval surfaces it as preamble
- Mem0 versioning + deletion in pathway_memory:
    * UPSERT: ADD on new workflow, UPDATE bumps replay_count on identical
    * REVISE: chains versions, parent.superseded_at + superseded_by stamped
    * RETIRE: marks specific trace retired with reason, excluded from retrieval
    * HISTORY: walks chain root→tip, cycle-safe

KEY DIRECTORIES
- crates/vectord/src/pathway_memory.rs — Mem0 ops live here
- crates/vectord/src/playbook_memory.rs — original Mem0 reference
- tests/agent_test/ — local-model agent harness + PRD + session archives
- scripts/dump_raw_corpus.sh — MinIO bucket dump (raw test corpus)
- scripts/vectorize_raw_corpus.ts — corpus → vector indexes
- scripts/analyze_chicago_contracts.ts — real inference pipeline
- scripts/seal_agent_playbook.ts — Mem0 upsert from agent traces

Replication: see REPLICATION.md for Debian 13 clean install + cloud-only
adaptation (no local Ollama).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 19:43:27 -05:00

370 lines
22 KiB
Python

#!/usr/bin/env python3
"""Pipeline Lab server — serves notebook UI on :3500, proxies API to sidecar :3200."""
import http.server
import json
import urllib.request
import urllib.error
PORT = 3500
SIDECAR = "http://localhost:3200"
class LabHandler(http.server.BaseHTTPRequestHandler):
def log_message(self, fmt, *args):
pass # quiet
def do_GET(self):
if self.path == "/" or self.path == "":
self._serve_ui()
elif self.path.startswith("/lab/"):
self._proxy("GET")
else:
self.send_error(404)
def do_POST(self):
if self.path.startswith("/lab/"):
self._proxy("POST")
else:
self.send_error(404)
def do_DELETE(self):
if self.path.startswith("/lab/"):
self._proxy("DELETE")
else:
self.send_error(404)
def _proxy(self, method):
"""Proxy request to sidecar."""
url = SIDECAR + self.path
body = None
if method in ("POST", "PUT"):
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length) if length else None
req = urllib.request.Request(url, data=body, method=method)
req.add_header("Content-Type", self.headers.get("Content-Type", "application/json"))
try:
with urllib.request.urlopen(req, timeout=120) as resp:
data = resp.read()
self.send_response(resp.status)
self.send_header("Content-Type", resp.headers.get("Content-Type", "application/json"))
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(data)
except urllib.error.HTTPError as e:
self.send_response(e.code)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(e.read())
except Exception as e:
self.send_response(502)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({"error": str(e)}).encode())
def _serve_ui(self):
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(HTML.encode())
HTML = r"""<!DOCTYPE html>
<html lang="en"><head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Pipeline Lab &#x2014; Lakehouse</title>
<style>
:root{--bg:#08090c;--surface:rgba(14,16,22,0.9);--border:#2a2d35;--text:#e8e6e3;--text2:#7a7872;--accent:#4ade80;--gold:#e2b55a;--red:#e05252;--blue:#5b9cf5;--purple:#c084fc}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'SF Mono','Menlo','Consolas',monospace;background:var(--bg);color:var(--text);min-height:100vh;padding:20px 28px;font-size:13px}
h1{font-size:18px;font-weight:700;margin-bottom:4px}h1 span{color:var(--accent)}
.subtitle{color:var(--text2);font-size:11px;margin-bottom:20px}
.nav{display:flex;gap:8px;margin-bottom:16px;font-size:10px}
.nav a{color:var(--text2);text-decoration:none;padding:4px 10px;border:1px solid var(--border);border-radius:3px}
.nav a:hover{border-color:var(--accent);color:var(--accent)}
.cells{display:flex;flex-direction:column;gap:12px;max-width:1100px}
.cell{background:var(--surface);border:1px solid var(--border);border-radius:4px;overflow:hidden}
.cell.running{border-color:var(--gold);animation:pulse 1.5s infinite}
@keyframes pulse{0%,100%{border-color:var(--gold)}50%{border-color:var(--border)}}
.cell-header{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border);font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--text2)}
.cell-type{font-weight:700}
.cell-time{margin-left:auto}
.cell-input{padding:12px;background:rgba(0,0,0,0.3)}
.cell-input textarea{width:100%;min-height:60px;background:transparent;border:none;color:var(--text);font-family:inherit;font-size:13px;resize:vertical;outline:none;line-height:1.6}
.cell-output{padding:12px;font-size:12px;line-height:1.6;white-space:pre-wrap;max-height:500px;overflow-y:auto;display:none}
.cell-output.has-data{display:block;border-top:1px solid var(--border)}
.toolbar{display:flex;gap:6px;padding:8px 12px;border-top:1px solid var(--border);flex-wrap:wrap}
.btn{font-family:inherit;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;padding:5px 12px;border:1px solid var(--border);border-radius:3px;background:transparent;color:var(--text2);cursor:pointer;transition:all 0.15s}
.btn:hover{border-color:var(--accent);color:var(--accent)}
.btn.primary{border-color:var(--accent);color:var(--accent);background:rgba(74,222,128,0.06)}
.btn.gold{border-color:var(--gold);color:var(--gold)}
.btn.blue{border-color:var(--blue);color:var(--blue)}
.btn.purple{border-color:var(--purple);color:var(--purple)}
.btn.red{border-color:var(--red);color:var(--red)}
.top-bar{display:flex;gap:8px;margin-bottom:16px;align-items:center;flex-wrap:wrap}
.status-bar{display:flex;gap:12px;padding:8px 12px;background:var(--surface);border:1px solid var(--border);border-radius:4px;margin-bottom:16px;font-size:10px;color:var(--text2)}
.stat{display:flex;align-items:center;gap:4px}.stat b{color:var(--text)}
.result-row{display:flex;gap:8px;padding:6px 8px;border-bottom:1px solid rgba(42,45,53,0.3);align-items:center;font-size:11px}
.result-row:last-child{border-bottom:none}
.score-bar{width:60px;height:5px;background:rgba(0,0,0,0.2);border-radius:3px;overflow:hidden}
.score-fill{height:100%;border-radius:3px}
.threshold-slider{display:flex;align-items:center;gap:8px;padding:0 12px;margin:4px 0}
.threshold-slider input[type=range]{flex:1;accent-color:var(--accent)}
.threshold-slider .val{font-weight:700;min-width:36px;text-align:right}
</style></head><body>
<h1><span>Pipeline Lab</span> // Lakehouse</h1>
<div class="subtitle">Embedding-based screening vs LLM classification &#x2014; iterative experimentation</div>
<div class="nav">
<a href="http://192.168.1.177:3100">Gateway :3100</a>
<a href="http://192.168.1.177:3300">UI :3300</a>
<a href="http://192.168.1.177:3400">Observer :3400</a>
</div>
<div class="status-bar" id="status-bar">
<div class="stat"><span>Exemplars:</span> <b id="st-exemplars">0</b></div>
<div class="stat"><span>Categories:</span> <b id="st-categories">0</b></div>
<div class="stat"><span>Pipelines:</span> <b id="st-pipelines">0</b></div>
<div class="stat" style="margin-left:auto"><span>Sidecar:</span> <b id="st-health" style="color:var(--text2)">...</b></div>
</div>
<div class="top-bar">
<button class="btn primary" onclick="addCell('exemplars')">+ Exemplars</button>
<button class="btn gold" onclick="addCell('screen')">+ Screen</button>
<button class="btn blue" onclick="addCell('classify')">+ Classify</button>
<button class="btn purple" onclick="addCell('benchmark')">+ Benchmark</button>
<button class="btn" onclick="addCell('similarity')">+ Similarity</button>
<button class="btn" onclick="addCell('generate')">+ Generate</button>
<button class="btn" onclick="addCell('pipeline')">+ Pipeline</button>
<span style="flex:1"></span>
<button class="btn red" onclick="clearCells()">Clear All</button>
</div>
<div class="cells" id="cells"></div>
<script>
var cellCounter = 0;
function esc(t){var d=document.createElement('span');d.textContent=String(t);return d.innerHTML}
async function api(path, body) {
var opts = body ? {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body)} : {};
var r = await fetch('/lab' + path, opts);
if (!r.ok) { var e = await r.json().catch(function(){return {error:'request failed'}}); throw new Error(e.error || r.statusText); }
return r.json();
}
async function refreshStatus() {
try {
var ex = await api('/exemplars');
var pl = await api('/pipelines');
document.getElementById('st-exemplars').textContent = ex.total || 0;
document.getElementById('st-categories').textContent = Object.keys(ex.categories || {}).length;
document.getElementById('st-pipelines').textContent = (pl.pipelines || []).length;
document.getElementById('st-health').textContent = 'connected';
document.getElementById('st-health').style.color = 'var(--accent)';
} catch(e) {
document.getElementById('st-health').textContent = 'error: ' + e.message;
document.getElementById('st-health').style.color = 'var(--red)';
}
}
var placeholders = {
exemplars:'Category: decision\n---\nWe decided to use Parquet for all storage\nThe team chose React over Vue\nArchitecture decision: microservices',
screen:'We decided to migrate to PostgreSQL\nThe weather is nice today\nArchitecture: chose event sourcing over CRUD\nLunch was great',
classify:'We decided to migrate to PostgreSQL\nThe weather is nice today\nArchitecture: chose event sourcing',
benchmark:'We decided to use Kubernetes for orchestration\nThe new hire starts Monday\nTechnical debt: refactor the auth module\nLunch menu looks good today',
similarity:'We chose React for the frontend\nReact was selected as our UI framework\nThe database uses PostgreSQL',
generate:'Enter a prompt for the LLM...',
pipeline:'Pipeline name: my-extraction\n---\nscreen | threshold=0.6\nclassify\nextract | prompt=Extract the key decision and rationale\nvalidate | dedup_threshold=0.9'
};
var colors = {exemplars:'var(--accent)',screen:'var(--gold)',classify:'var(--blue)',benchmark:'var(--purple)',similarity:'var(--text2)',generate:'var(--text2)',pipeline:'var(--accent)'};
var labels = {exemplars:'EXEMPLARS',screen:'SCREEN',classify:'CLASSIFY (LLM)',benchmark:'BENCHMARK A/B',similarity:'SIMILARITY',generate:'GENERATE',pipeline:'PIPELINE'};
function addCell(type) {
var id = 'cell-' + (++cellCounter);
var cells = document.getElementById('cells');
var cell = document.createElement('div'); cell.className = 'cell'; cell.id = id;
var header = document.createElement('div'); header.className = 'cell-header';
var typeSpan = document.createElement('span'); typeSpan.className = 'cell-type'; typeSpan.style.color = colors[type]||'var(--text2)'; typeSpan.textContent = labels[type]||type; header.appendChild(typeSpan);
var numSpan = document.createElement('span'); numSpan.textContent = 'Cell #' + cellCounter; header.appendChild(numSpan);
var timeSpan = document.createElement('span'); timeSpan.className = 'cell-time'; timeSpan.id = id+'-time'; header.appendChild(timeSpan);
cell.appendChild(header);
var inputDiv = document.createElement('div'); inputDiv.className = 'cell-input';
var ta = document.createElement('textarea'); ta.id = id+'-input'; ta.placeholder = placeholders[type]||''; ta.value = placeholders[type]||'';
inputDiv.appendChild(ta); cell.appendChild(inputDiv);
if (type === 'screen' || type === 'benchmark') {
var sl = document.createElement('div'); sl.className = 'threshold-slider';
var slLbl = document.createElement('span'); slLbl.style.cssText='font-size:10px;color:var(--text2)'; slLbl.textContent='Threshold:'; sl.appendChild(slLbl);
var range = document.createElement('input'); range.type='range'; range.min='0.3'; range.max='0.95'; range.step='0.05'; range.value='0.65'; range.id=id+'-threshold';
var valSpan = document.createElement('span'); valSpan.className='val'; valSpan.textContent='0.65';
range.oninput=function(){valSpan.textContent=this.value};
sl.appendChild(range); sl.appendChild(valSpan); cell.appendChild(sl);
}
var outputDiv = document.createElement('div'); outputDiv.className='cell-output'; outputDiv.id=id+'-output'; cell.appendChild(outputDiv);
var tb = document.createElement('div'); tb.className='toolbar';
var runBtn = document.createElement('button'); runBtn.className='btn primary'; runBtn.textContent='Run'; runBtn.onclick=function(){runCell(id,type)}; tb.appendChild(runBtn);
var rmBtn = document.createElement('button'); rmBtn.className='btn red'; rmBtn.textContent='Remove'; rmBtn.onclick=function(){cell.remove()}; tb.appendChild(rmBtn);
cell.appendChild(tb);
cells.appendChild(cell); ta.focus();
}
function clearCells(){document.getElementById('cells').textContent='';cellCounter=0}
function parseLines(text){return text.split('\n').map(function(l){return l.trim()}).filter(function(l){return l&&l.charAt(0)!=='#'})}
async function runCell(id, type) {
var cell = document.getElementById(id);
var input = document.getElementById(id+'-input').value;
var output = document.getElementById(id+'-output');
var timeEl = document.getElementById(id+'-time');
cell.classList.add('running');
output.className='cell-output has-data'; output.textContent='Running...'; output.style.color='';
try {
var t0=performance.now(), result;
if (type==='exemplars') {
var parts=input.split('---'); var catLine=(parts[0]||'').trim();
var category=catLine.replace(/^category:\s*/i,'').trim().toLowerCase();
var texts=parseLines(parts.slice(1).join('\n'));
if(!category||!texts.length){output.textContent='Format: Category: name\\n---\\ntext1\\ntext2';return}
result=await api('/exemplars',{category:category,texts:texts});
output.textContent='Added '+result.added+' exemplars to "'+result.category+'" (total: '+result.total+')';
output.style.color='var(--accent)'; refreshStatus();
}
else if (type==='screen') {
var texts=parseLines(input);
var threshold=parseFloat((document.getElementById(id+'-threshold')||{}).value||'0.65');
result=await api('/screen',{texts:texts,threshold:threshold});
renderScreen(output,result,threshold);
}
else if (type==='classify') {
var texts=parseLines(input); result=await api('/classify',{texts:texts});
renderClassify(output,result);
}
else if (type==='benchmark') {
var texts=parseLines(input);
var threshold=parseFloat((document.getElementById(id+'-threshold')||{}).value||'0.65');
result=await api('/benchmark',{texts:texts,threshold:threshold});
renderBenchmark(output,result);
}
else if (type==='similarity') {
var texts=parseLines(input); result=await api('/cell',{action:'similarity',texts:texts});
renderMatrix(output,result);
}
else if (type==='generate') {
result=await api('/cell',{action:'generate',text:input});
output.textContent=result.text||'(empty)';
}
else if (type==='pipeline') {
var parts=input.split('---'); var pName=(parts[0]||'').trim().replace(/^pipeline\s*name:\s*/i,'').trim();
var stageLines=parseLines(parts.slice(1).join('\n'));
var stages=stageLines.map(function(line){
var ps=line.split('|').map(function(s){return s.trim()});var config={};
ps.slice(1).forEach(function(p){var kv=p.split('=');if(kv.length===2){var v=kv[1].trim();config[kv[0].trim()]=isNaN(parseFloat(v))?v:parseFloat(v)}});
return{name:ps[0],mode:ps[0],config:config};
});
await api('/pipelines',{name:pName,stages:stages,description:'Pipeline Lab'});
output.textContent='Pipeline "'+pName+'" saved ('+stages.length+' stages)';
output.style.color='var(--accent)'; refreshStatus();
}
timeEl.textContent=Math.round(performance.now()-t0)+'ms'+(result&&result.time_ms?' (server: '+result.time_ms+'ms)':'');
} catch(e){output.textContent='Error: '+e.message;output.style.color='var(--red)'} finally{cell.classList.remove('running')}
}
function renderScreen(el,results,threshold){
el.textContent='';
results.forEach(function(r){
var row=document.createElement('div');row.className='result-row';
var cat=document.createElement('span');cat.style.cssText='min-width:90px;font-weight:700;color:'+(r.above_threshold?'var(--accent)':'var(--text2)');cat.textContent=r.best_category||'none';row.appendChild(cat);
var sim=document.createElement('span');sim.style.cssText='min-width:50px;font-weight:700;color:'+(r.similarity>=0.7?'var(--accent)':r.similarity>=threshold?'var(--gold)':'var(--text2)');sim.textContent=(r.similarity*100).toFixed(1)+'%';row.appendChild(sim);
var bar=document.createElement('div');bar.className='score-bar';var fill=document.createElement('div');fill.className='score-fill';fill.style.width=(r.similarity*100)+'%';fill.style.background=r.similarity>=0.7?'var(--accent)':r.similarity>=threshold?'var(--gold)':'var(--red)';bar.appendChild(fill);row.appendChild(bar);
var txt=document.createElement('span');txt.style.cssText='flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';txt.textContent=r.text;row.appendChild(txt);
var badge=document.createElement('span');badge.style.cssText='font-size:9px;padding:2px 6px;border-radius:2px;border:1px solid;'+(r.above_threshold?'color:var(--accent);border-color:var(--accent)':'color:var(--text2);border-color:var(--border)');badge.textContent=r.above_threshold?'PASS':'FILTERED';row.appendChild(badge);
el.appendChild(row);
});
}
function renderClassify(el,results){
el.textContent='';
results.forEach(function(r){
var row=document.createElement('div');row.className='result-row';
var cat=document.createElement('span');cat.style.cssText='min-width:90px;font-weight:700;color:var(--blue)';cat.textContent=r.category;row.appendChild(cat);
var conf=document.createElement('span');conf.style.cssText='min-width:50px;font-size:10px;color:'+(r.confidence==='high'?'var(--accent)':r.confidence==='medium'?'var(--gold)':'var(--text2)');conf.textContent=r.confidence;row.appendChild(conf);
var txt=document.createElement('span');txt.style.flex='1';txt.textContent=r.text;row.appendChild(txt);
el.appendChild(row);
});
}
function renderBenchmark(el,result){
el.textContent='';
var summary=document.createElement('div');summary.style.cssText='display:flex;gap:12px;margin-bottom:12px;flex-wrap:wrap';
[['Agreement',(result.agreement_rate*100).toFixed(1)+'%',result.agreement_rate>=0.8?'var(--accent)':'var(--gold)'],
['Speedup',result.speedup+'x',result.speedup>=2?'var(--accent)':'var(--text)'],
['Embed',result.embed_time_ms+'ms','var(--gold)'],
['LLM',result.llm_time_ms+'ms','var(--blue)'],
['Hybrid',result.hybrid_estimated_ms+'ms','var(--accent)'],
['Screened',result.texts_screened_out+'/'+result.total_texts,'var(--purple)']
].forEach(function(s){
var box=document.createElement('div');box.style.cssText='background:rgba(0,0,0,0.2);padding:8px 12px;border-radius:3px;text-align:center';
var lbl=document.createElement('div');lbl.style.cssText='font-size:9px;color:var(--text2);text-transform:uppercase;letter-spacing:0.5px';lbl.textContent=s[0];box.appendChild(lbl);
var val=document.createElement('div');val.style.cssText='font-size:18px;font-weight:700;color:'+s[2];val.textContent=s[1];box.appendChild(val);
summary.appendChild(box);
});
el.appendChild(summary);
// Side by side
var grid=document.createElement('div');grid.style.cssText='display:grid;grid-template-columns:1fr 1fr;gap:12px';
[[result.embed_results||[],'EMBEDDING ('+result.embed_time_ms+'ms)','var(--gold)',true],
[result.llm_results||[],'LLM ('+result.llm_time_ms+'ms)','var(--blue)',false]
].forEach(function(col){
var div=document.createElement('div');div.style.cssText='background:rgba(0,0,0,0.2);border-radius:3px;padding:10px';
var title=document.createElement('div');title.style.cssText='font-size:10px;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px;font-weight:700;color:'+col[2];title.textContent=col[1];div.appendChild(title);
col[0].forEach(function(r){
var row=document.createElement('div');row.style.cssText='font-size:11px;padding:3px 0;display:flex;gap:6px;align-items:center';
var c=document.createElement('span');c.style.cssText='min-width:70px;font-weight:700;color:'+(col[3]?(r.above_threshold?'var(--accent)':'var(--text2)'):'var(--blue)');c.textContent=(col[3]?r.best_category:r.category)||'none';row.appendChild(c);
var s=document.createElement('span');s.style.cssText='min-width:40px;color:var(--text2)';s.textContent=col[3]?(r.similarity*100).toFixed(0)+'%':r.confidence;row.appendChild(s);
var t=document.createElement('span');t.style.cssText='flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';t.textContent=r.text;row.appendChild(t);
div.appendChild(row);
});
grid.appendChild(div);
});
el.appendChild(grid);
}
function renderMatrix(el,result){
el.textContent='';
var matrix=result.matrix||[],texts=result.texts||[];
if(!matrix.length){el.textContent='No results';return}
var tbl=document.createElement('table');tbl.style.cssText='border-collapse:collapse;font-size:11px;width:100%';
var hdr=document.createElement('tr');var corner=document.createElement('th');hdr.appendChild(corner);
texts.forEach(function(t){var th=document.createElement('th');th.style.cssText='padding:4px;color:var(--text2);font-size:9px;max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';th.textContent=t.substring(0,20);th.title=t;hdr.appendChild(th)});
tbl.appendChild(hdr);
matrix.forEach(function(row,i){
var tr=document.createElement('tr');
var td0=document.createElement('td');td0.style.cssText='padding:4px;color:var(--text2);font-size:9px';td0.textContent=texts[i].substring(0,20);tr.appendChild(td0);
row.forEach(function(v,j){
var td=document.createElement('td');
td.style.cssText='padding:4px;text-align:center;font-weight:'+(v>=0.7?'700':'400')+';color:'+(v>=0.8?'var(--accent)':v>=0.6?'var(--gold)':'var(--text2)')+';background:'+(i===j?'rgba(74,222,128,0.1)':v>=0.8?'rgba(74,222,128,0.15)':v>=0.6?'rgba(226,181,90,0.1)':'transparent');
td.textContent=v.toFixed(2);tr.appendChild(td);
});
tbl.appendChild(tr);
});
el.appendChild(tbl);
}
refreshStatus();
</script>
</body></html>"""
if __name__ == "__main__":
print(f"Pipeline Lab running on http://0.0.0.0:{PORT}")
server = http.server.HTTPServer(("0.0.0.0", PORT), LabHandler)
server.serve_forever()