diff --git a/mcp-server/dashboard.css b/mcp-server/dashboard.css
new file mode 100644
index 0000000..0842307
--- /dev/null
+++ b/mcp-server/dashboard.css
@@ -0,0 +1,22 @@
+* { margin: 0; padding: 0; box-sizing: border-box; }
+body { font-family: 'SF Mono', 'Fira Code', monospace; background: #0a0a0f; color: #e0e0e0; }
+.header { background: linear-gradient(135deg, #1a1a2e, #16213e); padding: 20px 30px; border-bottom: 2px solid #0f3460; display: flex; justify-content: space-between; align-items: center; }
+.header h1 { font-size: 20px; color: #e94560; }
+.header .status { display: flex; gap: 15px; font-size: 12px; align-items: center; }
+.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 4px; }
+.dot.green { background: #00ff88; } .dot.red { background: #ff4444; }
+.grid { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; padding: 16px; max-width: 1400px; margin: 0 auto; }
+.card { background: #12121a; border: 1px solid #1a1a2e; border-radius: 8px; padding: 16px; }
+.card h2 { font-size: 14px; color: #e94560; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 1px; }
+.contract { border-left: 3px solid #0f3460; padding: 10px 14px; margin-bottom: 10px; background: #0d0d15; border-radius: 0 6px 6px 0; }
+.contract.urgent { border-left-color: #e94560; } .contract.high { border-left-color: #ff8c00; } .contract.filled { border-left-color: #00ff88; }
+.contract .title { font-weight: bold; font-size: 13px; } .contract .meta { font-size: 11px; color: #888; margin-top: 4px; }
+.contract .workers { font-size: 11px; color: #aaa; margin-top: 6px; } .contract .workers b { color: #00ff88; }
+.alert { padding: 8px 12px; margin-bottom: 8px; border-radius: 4px; font-size: 12px; }
+.alert.warn { background: #2a1a00; border-left: 3px solid #ffaa00; } .alert.info { background: #001a2a; border-left: 3px solid #00aaff; } .alert.good { background: #002a1a; border-left: 3px solid #00ff88; }
+.playbook { padding: 8px 12px; margin-bottom: 6px; background: #0d0d15; border-radius: 4px; font-size: 11px; } .playbook .op { color: #e94560; }
+.stat-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #1a1a2e; font-size: 13px; } .stat-row .val { color: #00ff88; font-weight: bold; }
+.day-nav { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
+.day-btn { padding: 6px 14px; background: #1a1a2e; border: 1px solid #0f3460; color: #e0e0e0; border-radius: 4px; cursor: pointer; font-size: 12px; } .day-btn.active { background: #e94560; border-color: #e94560; color: #fff; }
+.log { font-size: 11px; color: #888; max-height: 250px; overflow-y: auto; } .log div { padding: 3px 0; border-bottom: 1px solid #0d0d15; }
+.refresh-btn { background: #0f3460; border: none; color: #e0e0e0; padding: 6px 16px; border-radius: 4px; cursor: pointer; font-size: 12px; } .refresh-btn:hover { background: #e94560; }
diff --git a/mcp-server/dashboard.html b/mcp-server/dashboard.html
new file mode 100644
index 0000000..712930d
--- /dev/null
+++ b/mcp-server/dashboard.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+Lakehouse Staffing Co-Pilot
+
+
+
+
+
+
+
Contracts
Click Run Week Sim to start
+
+
+
+
+
+
+
diff --git a/mcp-server/dashboard.ts b/mcp-server/dashboard.ts
new file mode 100644
index 0000000..f9f6eca
--- /dev/null
+++ b/mcp-server/dashboard.ts
@@ -0,0 +1,201 @@
+const GW = "http://localhost:3700";
+let simData: any = null;
+let currentDay = 0;
+
+async function api(path: string, body?: any) {
+ const opts: RequestInit = body
+ ? { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }
+ : {};
+ const r = await fetch(GW + path, opts);
+ return r.json();
+}
+
+function addLog(msg: string, cls = "") {
+ const el = document.getElementById("log")!;
+ const div = document.createElement("div");
+ const t = new Date().toLocaleTimeString();
+ div.textContent = `${t} ${msg}`;
+ if (cls) div.className = cls;
+ el.prepend(div);
+ while (el.children.length > 50) el.lastChild?.remove();
+}
+
+function setText(id: string, text: string) {
+ const el = document.getElementById(id);
+ if (el) el.textContent = text;
+}
+
+function renderContracts(day: any) {
+ const el = document.getElementById("contracts")!;
+ el.replaceChildren();
+ if (!day?.contracts?.length) {
+ el.textContent = "No contracts for this day";
+ return;
+ }
+ for (const c of day.contracts) {
+ const div = document.createElement("div");
+ div.className = `contract ${c.filled >= c.headcount ? "filled" : c.priority === "urgent" ? "urgent" : c.priority === "high" ? "high" : ""}`;
+
+ const icon = c.priority === "urgent" ? "!!" : c.priority === "high" ? "!" : "";
+ const title = document.createElement("div");
+ title.className = "title";
+ title.textContent = `${icon} ${c.id} - ${c.client}`;
+ div.appendChild(title);
+
+ const meta = document.createElement("div");
+ meta.className = "meta";
+ meta.textContent = `${c.role} x${c.headcount} | ${c.city || c.state} | ${c.filled}/${c.headcount} filled | ${c.priority}`;
+ div.appendChild(meta);
+
+ if (c.matches?.length) {
+ const workers = document.createElement("div");
+ workers.className = "workers";
+ workers.textContent = c.matches.map((m: any) => `${m.name || m.doc_id} (${m.score?.toFixed(2) || "?"})`).join(", ");
+ div.appendChild(workers);
+ }
+
+ if (c.notes) {
+ const notes = document.createElement("div");
+ notes.className = "meta";
+ notes.textContent = c.notes;
+ div.appendChild(notes);
+ }
+
+ el.appendChild(div);
+ }
+}
+
+function renderDayNav() {
+ const nav = document.getElementById("day-nav")!;
+ nav.replaceChildren();
+ if (!simData?.days) return;
+ simData.days.forEach((d: any, i: number) => {
+ const btn = document.createElement("button");
+ btn.className = `day-btn ${i === currentDay ? "active" : ""}`;
+ btn.textContent = d.label;
+ btn.onclick = () => { currentDay = i; renderDayNav(); renderContracts(simData.days[i]); };
+ nav.appendChild(btn);
+ });
+}
+
+function renderWeekStats() {
+ const el = document.getElementById("week-stats")!;
+ el.replaceChildren();
+ if (!simData?.summary) return;
+ const s = simData.summary;
+ const rows = [
+ ["Total contracts", s.total_contracts],
+ ["Total positions", s.total_needed],
+ ["Filled", s.total_filled],
+ ["Fill rate", `${s.fill_pct}%`],
+ ["Emergencies", s.emergencies],
+ ["Handoffs", s.handoffs],
+ ["Playbooks", s.playbook_entries],
+ ];
+ for (const [label, val] of rows) {
+ const row = document.createElement("div");
+ row.className = "stat-row";
+ const nameSpan = document.createElement("span");
+ nameSpan.textContent = String(label);
+ const valSpan = document.createElement("span");
+ valSpan.className = "val";
+ valSpan.textContent = String(val);
+ row.appendChild(nameSpan);
+ row.appendChild(valSpan);
+ el.appendChild(row);
+ }
+}
+
+async function loadAlerts() {
+ const el = document.getElementById("alerts")!;
+ el.replaceChildren();
+ try {
+ const r = await api("/sql", { sql: "SELECT archetype, COUNT(*) cnt FROM ethereal_workers WHERE archetype IN ('erratic','silent') GROUP BY archetype" });
+ for (const row of r.rows || []) {
+ const div = document.createElement("div");
+ div.className = "alert warn";
+ div.textContent = `${row.archetype}: ${row.cnt} workers flagged`;
+ el.appendChild(div);
+ }
+ const info = document.createElement("div");
+ info.className = "alert info";
+ info.textContent = "System: all services running";
+ el.appendChild(info);
+ } catch {
+ const div = document.createElement("div");
+ div.className = "alert warn";
+ div.textContent = "Could not load alerts";
+ el.appendChild(div);
+ }
+}
+
+async function loadPlaybooks() {
+ const el = document.getElementById("playbooks")!;
+ el.replaceChildren();
+ try {
+ const r = await api("/playbooks", {});
+ const pbs = r.playbooks || [];
+ if (pbs.length === 0) {
+ el.textContent = "No playbooks yet - run the simulation";
+ return;
+ }
+ for (const p of pbs.slice(0, 8)) {
+ const div = document.createElement("div");
+ div.className = "playbook";
+ const op = document.createElement("span");
+ op.className = "op";
+ op.textContent = (p.operation || "").slice(0, 50);
+ div.appendChild(op);
+ div.appendChild(document.createElement("br"));
+ div.appendChild(document.createTextNode((p.result || "").slice(0, 80)));
+ el.appendChild(div);
+ }
+ } catch {
+ el.textContent = "Could not load playbooks";
+ }
+}
+
+async function checkServices() {
+ try {
+ await fetch(GW + "/health");
+ document.getElementById("svc-gw")!.className = "dot green";
+ } catch {
+ document.getElementById("svc-gw")!.className = "dot red";
+ }
+ try {
+ const r = await api("/vram");
+ setText("vram-display", `VRAM: ${r.gpu?.used_mib || "?"}/${r.gpu?.total_mib || "?"} MiB`);
+ } catch {}
+}
+
+// Week simulation runner
+(window as any).runWeek = async function () {
+ addLog("Starting week simulation...");
+ try {
+ const r = await fetch(GW + "/simulation/run", { method: "POST" });
+ simData = await r.json();
+ if (simData.error) {
+ addLog(`Error: ${simData.error}`, "fail");
+ return;
+ }
+ renderDayNav();
+ renderContracts(simData.days[0]);
+ renderWeekStats();
+ addLog(`Week complete: ${simData.summary?.total_filled}/${simData.summary?.total_needed} filled`, "ok");
+ } catch (e) {
+ addLog(`Simulation failed: ${e}`, "fail");
+ }
+};
+
+(window as any).refresh = async function () {
+ addLog("Refreshing...");
+ await checkServices();
+ await loadAlerts();
+ await loadPlaybooks();
+};
+
+// Initial load
+checkServices();
+loadAlerts();
+loadPlaybooks();
+setInterval(checkServices, 30000);
diff --git a/mcp-server/index.ts b/mcp-server/index.ts
index a572356..c596414 100644
--- a/mcp-server/index.ts
+++ b/mcp-server/index.ts
@@ -412,7 +412,26 @@ async function main() {
return new Response(await r.text(), { status: r.status, headers: { "Content-Type": "application/json" } });
}
- return err("Unknown path. Available: /health /search /sql /match /worker/:id /ask /log /playbooks /profile/:id /vram /api/*", 404);
+ // Dashboard UI
+ if (url.pathname === "/" || url.pathname === "/dashboard") {
+ return new Response(Bun.file(import.meta.dir + "/dashboard.html"));
+ }
+ if (url.pathname === "/dashboard.css") {
+ return new Response(Bun.file(import.meta.dir + "/dashboard.css"), { headers: { "Content-Type": "text/css" } });
+ }
+ if (url.pathname === "/dashboard.ts" || url.pathname === "/dashboard.js") {
+ // Bun transpiles TS on the fly
+ const built = await Bun.build({ entrypoints: [import.meta.dir + "/dashboard.ts"], target: "browser" });
+ const js = await built.outputs[0].text();
+ return new Response(js, { headers: { "Content-Type": "application/javascript" } });
+ }
+
+ // Week simulation endpoint
+ if (url.pathname === "/simulation/run" && req.method === "POST") {
+ return ok(await runWeekSimulation());
+ }
+
+ return err("Unknown path. Available: / /health /search /sql /match /worker/:id /ask /log /playbooks /profile/:id /vram /context /verify /simulation/run", 404);
} catch (e: any) {
return err(e.message || String(e), 500);
}
@@ -423,3 +442,131 @@ async function main() {
}
main().catch(console.error);
+
+// ─── Week simulation engine ───
+
+const ROLES = ["Forklift Operator","Machine Operator","Assembler","Loader","Quality Tech","Welder","Sanitation Worker","Shipping Clerk","Production Worker","Maintenance Tech"];
+const STATES = ["IL","IN","OH","MO","TN","KY","WI","MI"];
+const CITIES: Record = {
+ IL: ["Chicago","Springfield","Rockford","Peoria","Joliet"],
+ IN: ["Indianapolis","Fort Wayne","Evansville","South Bend"],
+ OH: ["Columbus","Cleveland","Cincinnati","Dayton"],
+ MO: ["St. Louis","Kansas City","Springfield"],
+ TN: ["Nashville","Memphis"], KY: ["Louisville","Lexington"],
+ WI: ["Milwaukee","Madison"], MI: ["Detroit","Grand Rapids"],
+};
+const CLIENTS = ["Midwest Logistics","Precision Mfg","Amazon DSP","CleanSpace","AutoParts Direct","Great Lakes Steel","Heartland Foods","Summit Packaging","Cardinal Health","TechFlow Assembly","River City Plastics","Prairie Wind Energy"];
+const PRIORITIES = ["urgent","high","medium","medium","medium","low"];
+const STARTS = ["5:00 AM","6:00 AM","6:30 AM","7:00 AM","7:30 AM","8:00 AM"];
+const NOTES = [
+ "Warehouse expansion — need experienced workers",
+ "Peak season surge — client called last night",
+ "2nd shift, CNC preferred",
+ "Chemical plant — hazmat cert MANDATORY",
+ "ISO audit next week — need detail-oriented workers",
+ "Structural welding — experienced only",
+ "Regular fill — ongoing contract",
+ "Client doubled their order",
+ "Night shift coverage needed",
+ "Replacing 2 no-shows from yesterday",
+];
+
+function pick(arr: T[]): T { return arr[Math.floor(Math.random() * arr.length)]; }
+
+async function runWeekSimulation() {
+ const days = ["Monday","Tuesday","Wednesday","Thursday","Friday"];
+ const staffers = ["Sarah (Lead)","Mike (Senior)","Kim (Junior)"];
+ const results: any[] = [];
+ let totalFilled = 0, totalNeeded = 0, emergencies = 0, handoffs = 0, playbookEntries = 0;
+
+ for (let d = 0; d < days.length; d++) {
+ const dayLabel = days[d];
+ const numContracts = 4 + Math.floor(Math.random() * 5); // 4-8 per day
+ const contracts: any[] = [];
+ const staffer = staffers[d % staffers.length];
+ const handoffTo = staffers[(d + 1) % staffers.length];
+
+ for (let c = 0; c < numContracts; c++) {
+ const state = pick(STATES);
+ const city = pick(CITIES[state] || [state]);
+ const role = pick(ROLES);
+ const priority = pick(PRIORITIES) as string;
+ const headcount = priority === "urgent" ? 4 + Math.floor(Math.random() * 5) :
+ priority === "high" ? 3 + Math.floor(Math.random() * 3) :
+ 2 + Math.floor(Math.random() * 3);
+ const minRel = priority === "urgent" ? 0.6 : priority === "high" ? 0.75 : 0.8;
+ const cid = `W${d+1}-${String(c+1).padStart(3,"0")}`;
+
+ if (priority === "urgent") emergencies++;
+ totalNeeded += headcount;
+
+ // Run hybrid search
+ let filled = 0;
+ let matches: any[] = [];
+ try {
+ const filt = `role = '${role}' AND state = '${state}' AND reliability >= ${minRel}`;
+ const r = await api("POST", "/vectors/hybrid", {
+ question: `Find ${role} workers for ${pick(NOTES)}`,
+ index_name: "ethereal_workers_v1",
+ sql_filter: filt,
+ filter_dataset: "ethereal_workers",
+ id_column: "worker_id",
+ top_k: headcount + 2,
+ generate: false,
+ });
+ matches = (r.sources || []).slice(0, headcount).map((s: any) => ({
+ doc_id: s.doc_id,
+ name: s.chunk_text?.split("—")[0]?.trim() || s.doc_id,
+ score: s.score,
+ }));
+ filled = matches.length;
+ } catch {}
+ totalFilled += Math.min(filled, headcount);
+
+ contracts.push({
+ id: cid, client: pick(CLIENTS), role, state, city,
+ headcount, filled: Math.min(filled, headcount), priority,
+ start: pick(STARTS), notes: pick(NOTES), matches,
+ staffer, handoff_to: d < 4 ? handoffTo : null,
+ });
+ }
+
+ // End of day: log playbook + prepare handoff
+ if (d < 4) {
+ handoffs++;
+ try {
+ await api("POST", "/api/ingest/file?name=successful_playbooks", null); // just trigger
+ } catch {}
+ }
+ playbookEntries++;
+
+ results.push({
+ label: dayLabel,
+ staffer,
+ handoff_to: d < 4 ? handoffTo : null,
+ contracts,
+ filled: contracts.reduce((s: number, c: any) => s + c.filled, 0),
+ needed: contracts.reduce((s: number, c: any) => s + c.headcount, 0),
+ });
+ }
+
+ const summary = {
+ total_contracts: results.reduce((s, d) => s + d.contracts.length, 0),
+ total_needed: totalNeeded,
+ total_filled: totalFilled,
+ fill_pct: Math.round(totalFilled / Math.max(totalNeeded, 1) * 100),
+ emergencies,
+ handoffs,
+ playbook_entries: playbookEntries,
+ };
+
+ // Log the week to playbooks
+ 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 });
+ } catch {}
+
+ return { days: results, summary };
+}