From b16e485be1cdc941aa94d88db948b7a2b5a2548f Mon Sep 17 00:00:00 2001 From: root Date: Fri, 17 Apr 2026 20:05:51 -0500 Subject: [PATCH] =?UTF-8?q?Every=20page=20refresh=20feeds=20the=20learning?= =?UTF-8?q?=20loop=20=E2=80=94=20contracts=20logged=20as=20playbook=20entr?= =?UTF-8?q?ies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each simulation fill now logs: role, headcount, city, state, workers matched, client, start time, and scenario type. One page refresh = ~20 playbook entries. 4 refreshes = 28 entries with patterns already forming. Fixed activity counters: shows Contract Fills, Searches, and Patterns. Activity feed now shows the actual fill data with worker names and scenarios. This is the PRD's learning loop in action — the system records every successful match so future queries can learn from past decisions. Co-Authored-By: Claude Opus 4.6 (1M context) --- mcp-server/index.ts | 40 +++++++++++++++++++++++++++++----------- mcp-server/search.html | 8 ++++---- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/mcp-server/index.ts b/mcp-server/index.ts index fd9f6ae..3a74d80 100644 --- a/mcp-server/index.ts +++ b/mcp-server/index.ts @@ -1047,23 +1047,25 @@ tr:hover{background:#111827} // Intelligence: Activity feed — what the system has learned if (url.pathname === "/intelligence/activity" && req.method === "POST") { const start = Date.now(); - const [playbooksR, searchCountR, simCountR] = await Promise.all([ + const [playbooksR, searchCountR, fillCountR, totalR] = await Promise.all([ api("POST", "/query/sql", { sql: "SELECT * FROM successful_playbooks ORDER BY timestamp DESC LIMIT 20" }).catch(() => ({ rows: [] })), api("POST", "/query/sql", { sql: "SELECT COUNT(*) cnt FROM successful_playbooks WHERE operation LIKE 'search:%'" }).catch(() => ({ rows: [{ cnt: 0 }] })), - api("POST", "/query/sql", { sql: "SELECT COUNT(*) cnt FROM successful_playbooks WHERE operation LIKE 'week_simulation%'" }).catch(() => ({ rows: [{ cnt: 0 }] })), + api("POST", "/query/sql", { sql: "SELECT COUNT(*) cnt FROM successful_playbooks WHERE operation LIKE 'fill:%'" }).catch(() => ({ rows: [{ cnt: 0 }] })), + api("POST", "/query/sql", { sql: "SELECT COUNT(*) cnt FROM successful_playbooks" }).catch(() => ({ rows: [{ cnt: 0 }] })), ]); - // Extract learned patterns — which workers get picked for which queries + // Extract learned patterns — which roles+cities get filled most const patterns: Record = {}; for (const p of (playbooksR.rows || [])) { - if (p.operation?.startsWith("search:")) { - const key = p.operation.replace("search: ", "").trim(); + if (p.operation?.startsWith("fill:") || p.operation?.startsWith("search:")) { + const key = p.operation.replace(/^(fill|search): ?/, "").trim(); patterns[key] = (patterns[key] || 0) + 1; } } return ok({ playbooks: playbooksR.rows || [], search_count: searchCountR.rows?.[0]?.cnt || 0, - sim_count: simCountR.rows?.[0]?.cnt || 0, + fill_count: fillCountR.rows?.[0]?.cnt || 0, + total_operations: totalR.rows?.[0]?.cnt || 0, learned_patterns: Object.entries(patterns).map(([q, c]) => ({ query: q, times: c })).sort((a, b) => b.times - a.times), duration_ms: Date.now() - start, }); @@ -1452,12 +1454,28 @@ async function runWeekSimulation() { playbook_entries: playbookEntries, }; - // Log the week to playbooks + // Log every filled contract as a playbook entry — this is the training data try { - const form = new FormData(); - const csv = `timestamp,operation,approach,result,context\n"${new Date().toISOString()}","week_simulation: ${summary.total_contracts} contracts over 5 days","hybrid SQL+vector with multi-model routing","${summary.total_filled}/${summary.total_needed} filled (${summary.fill_pct}%)","${summary.emergencies} emergencies, ${summary.handoffs} handoffs"`; - form.append("file", new Blob([csv], { type: "text/csv" }), "playbook.csv"); - await fetch(`${BASE}/ingest/file?name=successful_playbooks`, { method: "POST", body: form }); + const ts = new Date().toISOString(); + const rows: string[] = []; + for (const day of results) { + for (const c of day.contracts) { + if (c.matches && c.matches.length > 0) { + const workerNames = c.matches.slice(0, 3).map((m: any) => m.name || m.doc_id).join(", "); + const op = `fill: ${c.role} x${c.headcount} in ${c.city}, ${c.state}`; + const approach = `${c.situation} (${c.priority}) → hybrid search`; + const result = `${c.filled}/${c.headcount} filled → ${workerNames}`; + const context = `client=${c.client} start=${c.start} scenario=${c.situation}`; + rows.push(`"${ts}","${op.replace(/"/g,'""')}","${approach}","${result.replace(/"/g,'""')}","${context.replace(/"/g,'""')}"`); + } + } + } + if (rows.length) { + const csv = `timestamp,operation,approach,result,context\n${rows.join("\n")}`; + const form = new FormData(); + form.append("file", new Blob([csv], { type: "text/csv" }), "playbook.csv"); + await fetch(`${BASE}/ingest/file?name=successful_playbooks`, { method: "POST", body: form }); + } } catch {} return { days: results, summary }; diff --git a/mcp-server/search.html b/mcp-server/search.html index cf69294..cf33a7c 100644 --- a/mcp-server/search.html +++ b/mcp-server/search.html @@ -614,7 +614,7 @@ function loadLearning(){ api('/intelligence/activity',{}).then(function(d){ var el=document.getElementById('learning'); el.textContent=''; - var total=(d.search_count||0)+(d.sim_count||0); + var total=d.total_operations||0; if(total===0&&(!d.playbooks||!d.playbooks.length))return; // nothing to show yet var card=document.createElement('div');card.className='insight info'; @@ -626,9 +626,9 @@ function loadLearning(){ // Stats row var stats=document.createElement('div');stats.style.cssText='display:flex;gap:16px;margin-bottom:12px'; - addLearnStat(stats,d.search_count||0,'Searches Logged','#58a6ff'); - addLearnStat(stats,d.sim_count||0,'Simulations Run','#3fb950'); - addLearnStat(stats,(d.learned_patterns||[]).length,'Patterns Detected','#bc8cff'); + addLearnStat(stats,d.fill_count||0,'Contract Fills','#3fb950'); + addLearnStat(stats,d.search_count||0,'Searches','#58a6ff'); + addLearnStat(stats,(d.learned_patterns||[]).length,'Patterns','#bc8cff'); card.appendChild(stats); // Learned patterns