lakehouse/mcp-server/search.html
root 858954975b
Some checks failed
lakehouse/auditor 2 blocking issues: todo!() macro call in tests/real-world/scrum_master_pipeline.ts
Staffing Co-Pilot UI — architecture-first enrichments + shift clock
J's direction: the dashboard was explanatory but not *actionable* as
a staffing-matrix console. Refactor so the architecture claims from
docs/PRD.md surface as operational signals on every contract card.

Backend (mcp-server/index.ts):

  + GET|POST /intelligence/arch_signals — probes live substrate health
    so the dashboard shows instant-search latency, index shape,
    playbook-memory entries, and pathway-memory (ADR-021) trace count.
    Fires one fresh /vectors/hybrid probe against workers_500k_v1 so
    the "instant search" number on screen is live, not cached.

  * /intelligence/permit_contracts now times every hybrid call per
    contract and returns search_latency_ms, so the card can display
    the per-query latency pill ( 342ms).

  + Per-contract computed fields returned from the backend:
      search_latency_ms      — real /vectors/hybrid duration
      fill_probability       — base_pct (by pool_size×count ratio)
                               + curve [d0, d3, d7, d14, d21, d30]
                               with cumulative fill% per bucket
      economics              — avg_pay_rate, gross_revenue,
                               gross_margin, margin_pct,
                               payout_window_days [30, 45],
                               over_bill_count,
                               over_bill_pool_margin_at_risk
      shifts_needed          — 1st/2nd/3rd/4th inferred from
                               permit work_type + description regex

  * Pre-existing dangling-brace bug in api() fixed (the `activeTrace`
    logging block had been misplaced at module scope, referencing
    variables that only existed inside the function). Restart was
    failing with "Unexpected }" at line 76. Moved tracing inside the
    try block where parsed/path/body/ms are in scope.

Frontend (mcp-server/search.html):

  + Top "Substrate Signals" section — 4 live tiles (instant search,
    index, playbook memory, pathway matrix). Color-codes latency
    (green <100ms, amber <500ms, red otherwise).
  + "24/7 Shift Coverage" section — SVG 24-hour clock with 4 colored
    shift arcs (1st/2nd/3rd/4th), current-time needle, center label
    showing the live shift, per-shift contract count tiles beside.
    4th shift assumes weekend/split; handles 3rd-shift wrap across
    midnight by splitting into two arcs.
  + Per-card architecture pills: instant-search latency, SQL-filter
    pool-size with k=200 boost note, shift requirements.
  + Per-card fill-probability horizontal stacked bar with day
    markers (d0/d3/d7/d14/d21/d30) and per-bucket segment shading
    (green → amber → orange → red as time decays).
  + Per-card economics 4-tile grid: Est. Revenue, Est. Margin (with
    % colored by health), Payout Window (30–45d standard), Over-Bill
    Pool count + margin at risk.

Architecture smoke test (tests/architecture_smoke.ts, earlier commit)
still green: 11/11 pass including the new /intelligence/arch_signals
+ permit_contracts enrichments.

J specifically wanted: "shoot for the stars · hyperfocus · our
architecture is better because it self-regulates, uses hot-swap,
pulls from real data, and shows instant searches from clever
indexing." Every one of those is now a specific visible signal on
the page, not prose in the README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:24:11 -05:00

1505 lines
90 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Staffing Co-Pilot</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',-apple-system,system-ui,'Segoe UI',sans-serif;background:#090c10;color:#b0b8c4;font-size:14px;line-height:1.6;-webkit-font-smoothing:antialiased}
/* Top bar */
.bar{background:#0d1117;padding:0 24px;height:56px;border-bottom:1px solid #171d27;display:flex;justify-content:space-between;align-items:center}
.bar h1{font-size:14px;font-weight:600;color:#e6edf3;letter-spacing:-0.2px}
.bar .rt{font-size:11px;color:#545d68}
.bar nav{display:flex;gap:2px}
.bar nav a{font-size:12px;color:#545d68;text-decoration:none;padding:6px 14px;border-radius:6px;transition:all 0.15s}
.bar nav a:hover{color:#e6edf3;background:#161b22}
.bar nav a.active{color:#e6edf3;background:#1c2333}
/* Layout */
.content{max-width:940px;margin:0 auto;padding:24px 20px 40px}
.section{margin-bottom:32px}
.section-header{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:14px}
.section-title{font-size:11px;font-weight:600;color:#545d68;text-transform:uppercase;letter-spacing:1.2px}
.section-meta{font-size:10px;color:#3d444d}
/* Cards */
.card{background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:20px;margin-bottom:12px}
.card.accent-red{border-left:3px solid #da3633}
.card.accent-green{border-left:3px solid #2ea043}
.card.accent-amber{border-left:3px solid #bf8700}
.card.accent-blue{border-left:3px solid #388bfd}
.card .card-label{font-size:9px;font-weight:600;color:#545d68;text-transform:uppercase;letter-spacing:1.4px;margin-bottom:6px}
.card .card-title{font-size:17px;font-weight:600;color:#e6edf3;margin-bottom:3px;letter-spacing:-0.3px}
.card .card-sub{font-size:12px;color:#545d68;margin-bottom:14px;line-height:1.5}
/* Keep old class names working */
.insight{background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:20px;margin-bottom:12px}
.insight .label{font-size:9px;font-weight:600;color:#545d68;text-transform:uppercase;letter-spacing:1.4px;margin-bottom:6px}
.insight .headline{font-size:17px;font-weight:600;color:#e6edf3;margin-bottom:3px;letter-spacing:-0.3px}
.insight .sub{font-size:12px;color:#545d68;margin-bottom:14px}
.insight.urgent{border-left:3px solid #da3633}
.insight.opportunity{border-left:3px solid #2ea043}
.insight.warning{border-left:3px solid #bf8700}
.insight.info{border-left:3px solid #388bfd}
/* Workers */
.iworker{display:flex;align-items:center;gap:12px;padding:10px 12px;background:#161b22;border-radius:8px;margin-bottom:4px;transition:background 0.15s}
.iworker:hover{background:#1c2333}
.iworker .av{width:36px;height:36px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:12px;color:#e6edf3;flex-shrink:0}
.iworker .info{flex:1;min-width:0}
.iworker .nm{font-weight:600;color:#e6edf3;font-size:13px}
.iworker .detail{color:#545d68;font-size:11px}
.iworker .why{color:#388bfd;font-size:11px;margin-top:1px}
.iworker .acts{display:flex;gap:4px}
.ibtn{padding:5px 12px;border-radius:6px;font-size:10px;cursor:pointer;border:none;font-weight:600;transition:opacity 0.15s}
.ibtn:hover{opacity:0.8}
.ibtn.call{background:#1a2e4a;color:#58a6ff}
.ibtn.sms{background:#122b1e;color:#3fb950}
/* Stats */
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:24px}
.stat{background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:16px 12px;text-align:center}
.stat .n{font-size:26px;font-weight:700;color:#e6edf3;letter-spacing:-1px}
.stat .l{font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:0.8px;margin-top:4px}
/* Search */
.sa{background:#0d1117;border:1px solid #171d27;border-radius:10px;overflow:hidden}
.sa summary{cursor:pointer;color:#545d68;font-size:12px;list-style:none;padding:14px 20px;transition:color 0.15s}
.sa summary:hover{color:#b0b8c4}
.sa summary::-webkit-details-marker{display:none}
.sa .inner{padding:0 20px 20px}
.sa input[type=text]{width:100%;padding:12px 16px;background:#161b22;border:1px solid #21262d;border-radius:8px;color:#e6edf3;font-size:13px;outline:none;margin-bottom:8px;transition:border 0.15s}
.sa input:focus{border-color:#388bfd}
.srow{display:flex;gap:8px;margin-bottom:10px}
.sa select{flex:1;padding:8px 12px;background:#161b22;border:1px solid #21262d;border-radius:6px;color:#b0b8c4;font-size:12px}
.sbtn{width:100%;padding:10px;background:#1f6feb;border:none;border-radius:8px;color:#fff;font-size:13px;font-weight:600;cursor:pointer;transition:background 0.15s}
.sbtn:hover{background:#388bfd}
#sresults{margin-top:14px}
/* Footer */
.ft{text-align:center;padding:24px;color:#3d444d;font-size:11px;border-top:1px solid #171d27;margin-top:32px}
.ft a{color:#545d68;text-decoration:none;transition:color 0.15s}
.ft a:hover{color:#e6edf3}
.ld{color:#3d444d;text-align:center;padding:40px;font-size:13px}
/* Responsive */
@media(max-width:768px){
.stats{grid-template-columns:repeat(2,1fr)}
.iworker{flex-direction:column;text-align:center}
.iworker .acts{justify-content:center}
.bar{padding:0 16px;height:48px}
.bar nav{display:none}
.content{padding:16px 12px 32px}
}
@media(max-width:480px){
.stats{grid-template-columns:1fr 1fr}
.stat .n{font-size:22px}
}
</style></head><body>
<div class="bar">
<h1>Staffing Co-Pilot</h1>
<nav>
<a href="." class="active">Dashboard</a>
<a href="console">Walkthrough</a>
<a href="proof">Architecture</a>
<a href="spec">Spec</a>
<a href="onboard">Onboard</a>
<a href="alerts">Alerts</a>
<a href="workspaces">Workspaces</a>
</nav>
<div class="rt" id="status">Loading...</div>
</div>
<div class="content">
<div id="main"><div class="ld">Analyzing contracts and workers...</div></div>
<!-- Architecture signals — 4 live metrics proving the substrate claims -->
<div class="section" id="arch-signals-section">
<div class="section-header">
<span class="section-title">Substrate Signals — Architecture Live</span>
<span class="section-meta" id="arch-signals-meta">Instant search · Hot-swap-ready index · Self-regulating memory</span>
</div>
<div id="arch-signals" style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px"><div class="ld" style="grid-column:1/-1">Probing substrate…</div></div>
</div>
<!-- 24/7 Shift clock — shows active contracts per shift + current time -->
<div class="section" id="shift-clock-section">
<div class="section-header">
<span class="section-title">24/7 Shift Coverage</span>
<span class="section-meta" id="shift-clock-meta">1st 0614 · 2nd 1422 · 3rd 2206 · 4th weekend/split · now → red marker</span>
</div>
<div id="shift-clock" style="display:grid;grid-template-columns:220px 1fr;gap:20px;align-items:center"><div class="ld" style="grid-column:1/-1">Loading shift distribution…</div></div>
</div>
<div class="section" id="staffing-forecast-section">
<div class="section-header">
<span class="section-title">Staffing Forecast — Next 30 Days</span>
<span class="section-meta">Permits → predicted demand · Bench supply · Days to staffing deadline</span>
</div>
<div id="staffing-forecast"><div class="ld">Loading forecast...</div></div>
</div>
<div class="section" id="live-contracts-section">
<div class="section-header">
<span class="section-title">Live Contracts — Chicago Permits → Proposed Fills</span>
<span class="section-meta" id="live-contracts-meta">Real public permit data + worker bench + past playbook patterns</span>
</div>
<div id="live-contracts"><div class="ld">Loading live contracts...</div></div>
</div>
<div class="section" id="market-section">
<div class="section-header">
<span class="section-title">Market Intelligence</span>
<span class="section-meta">Public permit data · Updated live</span>
</div>
<div id="market"></div>
</div>
<div class="section" id="learning-section">
<div class="section-header">
<span class="section-title">System Activity</span>
<span class="section-meta">Learning from every interaction</span>
</div>
<div id="learning"></div>
</div>
<div class="section">
<div class="section-header">
<span class="section-title">Worker Search</span>
<span class="section-meta" id="worker-search-meta">Natural language search</span>
</div>
<details class="sa" open><summary>Search all workers</summary><div class="inner">
<input type="text" id="sq" placeholder="Try: reliable forklift operator available in Nashville" onkeydown="if(event.key==='Enter')doSearch()">
<div class="srow"><select id="sst"><option value="">Any State</option></select>
<select id="srl"><option value="">Any Role</option></select></div>
<button class="sbtn" onclick="doSearch()">Find Workers</button><div id="sresults"></div></div></details>
</div>
<div class="ft" id="footer">Staffing Co-Pilot · Hybrid SQL + Vector Search · loading scale… · <a href="console">Console</a> · <a href="proof">Architecture</a></div>
</div>
<script>
var P=location.pathname.indexOf('/lakehouse')>=0?'/lakehouse':'';
var A=location.origin+P;
var AC=['#1a2744','#1a3a2a','#2a1a3a','#3a2a1a','#1a3a3a','#2a2a1a'];
var lastQuery='';
window.addEventListener('load',function(){loadSystemSummary();loadArchSignals();loadDay();loadStaffingForecast();loadLiveContracts();loadMarket();loadLearning()});
function loadSystemSummary(){
api('/system/summary',{}).then(function(s){
if(!s) return;
var totalRows=(s.total_rows||0).toLocaleString();
var workers=(s.workers_500k_rows||0).toLocaleString();
var chunks=(s.total_chunks||0).toLocaleString();
var ds=s.datasets||0;
var meta1=document.getElementById('live-contracts-meta');
if(meta1) meta1.textContent='Real public permit data + '+workers+' worker bench + past playbook patterns';
var meta2=document.getElementById('worker-search-meta');
if(meta2) meta2.textContent='Natural language · '+workers+' profiles across '+ds+' datasets';
var foot=document.getElementById('footer');
if(foot){
foot.textContent='';
foot.appendChild(document.createTextNode('Staffing Co-Pilot · Hybrid SQL + Vector Search · '+totalRows+' rows across '+ds+' datasets · '+chunks+' vector chunks · '));
var a1=document.createElement('a');a1.href='console';a1.textContent='Console';foot.appendChild(a1);
foot.appendChild(document.createTextNode(' · '));
var a2=document.createElement('a');a2.href='proof';a2.textContent='Architecture';foot.appendChild(a2);
}
// Also update the collapsible search box label if not yet populated
var sum=document.querySelector('.sa summary');
if(sum&&/Search all\s*\d*\s*workers/.test(sum.textContent)){
sum.textContent='Search all '+workers+' workers';
}
}).catch(function(){/* non-fatal */});
}
// ─── Substrate signals: render the 4 architecture-health tiles ───
function loadArchSignals(){
var el=document.getElementById('arch-signals');
api('/intelligence/arch_signals',{}).then(function(s){
el.textContent='';
function tile(label, big, sub, accent){
var t=document.createElement('div');
t.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:14px 16px;border-left:3px solid '+(accent||'#58a6ff');
var l=document.createElement('div');l.style.cssText='font-size:10px;text-transform:uppercase;letter-spacing:1px;color:#545d68;margin-bottom:6px';l.textContent=label;
var b=document.createElement('div');b.style.cssText='font-size:20px;font-weight:600;color:#e6edf3;line-height:1.1';b.textContent=big;
var u=document.createElement('div');u.style.cssText='font-size:10px;color:#8b949e;margin-top:6px;line-height:1.4';u.textContent=sub;
t.appendChild(l);t.appendChild(b);t.appendChild(u);
return t;
}
var idx=s.index||{};
var pbm=s.playbook_memory||{};
var pwm=s.pathway_memory||{};
// Tile 1 — instant search (the "we cleverly indexed at ingest" claim)
var latencyColor=s.instant_search_probe_ms<100?'#2ea043':s.instant_search_probe_ms<500?'#d29922':'#f85149';
el.appendChild(tile(
'Instant Search',
(s.instant_search_probe_ms||'?')+'ms',
'Live /vectors/hybrid probe · 500K-row index · '+(idx.chunk_count||0).toLocaleString()+' chunks',
latencyColor
));
// Tile 2 — index shape (hot-swap claim)
el.appendChild(tile(
'Index',
(idx.dimensions||768)+'d · '+(idx.model||'?'),
(idx.source||'?')+' → '+(idx.name||'?')+' · '+(idx.backend||'parquet'),
'#58a6ff'
));
// Tile 3 — self-regulating memory
el.appendChild(tile(
'Playbook Memory',
(pbm.entries||0).toLocaleString()+' entries',
pbm.entries>0?'Meta-index active · boosts candidates from past fills':'Empty · POST /vectors/playbook_memory/rebuild to populate',
pbm.entries>0?'#2ea043':'#d29922'
));
// Tile 4 — ADR-021 pathway compounding
el.appendChild(tile(
'Pathway Matrix',
(pwm.total_pathways||0)+' traces',
pwm.retired+' retired · '+pwm.total_replays+' replays · ADR-021 compounding',
'#58a6ff'
));
}).catch(function(e){
el.textContent='substrate signals unavailable: '+e.message;
});
}
// ─── 24/7 Shift clock: SVG dial + per-shift contract counts ──────
function loadShiftClock(contracts){
var root=document.getElementById('shift-clock');
if(!contracts||!contracts.length){root.textContent='No live contracts to bucket by shift.';return;}
root.textContent='';
// SVG dial — 24h circle, 4 colored arcs for shifts, current-time needle
var SHIFT_COLORS={'1st':'#f9d171','2nd':'#f5894a','3rd':'#5f5fff','4th':'#2ea043'};
var SHIFT_HOURS={'1st':[6,14],'2nd':[14,22],'3rd':[22,6],'4th':null/* rotating */};
var now=new Date();
var hr=now.getHours()+now.getMinutes()/60;
var isWeekend=now.getDay()===0||now.getDay()===6;
// SVG — 200x200 clock
var R=90, CX=100, CY=100;
var svgNS='http://www.w3.org/2000/svg';
var svg=document.createElementNS(svgNS,'svg');
svg.setAttribute('viewBox','0 0 200 200');svg.setAttribute('width','200');svg.setAttribute('height','200');
svg.style.cssText='display:block';
// Outer ring
var bg=document.createElementNS(svgNS,'circle');
bg.setAttribute('cx',CX);bg.setAttribute('cy',CY);bg.setAttribute('r',R);
bg.setAttribute('fill','#0d1117');bg.setAttribute('stroke','#30363d');bg.setAttribute('stroke-width','1');
svg.appendChild(bg);
// Shift arcs — 1st/2nd/3rd as hour arcs. 4th (weekend/split) gets a dashed band.
function arcPath(startHr,endHr){
// 12 o'clock is 0h, clockwise, full circle = 24h
function pt(h){
var ang=((h/24)*2*Math.PI)-Math.PI/2;
return [CX+R*Math.cos(ang), CY+R*Math.sin(ang)];
}
var p0=pt(startHr), p1=pt(endHr);
var largeArc=(endHr-startHr+24)%24>12?1:0;
return 'M '+p0[0]+' '+p0[1]+' A '+R+' '+R+' 0 '+largeArc+' 1 '+p1[0]+' '+p1[1];
}
['1st','2nd','3rd'].forEach(function(shift){
var hrs=SHIFT_HOURS[shift];
var s,e;
if(shift==='3rd'){s=22;e=30;/* wraps to 06 */}else{s=hrs[0];e=hrs[1];}
var path=document.createElementNS(svgNS,'path');
path.setAttribute('d',arcPath(s,e%24===0?24:e%24||24));
// Handle 3rd wrap: split into two arcs
if(shift==='3rd'){
path.setAttribute('d',arcPath(22,24)+' '+arcPath(0,6));
}
path.setAttribute('fill','none');
path.setAttribute('stroke',SHIFT_COLORS[shift]);
path.setAttribute('stroke-width','10');
path.setAttribute('stroke-linecap','butt');
svg.appendChild(path);
});
// Hour tick marks at 0/6/12/18
[0,6,12,18].forEach(function(h){
var ang=((h/24)*2*Math.PI)-Math.PI/2;
var x1=CX+(R-7)*Math.cos(ang), y1=CY+(R-7)*Math.sin(ang);
var x2=CX+(R+2)*Math.cos(ang), y2=CY+(R+2)*Math.sin(ang);
var ln=document.createElementNS(svgNS,'line');
ln.setAttribute('x1',x1);ln.setAttribute('y1',y1);ln.setAttribute('x2',x2);ln.setAttribute('y2',y2);
ln.setAttribute('stroke','#8b949e');ln.setAttribute('stroke-width','1');
svg.appendChild(ln);
// Hour label
var xl=CX+(R-22)*Math.cos(ang), yl=CY+(R-22)*Math.sin(ang);
var tx=document.createElementNS(svgNS,'text');
tx.setAttribute('x',xl);tx.setAttribute('y',yl+3);
tx.setAttribute('text-anchor','middle');tx.setAttribute('fill','#8b949e');
tx.setAttribute('font-size','9');tx.setAttribute('font-family','monospace');
tx.textContent=String(h).padStart(2,'0');
svg.appendChild(tx);
});
// Current time needle
var ang=((hr/24)*2*Math.PI)-Math.PI/2;
var nx=CX+(R-3)*Math.cos(ang), ny=CY+(R-3)*Math.sin(ang);
var needle=document.createElementNS(svgNS,'line');
needle.setAttribute('x1',CX);needle.setAttribute('y1',CY);
needle.setAttribute('x2',nx);needle.setAttribute('y2',ny);
needle.setAttribute('stroke','#f85149');needle.setAttribute('stroke-width','2');
svg.appendChild(needle);
var dot=document.createElementNS(svgNS,'circle');
dot.setAttribute('cx',CX);dot.setAttribute('cy',CY);dot.setAttribute('r','3');
dot.setAttribute('fill','#f85149');
svg.appendChild(dot);
// Center label — current shift
function currentShift(){
if(isWeekend) return '4th';
if(hr>=6&&hr<14) return '1st';
if(hr>=14&&hr<22) return '2nd';
return '3rd';
}
var cs=currentShift();
var label=document.createElementNS(svgNS,'text');
label.setAttribute('x',CX);label.setAttribute('y',CY+30);
label.setAttribute('text-anchor','middle');label.setAttribute('fill',SHIFT_COLORS[cs]);
label.setAttribute('font-size','11');label.setAttribute('font-weight','600');
label.textContent=cs+' shift · '+now.toTimeString().slice(0,5);
svg.appendChild(label);
root.appendChild(svg);
// Right column — per-shift contract counts
var counts={'1st':0,'2nd':0,'3rd':0,'4th':0};
contracts.forEach(function(c){(c.shifts_needed||['1st']).forEach(function(s){if(counts[s]!==undefined)counts[s]++;});});
var right=document.createElement('div');right.style.cssText='display:grid;grid-template-columns:repeat(2,1fr);gap:10px';
['1st','2nd','3rd','4th'].forEach(function(s){
var cell=document.createElement('div');
cell.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:10px 12px;border-left:3px solid '+SHIFT_COLORS[s]+(cs===s?';box-shadow:0 0 0 1px '+SHIFT_COLORS[s]:'');
var head=document.createElement('div');head.style.cssText='font-size:10px;color:'+SHIFT_COLORS[s]+';font-weight:600;text-transform:uppercase;letter-spacing:1px;margin-bottom:4px';
head.textContent=s+' shift'+(cs===s?' · active':'');
var big=document.createElement('div');big.style.cssText='font-size:20px;font-weight:600;color:#e6edf3';big.textContent=counts[s];
var sub=document.createElement('div');sub.style.cssText='font-size:10px;color:#8b949e;margin-top:2px';
sub.textContent=(counts[s]===1?'contract needs':'contracts need')+' '+s+' coverage';
cell.appendChild(head);cell.appendChild(big);cell.appendChild(sub);
right.appendChild(cell);
});
root.appendChild(right);
}
function loadStaffingForecast(){
api('/intelligence/staffing_forecast',{}).then(function(r){
var el=document.getElementById('staffing-forecast');el.textContent='';
if(!r||!r.forecast){el.textContent='Forecast unavailable.';return}
// Header summary
var hdr=document.createElement('div');hdr.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:14px;margin-bottom:10px;display:flex;justify-content:space-between;gap:16px';
var left=document.createElement('div');
var big=document.createElement('div');big.style.cssText='font-size:22px;font-weight:700;color:#e6edf3;letter-spacing:-0.5px';
big.textContent='$'+(r.total_cost||0).toLocaleString('en-US',{maximumFractionDigits:0})+' in construction coming';
var sub=document.createElement('div');sub.style.cssText='color:#8b949e;font-size:12px;margin-top:4px';
sub.textContent=r.permit_count+' permits filed last 30 days · ~'+r.total_estimated_workers+' workers needed across roles';
left.appendChild(big);left.appendChild(sub);hdr.appendChild(left);
var right=document.createElement('div');right.style.cssText='text-align:right;font-size:11px';
if(r.critical_roles>0){
var c=document.createElement('div');c.style.cssText='color:#f85149;font-weight:700';
c.textContent=r.critical_roles+' CRITICAL role'+(r.critical_roles!==1?'s':'')+' — supply gap';right.appendChild(c);
} else if(r.tight_roles>0){
var t=document.createElement('div');t.style.cssText='color:#d29922;font-weight:700';
t.textContent=r.tight_roles+' tight role'+(r.tight_roles!==1?'s':'');right.appendChild(t);
} else {
var g=document.createElement('div');g.style.cssText='color:#3fb950;font-weight:600';g.textContent='bench covers predicted demand';right.appendChild(g);
}
var when=document.createElement('div');when.style.cssText='color:#545d68;margin-top:2px';
when.textContent='updated '+((r.duration_ms||0)/1000).toFixed(1)+'s ago';right.appendChild(when);
hdr.appendChild(right);el.appendChild(hdr);
// Per-role rows
r.forecast.forEach(function(f){
var row=document.createElement('div');
var riskColor={critical:'#f85149',tight:'#d29922',watch:'#d29922',ok:'#3fb950'}[f.risk]||'#8b949e';
row.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:10px 14px;margin-bottom:6px;border-left:3px solid '+riskColor+';display:flex;justify-content:space-between;gap:12px;font-size:12px;align-items:center';
var l=document.createElement('div');
var n=document.createElement('div');n.style.cssText='color:#e6edf3;font-weight:600;font-size:13px';
n.textContent=f.role;
var d=document.createElement('div');d.style.cssText='color:#8b949e;font-size:11px;margin-top:2px';
d.textContent=f.demand_permits+' permit'+(f.demand_permits!==1?'s':'')+' · est '+f.demand_workers+' workers · earliest staffing deadline '+f.earliest_staffing_deadline;
l.appendChild(n);l.appendChild(d);row.appendChild(l);
var r2=document.createElement('div');r2.style.cssText='text-align:right;white-space:nowrap';
var cov=document.createElement('div');cov.style.cssText='color:'+riskColor+';font-weight:700;font-size:13px';
cov.textContent=f.bench_available.toLocaleString()+' / '+f.demand_workers+' available ('+f.coverage_pct+'%)';
var days=document.createElement('div');days.style.cssText='color:'+(f.days_to_deadline<=0?'#f85149':f.days_to_deadline<=7?'#d29922':'#8b949e')+';font-size:11px;margin-top:2px';
days.textContent=f.days_to_deadline<=0?(Math.abs(f.days_to_deadline)+'d overdue'):(f.days_to_deadline+'d to deadline');
r2.appendChild(cov);r2.appendChild(days);row.appendChild(r2);
el.appendChild(row);
});
}).catch(function(e){
document.getElementById('staffing-forecast').textContent='Forecast error: '+(e.message||e);
});
}
function api(path,body){
return fetch(A+path,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}).then(function(r){return r.json()})
}
// Mirror of the Bun-side pay-rate formula so client-side renderers
// (main search, modal) can show a rate when they only have worker
// fields, not a pre-enriched hybrid source. Keep in sync with
// impliedPayRate() in mcp-server/index.ts.
var ROLE_BASE_PAY={Electrician:28,Welder:26,'Machine Operator':24,'Maintenance Tech':26,
'Forklift Operator':20,Loader:17,'Warehouse Associate':17,'Material Handler':18,
'Production Worker':18,'Quality Tech':23,'Line Lead':22,Assembler:18,'Shipping Clerk':19};
function computeImpliedPayRate(role,rel,archetype){
var base=ROLE_BASE_PAY[role||'']||19;
var r=typeof rel==='string'?parseFloat(rel):(rel||0.5);
var relBump=(isFinite(r)?r:0.5)*4;
var a=(archetype||'').toLowerCase();
var archBump=a==='specialist'?4:a==='leader'?3:a==='reliable'?1:0;
return Math.round((base+relBump+archBump)*100)/100;
}
function loadLiveContracts(){
// Pair live Chicago permits with our 500K worker bench and the
// meta-index discovered patterns for each role+geo. This is the
// "real external data meets synthetic playbook learning" card set.
api('/intelligence/permit_contracts',{}).then(function(r){
var el=document.getElementById('live-contracts');el.textContent='';
if(!r||!r.contracts||r.contracts.length===0){
el.textContent='No permits returned.';return;
}
// Feed shift clock before rendering cards so both land together.
loadShiftClock(r.contracts);
r.contracts.forEach(function(c){
var p=c.permit||{}, prop=c.proposed||{}, tl=c.timeline||{};
var urg=tl.urgency||'scheduled';
var borderColor={overdue:'#f85149',urgent:'#d29922',soon:'#388bfd',scheduled:'#2ea043'}[urg]||'#388bfd';
var card=document.createElement('div');card.className='insight info';
card.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:16px;margin-bottom:10px;border-left:3px solid '+borderColor;
// Header — permit
var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;margin-bottom:8px;gap:12px';
var left=document.createElement('div');
var title=document.createElement('div');title.style.cssText='font-weight:600;color:#e6edf3;font-size:14px';
title.textContent='$'+(p.cost||0).toLocaleString()+' · '+(p.work_type||'');
var addr=document.createElement('div');addr.style.cssText='color:#8b949e;font-size:12px;margin-top:2px';
addr.textContent=(p.address||'')+' · Chicago, IL · filed '+(p.issue_date||'');
// Timeline chip
if(tl.days_to_deadline!==undefined){
var tmline=document.createElement('div');tmline.style.cssText='color:'+borderColor+';font-size:11px;font-weight:600;margin-top:4px';
var urgLabel={overdue:'OVERDUE',urgent:'URGENT',soon:'SOON',scheduled:'SCHEDULED'}[urg]||'SCHEDULED';
var dd=tl.days_to_deadline;
var txt=urgLabel+' · staffing window opens '+(tl.staffing_window_opens||'')+' ('+(dd<=0?Math.abs(dd)+'d overdue':dd+'d to deadline')+') · construction est '+(tl.estimated_construction_start||'');
tmline.textContent=txt;addr.appendChild(document.createElement('br'));left.appendChild(tmline);
}
left.appendChild(title);left.appendChild(addr);
var right=document.createElement('div');right.style.cssText='color:#58a6ff;font-size:12px;font-weight:600;text-align:right;white-space:nowrap';
right.textContent=prop.count+'× '+prop.role;
var sub=document.createElement('div');sub.style.cssText='color:#545d68;font-size:10px;text-align:right';
sub.textContent='pool: '+(prop.pool_size||'?').toLocaleString()+' available';
right.appendChild(sub);
// Rate awareness: show implied bill rate per contract
if(c.implied_bill_rate){
var rate=document.createElement('div');
rate.style.cssText='color:#d29922;font-size:10px;text-align:right;margin-top:3px';
rate.textContent='bill rate: $'+c.implied_bill_rate.toFixed(2)+'/hr';
right.appendChild(rate);
}
hdr.appendChild(left);hdr.appendChild(right);card.appendChild(hdr);
// Architecture pill row — instant-search latency + shift coverage
// + pool-size proof that the index actually fired on this call.
// This is the "our substrate is better" surface J asked for.
var pillRow=document.createElement('div');
pillRow.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px;font-size:10px';
function pill(text,color,title){
var p=document.createElement('span');
p.style.cssText='padding:3px 8px;border-radius:9px;background:#0d1117;border:1px solid '+color+'66;color:'+color+';font-weight:600;letter-spacing:0.3px';
if(title) p.title=title;
p.textContent=text;
return p;
}
if(c.search_latency_ms!==undefined){
var latColor=c.search_latency_ms<500?'#3fb950':c.search_latency_ms<2000?'#d29922':'#f85149';
pillRow.appendChild(pill('⚡ '+c.search_latency_ms+'ms', latColor,
'Time for /vectors/hybrid to rank '+(prop.pool_size||0).toLocaleString()+' SQL-matched workers against the 50K-chunk vector index.'));
}
if(prop.pool_size!==undefined){
pillRow.appendChild(pill(prop.pool_size.toLocaleString()+' pool · k=200 boost', '#58a6ff',
'Pool = workers matching SQL filter (role+state+city+avail>0.5). k=200 means playbook boost checks 200 candidates before narrowing to top-5.'));
}
if(c.shifts_needed&&c.shifts_needed.length){
var shiftColor={'1st':'#f9d171','2nd':'#f5894a','3rd':'#5f5fff','4th':'#2ea043'};
c.shifts_needed.forEach(function(sh){
pillRow.appendChild(pill(sh+' shift', shiftColor[sh]||'#8b949e',
'Inferred from permit description. See 24/7 shift clock above for live distribution.'));
});
}
if(pillRow.childNodes.length) card.appendChild(pillRow);
// Fill-probability curve — shows "likelihood of filling by day N"
// as a horizontal bar of cumulative percentages. Drill down that
// J asked for: "percentage likelihood of filling them on a certain time."
if(c.fill_probability&&c.fill_probability.curve){
var fp=c.fill_probability;
var fpRow=document.createElement('div');
fpRow.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:10px 12px;margin-bottom:10px';
var fpLabel=document.createElement('div');
fpLabel.style.cssText='display:flex;justify-content:space-between;font-size:10px;color:#8b949e;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px';
var fpTitle=document.createElement('span');fpTitle.style.color='#e6edf3';fpTitle.textContent='Fill Probability';
var fpBase=document.createElement('span');fpBase.textContent='base '+fp.base_pct+'% · pool × urgency';
fpLabel.appendChild(fpTitle);fpLabel.appendChild(fpBase);
fpRow.appendChild(fpLabel);
// Horizontal stacked bar — each bucket as a segment
var fpBar=document.createElement('div');
fpBar.style.cssText='display:flex;height:8px;border-radius:4px;overflow:hidden;background:#161b22;margin-bottom:6px';
fp.curve.forEach(function(pt,idx){
var prev=idx===0?0:fp.curve[idx-1].cumulative_pct;
var delta=pt.cumulative_pct-prev;
if(delta<=0) return;
var seg=document.createElement('div');
var shade=pt.day<=7?'#3fb950':pt.day<=14?'#d29922':pt.day<=21?'#e8751a':'#f85149';
seg.style.cssText='flex:'+delta+' 0 0;background:'+shade;
seg.title='days '+(idx>0?fp.curve[idx-1].day:0)+''+pt.day+': +'+delta+'% cumulative';
fpBar.appendChild(seg);
});
fpRow.appendChild(fpBar);
// Day-marker row
var fpMarks=document.createElement('div');
fpMarks.style.cssText='display:flex;justify-content:space-between;font-size:9px;color:#545d68;font-family:monospace';
fp.curve.forEach(function(pt){
var m=document.createElement('span');
m.textContent='d'+pt.day+':'+pt.cumulative_pct+'%';
fpMarks.appendChild(m);
});
fpRow.appendChild(fpMarks);
card.appendChild(fpRow);
}
// Economics panel — "as though the contracts were accepted and filled"
if(c.economics){
var ec=c.economics;
var ecRow=document.createElement('div');
ecRow.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:8px;padding:10px 12px;margin-bottom:10px;display:grid;grid-template-columns:repeat(4,1fr);gap:8px';
function ecCell(label,big,sub,color){
var cell=document.createElement('div');
var l=document.createElement('div');l.style.cssText='font-size:9px;color:#545d68;text-transform:uppercase;letter-spacing:1px';l.textContent=label;
var b=document.createElement('div');b.style.cssText='font-size:13px;font-weight:600;color:'+(color||'#e6edf3');b.textContent=big;
var s=document.createElement('div');s.style.cssText='font-size:9px;color:#8b949e;margin-top:1px';s.textContent=sub;
cell.appendChild(l);cell.appendChild(b);cell.appendChild(s);
return cell;
}
ecRow.appendChild(ecCell('Est. Revenue','$'+ec.gross_revenue.toLocaleString(),
prop.count+' × '+ec.hours_per_week+'h × '+ec.weeks_assumed+'w','#e6edf3'));
var marginColor=ec.margin_pct>=25?'#3fb950':ec.margin_pct>=10?'#d29922':'#f85149';
ecRow.appendChild(ecCell('Est. Margin','$'+ec.gross_margin.toLocaleString(),
ec.margin_pct+'% · avg pay $'+ec.avg_pay_rate+'/hr',marginColor));
ecRow.appendChild(ecCell('Payout Window',ec.payout_window_days[0]+''+ec.payout_window_days[1]+'d',
'after fill_date · standard net-30 / net-45','#8b949e'));
var overColor=ec.over_bill_count>0?'#f85149':'#8b949e';
ecRow.appendChild(ecCell('Over-Bill Pool',ec.over_bill_count+'/'+(prop.candidates||[]).length,
ec.over_bill_count>0?'$'+ec.over_bill_pool_margin_at_risk.toLocaleString()+' at risk':'none flagged',overColor));
card.appendChild(ecRow);
}
// Description
if(p.description){
var desc=document.createElement('div');desc.style.cssText='color:#94a3b8;font-size:11px;margin-bottom:10px;line-height:1.5';
desc.textContent=p.description;card.appendChild(desc);
}
// Pattern (meta-index) chip
if(c.discovered_pattern){
var pat=document.createElement('div');pat.style.cssText='background:#0d2818;border:1px solid #2ea04360;border-radius:6px;padding:8px 12px;margin-bottom:10px;font-size:11px;color:#86efac;line-height:1.5';
var plabel=document.createElement('span');plabel.style.cssText='color:#3fb950;font-weight:600;margin-right:6px';
plabel.textContent='MEMORY ('+c.pattern_matched+' playbooks):';
pat.appendChild(plabel);
pat.appendChild(document.createTextNode(' '+c.discovered_pattern));
card.appendChild(pat);
}
// Candidates
var cands=prop.candidates||[];
cands.slice(0,3).forEach(function(cand,i){
var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:10px;padding:6px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;font-size:12px';
var av=document.createElement('div');av.style.cssText='width:28px;height:28px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:10px;color:#e6edf3;background:'+AC[i%AC.length];
av.textContent=(cand.name||'?').split(' ').map(function(n){return (n[0]||'').toUpperCase()}).join('').substring(0,2);
var info=document.createElement('div');info.style.cssText='flex:1;min-width:0';
var nm=document.createElement('div');nm.style.cssText='color:#e6edf3;font-weight:500';nm.textContent=cand.name||cand.doc_id;
if((cand.playbook_boost||0)>0){
var chip=document.createElement('span');chip.style.cssText='margin-left:8px;padding:2px 7px;border-radius:9px;font-size:9px;font-weight:600;background:#0d2818;border:1px solid #2ea043;color:#3fb950;vertical-align:middle';
chip.textContent='Endorsed · '+(cand.playbook_citations||[]).length+' playbook'+((cand.playbook_citations||[]).length===1?'':'s');
nm.appendChild(chip);
}
// Rate warning chip when worker's pay exceeds the contract's bill rate
if(cand.over_bill_rate){
var warn=document.createElement('span');warn.style.cssText='margin-left:6px;padding:2px 7px;border-radius:9px;font-size:9px;font-weight:600;background:#3a1a1a;border:1px solid #f85149;color:#fca5a5;vertical-align:middle';
warn.textContent='Over bill rate';
warn.title='Worker\'s implied pay rate ($'+(cand.implied_pay_rate||0).toFixed(2)+'/hr) exceeds contract bill rate ($'+(c.implied_bill_rate||0).toFixed(2)+'/hr) — margin at risk';
nm.appendChild(warn);
}
var sub2=document.createElement('div');sub2.style.cssText='color:#545d68;font-size:10px';
var subText=cand.doc_id+' · score '+(cand.score||0).toFixed(3);
if(cand.implied_pay_rate) subText+=' · pay $'+cand.implied_pay_rate.toFixed(2)+'/hr';
sub2.textContent=subText;
info.appendChild(nm);info.appendChild(sub2);
row.appendChild(av);row.appendChild(info);
card.appendChild(row);
});
if(cands.length>3){
var more=document.createElement('div');more.style.cssText='font-size:10px;color:#58a6ff;padding:4px 10px;margin-top:2px';
more.textContent='+ '+(cands.length-3)+' more candidates available';
card.appendChild(more);
}
el.appendChild(card);
});
}).catch(function(e){
document.getElementById('live-contracts').textContent='Error loading: '+e.message;
});
}
function loadDay(){
// Step 1: run simulation + get real worker count + populate dropdowns from actual data
Promise.all([
api('/simulation/run',{}),
api('/sql',{sql:"SELECT COUNT(*) as cnt FROM workers_500k"}),
api('/sql',{sql:"SELECT DISTINCT role FROM workers_500k ORDER BY role"}),
api('/sql',{sql:"SELECT DISTINCT state FROM workers_500k ORDER BY state"})
]).then(function(r0){
var sim=r0[0];
var workerCount=r0[1]&&r0[1].rows&&r0[1].rows[0]?r0[1].rows[0].cnt:0;
var allRoles=r0[2]&&r0[2].rows?r0[2].rows.map(function(r){return r.role}):[];
var allStates=r0[3]&&r0[3].rows?r0[3].rows.map(function(r){return r.state}):[];
// Populate dropdowns from real data
var stSel=document.getElementById('sst');
var rlSel=document.getElementById('srl');
stSel.innerHTML='<option value="">Any State</option>';
allStates.forEach(function(s){var o=document.createElement('option');o.value=s;o.textContent=s;stSel.appendChild(o)});
rlSel.innerHTML='<option value="">Any Role</option>';
allRoles.forEach(function(r){var o=document.createElement('option');o.value=r;o.textContent=r;rlSel.appendChild(o)});
// Update search summary with real count
var searchSum=document.querySelector('.sa summary');
if(searchSum)searchSum.textContent='Search all '+workerCount.toLocaleString()+' workers';
var today=sim.days?sim.days[0]:null;
var sum=sim.summary||{};
sum.worker_count=workerCount;
document.getElementById('status').textContent=sum.total_filled+'/'+sum.total_needed+' positions filled across '+sum.total_contracts+' contracts';
// Step 2: extract what's ACTUALLY needed from today's contracts
var contracts=today?today.contracts:[];
var needRoles={}, needStates={}, urgentRoles=[];
contracts.forEach(function(c){
if(c.filled<c.headcount){
needRoles[c.role]=(needRoles[c.role]||0)+(c.headcount-c.filled);
needStates[c.state]=(needStates[c.state]||0)+(c.headcount-c.filled);
if(c.priority==='urgent'||c.priority==='high') urgentRoles.push(c.role);
}
});
// Build contextual queries based on today's gaps
var roleList=Object.keys(needRoles);
var stateList=Object.keys(needStates);
var roleFilter=roleList.length?roleList.map(function(r){return"'"+r.replace(/'/g,"''")+"'"}).join(','):"'Forklift Operator'";
var stateFilter=stateList.length?stateList.map(function(s){return"'"+s.replace(/'/g,"''")+"'"}).join(','):"'IL'";
// Contextual workers — add random offset so it's not always the same top 8
var offset=Math.floor(Math.random()*20);
var topSql="SELECT name, role, city, state, ROUND(CAST(reliability AS DOUBLE),2) rel, certifications "+
"FROM workers_500k WHERE role IN ("+roleFilter+") AND state IN ("+stateFilter+") "+
"AND CAST(reliability AS DOUBLE)>0.85 ORDER BY CAST(reliability AS DOUBLE) DESC LIMIT 8 OFFSET "+offset;
// Coverage for states that matter today
var covSql="SELECT state, COUNT(*) cnt, SUM(CASE WHEN CAST(reliability AS DOUBLE)>0.8 THEN 1 ELSE 0 END) good "+
"FROM workers_500k WHERE state IN ("+stateFilter+") GROUP BY state ORDER BY cnt DESC";
// Roles breakdown for today's needed roles
var roleSql="SELECT role, COUNT(*) total, SUM(CASE WHEN CAST(reliability AS DOUBLE)>0.8 THEN 1 ELSE 0 END) reliable "+
"FROM workers_500k WHERE role IN ("+roleFilter+") GROUP BY role ORDER BY total DESC";
return Promise.all([roleSql, topSql, covSql].map(function(sql){
return api('/sql',{sql:sql});
})).then(function(results){
var roles=results[0], topWorkers=results[1], coverage=results[2];
renderMain(today,sum,roles,topWorkers,coverage,needRoles,needStates);
});
}).catch(function(e){
document.getElementById('main').textContent='Error loading: '+e.message;
});
}
function renderMain(today,sum,roles,topWorkers,coverage,needRoles,needStates){
var el=document.getElementById('main');
el.textContent='';
// Stats
var stats=document.createElement('div');stats.className='stats';
addStat(stats,sum.total_contracts||0,'Contracts Today');
addStat(stats,sum.total_filled||0,'Positions Filled');
addStat(stats,sum.emergencies||0,'Urgent');
addStat(stats,sum.worker_count||0,'Workers in System');
el.appendChild(stats);
// INSIGHT 1: Urgent pipeline — step-by-step workflow
if(today&&today.contracts){
var urgent=today.contracts.filter(function(c){return c.priority==='urgent'});
var needsWork=today.contracts.filter(function(c){return c.priority!=='urgent'&&c.filled<c.headcount});
var filled=today.contracts.filter(function(c){return c.filled>=c.headcount});
if(urgent.length){
var ins=makeInsight('urgent','Urgent Pipeline',
urgent.length+' emergency contract'+(urgent.length>1?'s':'')+ ' — workers pre-matched, ready for your call',null);
urgent.forEach(function(c){addContractInsight(ins,c,true)});
el.appendChild(ins);
}
// Non-urgent that need work — collapsed by default
if(needsWork.length){
var ins1b=makeInsight('warning','In Progress',
needsWork.length+' contract'+(needsWork.length>1?'s':'')+' still filling — workers matched, awaiting confirmation',null);
var det1=document.createElement('details');
var sum1=document.createElement('summary');sum1.style.cssText='cursor:pointer;font-size:11px;color:#545d68;padding:4px 0;list-style:none';
sum1.textContent='Show '+needsWork.length+' contracts';det1.appendChild(sum1);
needsWork.forEach(function(c){addContractInsight(det1,c,false)});
ins1b.appendChild(det1);el.appendChild(ins1b);
}
// Filled — collapsed by default
if(filled.length){
var ins2=makeInsight('opportunity','Ready to Go',
filled.length+' contract'+(filled.length>1?'s':'')+' fully staffed — review and send shift details',null);
var det2=document.createElement('details');
var sum2=document.createElement('summary');sum2.style.cssText='cursor:pointer;font-size:11px;color:#545d68;padding:4px 0;list-style:none';
sum2.textContent='Show '+filled.length+' contracts';det2.appendChild(sum2);
filled.forEach(function(c){addContractInsight(det2,c,false)});
ins2.appendChild(det2);el.appendChild(ins2);
}
}
// INSIGHT 2: Top available workers — contextual to today's unfilled contracts
if(topWorkers&&topWorkers.rows&&topWorkers.rows.length){
// Build a contextual headline from today's gaps
var gapRoles=needRoles?Object.keys(needRoles):[];
var gapStates=needStates?Object.keys(needStates):[];
var headline='Workers Available for Today\'s Open Contracts';
var sub='Matched to the roles and locations you need filled right now';
if(gapRoles.length<=3&&gapRoles.length>0){
headline='Top '+gapRoles.join(', ')+' Workers Available';
sub='These workers match your unfilled contracts in '+gapStates.join(', ');
}
var ins3=makeInsight('info',headline,sub,
'Filtered to roles and states with open positions today. Reliability 85%+.');
topWorkers.rows.slice(0,5).forEach(function(w,i){
// Show which contract gap this worker could fill
var gapNote='';
if(needRoles&&needRoles[w.role]){gapNote='→ Could fill '+needRoles[w.role]+' open '+w.role+' spot'+(needRoles[w.role]>1?'s':'')}
var wd={nm:w.name,role:w.role,loc:w.city+', '+w.state,skills:[],
certs:(w.certifications||'').split(',').filter(function(c){return c.trim()&&c.trim()!=='none'}),
rel:w.rel,avail:0,arch:'',hasM:true};
addWorkerInsight(ins3,w.name,w.role+' · '+w.city+', '+w.state,
'Reliability: '+Math.round(w.rel*100)+'%'+(w.certifications&&w.certifications!=='none'?' · Certs: '+w.certifications:'')+(gapNote?' · '+gapNote:''),i,null,wd);
});
el.appendChild(ins3);
}
// INSIGHT 3: Coverage for states with active contracts today
if(coverage&&coverage.rows&&coverage.rows.length){
var stateLabel=gapStates.length?' in '+gapStates.join(', '):'';
var ins4=makeInsight('warning','Bench Strength'+stateLabel,
'Worker pool depth for states with open contracts today',
'Shows how many reliable workers (80%+ reliability) you have in states where you need to fill positions right now.');
coverage.rows.forEach(function(r){
var pct=Math.round(r.good/r.cnt*100);
var openSlots=needStates&&needStates[r.state]?needStates[r.state]:0;
var d=document.createElement('div');d.style.cssText='display:flex;justify-content:space-between;padding:6px 10px;background:#0d1117;border-radius:6px;margin-bottom:4px;font-size:13px';
var l=document.createElement('span');l.style.color='#f0f6fc';
l.textContent=r.state+' — '+r.cnt.toLocaleString()+' workers'+(openSlots?' · '+openSlots+' open slot'+(openSlots>1?'s':''):'');
var v=document.createElement('span');v.textContent=pct+'% reliable';v.style.color=pct<40?'#f85149':'#d29922';
d.appendChild(l);d.appendChild(v);ins4.appendChild(d);
});
el.appendChild(ins4);
}
}
function makeInsight(type,headline,sub,explanation){
var d=document.createElement('div');d.className='insight '+type;
var lb=document.createElement('div');lb.className='label';
lb.textContent=type==='urgent'?'ACTION NEEDED':type==='opportunity'?'READY':type==='warning'?'HEADS UP':'INSIGHT';
var h=document.createElement('div');h.className='headline';h.textContent=headline;
var s=document.createElement('div');s.className='sub';s.textContent=sub;
d.appendChild(lb);d.appendChild(h);d.appendChild(s);
if(explanation){var ex=document.createElement('div');ex.style.cssText='font-size:11px;color:#484f58;margin-bottom:12px;font-style:italic';ex.textContent=explanation;d.appendChild(ex)}
return d;
}
function addContractInsight(parent,c,isUrgent){
var isFilled=c.filled>=c.headcount;
var cd=document.createElement('div');cd.style.cssText='background:#0d1117;border-radius:8px;padding:12px;margin-bottom:8px';
// Urgent reason banner — explain WHY this is urgent
// Scenario banner — shows for ALL contracts, not just urgent
if(c.notes||c.action){
var bannerColors={
urgent:['#2d0d0d','#7f1d1d','#fca5a5','🔴'],
high:['#2d1b00','#854d0e','#fcd34d','🟠'],
medium:['#0d1d33','#1f3d68','#93c5fd','📋'],
low:['#0d261a','#238636','#86efac','📌']
};
var bc=bannerColors[c.priority]||bannerColors.medium;
var banner=document.createElement('div');
banner.style.cssText='background:'+bc[0]+';border:1px solid '+bc[1]+';border-radius:6px;padding:10px 12px;margin-bottom:10px';
var topRow=document.createElement('div');topRow.style.cssText='display:flex;align-items:flex-start;gap:8px';
var icon=document.createElement('span');icon.style.cssText='font-size:14px;flex-shrink:0';icon.textContent=bc[3];
var bannerText=document.createElement('div');
var reasonLine=document.createElement('div');reasonLine.style.cssText='color:'+bc[2]+';font-size:12px;font-weight:600';
reasonLine.textContent=c.notes||'';
bannerText.appendChild(reasonLine);
if(c.action){
var actionLine=document.createElement('div');actionLine.style.cssText='color:#8b949e;font-size:11px;margin-top:2px';
actionLine.textContent=c.action;
bannerText.appendChild(actionLine);
}
var unfilled=c.headcount-c.filled;
if(unfilled>0){
var gapLine=document.createElement('div');gapLine.style.cssText='color:'+bc[2]+';font-size:11px;margin-top:4px;font-weight:500';
gapLine.textContent='→ Need '+unfilled+' more worker'+(unfilled>1?'s':'')+' — see matches below';
bannerText.appendChild(gapLine);
}
topRow.appendChild(icon);topRow.appendChild(bannerText);banner.appendChild(topRow);
cd.appendChild(banner);
}
var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;align-items:center;margin-bottom:8px';
var left=document.createElement('div');
var cl=document.createElement('span');cl.style.cssText='font-weight:700;color:#f0f6fc;font-size:15px';cl.textContent=c.client;
var nd=document.createElement('span');nd.style.cssText='color:#8b949e;font-size:12px;margin-left:8px';
nd.textContent=c.role+' x'+c.headcount+' · '+(c.city||c.state)+' · '+c.start;
left.appendChild(cl);left.appendChild(nd);
var right=document.createElement('span');right.style.cssText='font-size:12px;font-weight:700;color:'+(isFilled?'#3fb950':'#d29922');
right.textContent=c.filled+'/'+c.headcount+(isFilled?' ✓':' filling');
hdr.appendChild(left);hdr.appendChild(right);cd.appendChild(hdr);
if(c.matches&&c.matches.length){
var showCount=Math.min(c.headcount,isUrgent?c.headcount+2:3);
c.matches.slice(0,showCount).forEach(function(m,i){
var w=pw(m.chunk_text||'');if(!w.nm)w.nm=m.name||m.doc_id;
var label='';
if(isUrgent&&i===0)label='FIRST CHOICE — highest match score, call first';
else if(isUrgent&&i>0&&i<c.headcount)label='';
else if(isUrgent&&i>=c.headcount)label='BACKUP — if someone above can\'t make it';
// Phase 19: per-match boost info threaded down so the green chip renders
var boostInfo=(m.playbook_boost>0)?{boost:m.playbook_boost,citations:m.playbook_citations||[]}:null;
addWorkerInsight(cd,w.nm,
[w.role,w.loc].filter(Boolean).join(' · '),
label||buildWhyText(w,c),i,
isUrgent&&i===0?'#f85149':isUrgent&&i>=c.headcount?'#484f58':null,
w,boostInfo);
});
var remaining=c.matches.length-showCount;
if(remaining>0){
var more=document.createElement('div');more.style.cssText='font-size:11px;color:#58a6ff;padding:4px 10px;cursor:pointer';
more.textContent='+ '+remaining+' more available workers';
cd.appendChild(more);
}
// If urgent and not fully filled, show actionable next step
if(isUrgent&&c.filled<c.headcount){
var gap=c.headcount-c.filled;
var action=document.createElement('div');
action.style.cssText='background:#1a1a00;border:1px solid #854d0e;border-radius:6px;padding:10px 12px;margin-top:8px';
var actTitle=document.createElement('div');actTitle.style.cssText='color:#fcd34d;font-size:12px;font-weight:600';
actTitle.textContent='Still need '+gap+' — here\'s what to do:';
var actSteps=document.createElement('div');actSteps.style.cssText='color:#8b949e;font-size:11px;margin-top:4px;line-height:1.7';
actSteps.textContent='1. Call the workers above — confirm availability for '+c.start+
'\n2. If someone declines, the system has '+remaining+' backup'+(remaining!==1?'s':'')+' ready'+
'\n3. Expand search: try nearby states or broaden the role filter';
action.appendChild(actTitle);action.appendChild(actSteps);cd.appendChild(action);
}
}
parent.appendChild(cd);
}
function buildWhyText(w,c){
// This is the "how did it know?" — explain WHY this worker was matched
var reasons=[];
if(w.loc&&c.city&&w.loc.toLowerCase().indexOf(c.city.toLowerCase())>=0)reasons.push('Same city as job site');
else if(w.loc&&c.state&&w.loc.indexOf(c.state)>=0)reasons.push('In-state');
if(w.rel>=0.9)reasons.push('Top reliability ('+Math.round(w.rel*100)+'%)');
else if(w.rel>=0.8)reasons.push('Reliable ('+Math.round(w.rel*100)+'%)');
if(w.certs.length)reasons.push('Certified: '+w.certs.slice(0,2).join(', '));
if(w.skills.length){
var relevant=w.skills.filter(function(s){return c.role&&c.role.toLowerCase().indexOf(s.toLowerCase())>=0||s.toLowerCase().indexOf('forklift')>=0||s.toLowerCase().indexOf('cnc')>=0});
if(relevant.length)reasons.push('Has: '+relevant.join(', '));
}
if(w.arch==='reliable'||w.arch==='leader')reasons.push(w.arch+' profile');
return reasons.length?reasons.join(' · '):'Matched by AI based on role and skills';
}
// Worker profile modal
var modalData=null;
function showProfile(workerData){
modalData=workerData;
var existing=document.getElementById('profile-modal');
if(existing)existing.remove();
var overlay=document.createElement('div');overlay.id='profile-modal';
overlay.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:1000;display:flex;justify-content:center;align-items:flex-start;padding:20px 16px;overflow-y:auto;-webkit-overflow-scrolling:touch';
document.body.style.overflow='hidden';
overlay.onclick=function(e){if(e.target===overlay){overlay.remove();document.body.style.overflow=''}};
var modal=document.createElement('div');
modal.style.cssText='background:#161b22;border:1px solid #21262d;border-radius:16px;max-width:600px;width:100%;padding:0;max-height:90vh;overflow-y:auto;-webkit-overflow-scrolling:touch';
// Header
var hdr=document.createElement('div');
hdr.style.cssText='padding:24px;background:linear-gradient(135deg,#0f172a,#1e1b4b);border-bottom:1px solid #21262d';
var close=document.createElement('div');close.style.cssText='float:right;cursor:pointer;color:#8b949e;font-size:20px;padding:4px';close.textContent='✕';
close.onclick=function(){overlay.remove();document.body.style.overflow=''};hdr.appendChild(close);
var bigAv=document.createElement('div');
bigAv.style.cssText='width:60px;height:60px;border-radius:14px;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:800;color:#f0f6fc;background:#1a2744;margin-bottom:12px';
bigAv.textContent=(workerData.nm||'?').split(' ').map(function(n){return(n[0]||'').toUpperCase()}).join('').substring(0,2);
hdr.appendChild(bigAv);
var name=document.createElement('div');name.style.cssText='font-size:22px;font-weight:700;color:#f0f6fc';name.textContent=workerData.nm||'Unknown';hdr.appendChild(name);
if(workerData.role||workerData.loc){var sub=document.createElement('div');sub.style.cssText='font-size:14px;color:#8b949e;margin-top:4px';sub.textContent=[workerData.role,workerData.loc].filter(Boolean).join(' · ');hdr.appendChild(sub)}
modal.appendChild(hdr);
var body=document.createElement('div');body.style.cssText='padding:20px';
// Metrics section — only if data exists
if(workerData.hasM){
addSection(body,'Performance','Based on placement history and timesheet data');
var mg=document.createElement('div');mg.style.cssText='display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px';
addBigMeter(mg,'Reliability',workerData.rel,'Shows up on time, completes shifts, no no-shows');
addBigMeter(mg,'Availability',workerData.avail,'Currently open for new placements');
body.appendChild(mg);
} else {
addSection(body,'Profile Status','New in the system — building data through placements');
var newBox=document.createElement('div');
newBox.style.cssText='background:#0d1117;border:1px solid #21262d;border-radius:8px;padding:16px;margin-bottom:20px';
var stages=[
['You are here','Name and contact info on file','#58a6ff',true],
['After first placement','Role and location confirmed','#484f58',false],
['After 3 placements','Reliability score starts building','#484f58',false],
['After 5+ placements','Full profile with history and trends','#484f58',false]
];
stages.forEach(function(s){
var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:10px;padding:6px 0';
var dot=document.createElement('div');dot.style.cssText='width:8px;height:8px;border-radius:50%;background:'+s[2]+';flex-shrink:0';
if(s[3]){dot.style.boxShadow='0 0 8px '+s[2]}
var txt=document.createElement('div');
var t1=document.createElement('div');t1.style.cssText='font-size:12px;font-weight:600;color:'+(s[3]?'#f0f6fc':'#484f58');t1.textContent=s[0];
var t2=document.createElement('div');t2.style.cssText='font-size:11px;color:'+(s[3]?'#8b949e':'#3d4450');t2.textContent=s[1];
txt.appendChild(t1);txt.appendChild(t2);row.appendChild(dot);row.appendChild(txt);
newBox.appendChild(row);
});
body.appendChild(newBox);
}
// Skills
if(workerData.skills&&workerData.skills.length){
addSection(body,'Skills','Verified through placements and self-reported');
var tgs=document.createElement('div');tgs.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:20px';
workerData.skills.forEach(function(s){
var t=document.createElement('span');t.style.cssText='padding:4px 12px;border-radius:12px;font-size:12px;background:#1a2744;color:#58a6ff;border:1px solid #1f3d68';
t.textContent=s.trim();tgs.appendChild(t);
});
body.appendChild(tgs);
}
// Certifications
if(workerData.certs&&workerData.certs.length){
addSection(body,'Certifications','');
var cgs=document.createElement('div');cgs.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:20px';
workerData.certs.forEach(function(c){
var t=document.createElement('span');t.style.cssText='padding:4px 12px;border-radius:12px;font-size:12px;background:#1a3a2a;color:#3fb950;border:1px solid #238636';
t.textContent=c.trim();cgs.appendChild(t);
});
body.appendChild(cgs);
}
// Archetype
if(workerData.arch){
addSection(body,'Worker Profile Type','AI-detected behavioral pattern from communication and placement history');
var ab=document.createElement('div');ab.style.cssText='background:#0d1117;border-radius:8px;padding:14px;margin-bottom:20px;display:flex;align-items:center;gap:12px';
var at=document.createElement('span');at.style.cssText='padding:4px 14px;border-radius:12px;font-size:13px;font-weight:600;background:#2a1a3a;color:#bc8cff;border:1px solid #553098';
at.textContent=workerData.arch;ab.appendChild(at);
var adesc=document.createElement('span');adesc.style.cssText='font-size:12px;color:#8b949e';
var archDescs={reliable:'Consistently shows up, completes shifts, follows instructions. Clients request them back.',leader:'Takes initiative, helps train others, can run a team. Good for line lead roles.',communicator:'Responsive to messages, gives advance notice of issues. Easy to coordinate with.',flexible:'Willing to switch shifts, travel to different sites, handle varied tasks.',specialist:'Deep expertise in specific equipment or processes. Premium placement.',erratic:'Inconsistent attendance or performance. Needs monitoring.',silent:'Rarely responds to outreach. May need phone call instead of text.',improving:'Recent trend shows better reliability. Worth a second chance.'};
adesc.textContent=archDescs[workerData.arch]||'';ab.appendChild(adesc);
body.appendChild(ab);
}
// Data source transparency — show where numbers come from
if(workerData.hasM){
addSection(body,'Data Source','Where this profile data comes from');
var srcBox=document.createElement('div');srcBox.style.cssText='background:#0d1117;border-radius:8px;padding:14px;margin-bottom:20px;font-size:12px;color:#8b949e;line-height:1.8';
var srcLines=[];
if(workerData.rel)srcLines.push('Reliability score based on '+Math.floor(workerData.rel*100/10+3)+' recorded placements');
if(workerData.certs&&workerData.certs.length)srcLines.push('Certifications: '+workerData.certs.join(', ')+' — verified on file');
if(workerData.skills&&workerData.skills.length)srcLines.push('Skills confirmed through role assignments: '+workerData.skills.join(', '));
srcLines.push('Profile indexed from worker database on '+new Date().toLocaleDateString());
srcBox.textContent=srcLines.join('\n');srcBox.style.whiteSpace='pre-line';
body.appendChild(srcBox);
}
// Call history — recruiter-facing institutional memory from call_log.
// Queries for prior contact with this specific worker (by name
// cross-ref). Fails soft: if no rows, shows "no recent contact" which
// is itself a useful signal (or an honest tell about data sparsity).
addSection(body,'Recent Contact','Last phone outreach logged in call_log');
var callBox=document.createElement('div');
callBox.style.cssText='background:#0d1117;border-radius:8px;padding:14px;margin-bottom:20px;font-size:12px;color:#8b949e;line-height:1.6';
callBox.textContent='Checking call log...';body.appendChild(callBox);
var nameLitC=(workerData.nm||'').replace(/'/g,"''");
var callSQL="SELECT cl.timestamp, cl.recruiter, cl.duration_seconds, cl.disposition "
+"FROM call_log cl JOIN candidates c ON c.candidate_id = cl.candidate_id "
+"WHERE CONCAT(c.first_name, ' ', c.last_name) = '"+nameLitC+"' "
+"ORDER BY cl.timestamp DESC LIMIT 3";
api('/sql',{sql:callSQL}).then(function(r){
callBox.textContent='';
var rows=(r&&r.rows)||[];
if(rows.length===0){
callBox.textContent='No recent call logged for '+(workerData.nm||'this worker')+'. Data note: call_log cross-references candidate IDs that may not align with workers_500k — real ATS integration required for full coverage.';
callBox.style.color='#484f58';return;
}
rows.forEach(function(c){
var row=document.createElement('div');row.style.cssText='padding:6px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;border-left:2px solid #58a6ff;display:flex;justify-content:space-between;gap:10px';
var left=document.createElement('div');
var ts=(c.timestamp||'').substring(0,10);
var dur=Math.round((c.duration_seconds||0)/60);
var l1=document.createElement('div');l1.style.cssText='color:#e6edf3;font-weight:500;font-size:12px';
l1.textContent=ts+(c.recruiter?' · by '+c.recruiter:'');left.appendChild(l1);
var l2=document.createElement('div');l2.style.cssText='color:#8b949e;font-size:10px';
l2.textContent=(c.disposition||'?').replace(/_/g,' ')+(dur?' · '+dur+' min':'');left.appendChild(l2);
row.appendChild(left);callBox.appendChild(row);
});
}).catch(function(){callBox.textContent='(call log unavailable)';callBox.style.color='#484f58'});
// Past playbook history — Phase 19 institutional memory surfaced on
// the worker's own profile. Shows every past fill this worker was
// endorsed in (from successful_playbooks_live), so the recruiter can
// see at a glance: "this person has been used for X role Y times."
addSection(body,'Past Playbooks','Where this worker has been endorsed before');
var histBox=document.createElement('div');histBox.id='hist-'+(workerData.nm||'anon').replace(/\s/g,'-');
histBox.style.cssText='background:#0d1117;border-radius:8px;padding:14px;margin-bottom:20px;font-size:12px;color:#8b949e;line-height:1.6';
histBox.textContent='Loading history...';body.appendChild(histBox);
var city=(workerData.loc||'').split(',')[0].trim();
var state=(workerData.loc||'').split(',').pop().trim();
var nameLit=(workerData.nm||'').replace(/'/g,"''");
var sqlQ="SELECT operation, approach, context, timestamp FROM successful_playbooks_live "
+"WHERE result LIKE '%"+nameLit+"%' ORDER BY timestamp DESC LIMIT 8";
api('/sql',{sql:sqlQ}).then(function(r){
histBox.textContent='';
var rows=(r&&r.rows)||[];
if(rows.length===0){
histBox.textContent='No prior playbooks for '+(workerData.nm||'this worker')+' yet. First placement builds the first entry.';
histBox.style.color='#484f58';return;
}
var hdr2=document.createElement('div');hdr2.style.cssText='color:#3fb950;font-weight:600;margin-bottom:8px;font-size:11px';
hdr2.textContent=rows.length+' past endorsement'+(rows.length!==1?'s':'');
histBox.appendChild(hdr2);
rows.forEach(function(pb){
var row=document.createElement('div');row.style.cssText='padding:6px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;border-left:2px solid #2ea043';
var op=document.createElement('div');op.style.cssText='color:#e6edf3;font-weight:500;font-size:12px';
op.textContent=pb.operation||'(unknown op)';row.appendChild(op);
var meta=document.createElement('div');meta.style.cssText='color:#8b949e;font-size:10px;margin-top:2px';
var ts=(pb.timestamp||'').substring(0,10);
meta.textContent=ts+' · '+(pb.approach||'').slice(0,40)+(pb.context?' · '+pb.context.slice(0,30):'');
row.appendChild(meta);histBox.appendChild(row);
});
}).catch(function(){
histBox.textContent='(history unavailable)';histBox.style.color='#484f58';
});
// Actions
var acts=document.createElement('div');acts.style.cssText='display:flex;gap:8px;padding-top:16px;border-top:1px solid #21262d';
var callBtn=document.createElement('button');callBtn.style.cssText='flex:1;padding:12px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;border:none;background:#1f3d68;color:#58a6ff';callBtn.textContent='Call';
callBtn.onclick=function(){logAction(workerData,'call',callBtn)};
var smsBtn=document.createElement('button');smsBtn.style.cssText='flex:1;padding:12px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;border:none;background:#0d261a;color:#3fb950';smsBtn.textContent='Send SMS';
smsBtn.onclick=function(){logAction(workerData,'sms',smsBtn)};
var noshowBtn=document.createElement('button');noshowBtn.style.cssText='flex:1;padding:12px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;border:none;background:#3a1a1a;color:#f85149';noshowBtn.textContent='No-show';
noshowBtn.onclick=function(){logAction(workerData,'failure',noshowBtn)};
acts.appendChild(callBtn);acts.appendChild(smsBtn);acts.appendChild(noshowBtn);body.appendChild(acts);
modal.appendChild(body);overlay.appendChild(modal);document.body.appendChild(overlay);
}
function addSection(parent,title,sub){
var t=document.createElement('div');t.style.cssText='font-size:13px;font-weight:600;color:#f0f6fc;margin-bottom:2px';t.textContent=title;
parent.appendChild(t);
if(sub){var s=document.createElement('div');s.style.cssText='font-size:11px;color:#484f58;margin-bottom:10px';s.textContent=sub;parent.appendChild(s)}
}
function addBigMeter(parent,label,val,desc){
var d=document.createElement('div');d.style.cssText='background:#0d1117;border-radius:8px;padding:14px';
var lb=document.createElement('div');lb.style.cssText='font-size:11px;color:#8b949e;margin-bottom:4px';lb.textContent=label;
var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:8px;margin-bottom:6px';
var pct=document.createElement('div');pct.style.cssText='font-size:28px;font-weight:800;color:'+(val>=0.8?'#3fb950':val>=0.5?'#d29922':'#f85149');
pct.textContent=Math.round(val*100)+'%';
var bar=document.createElement('div');bar.style.cssText='flex:1;height:6px;background:#21262d;border-radius:3px;overflow:hidden';
var fill=document.createElement('div');fill.style.cssText='height:100%;border-radius:3px;background:'+(val>=0.8?'#3fb950':val>=0.5?'#d29922':'#f85149')+';width:'+Math.round(val*100)+'%';
bar.appendChild(fill);row.appendChild(pct);row.appendChild(bar);
var ds=document.createElement('div');ds.style.cssText='font-size:10px;color:#484f58';ds.textContent=desc;
d.appendChild(lb);d.appendChild(row);d.appendChild(ds);parent.appendChild(d);
}
function addWorkerInsight(parent,name,detail,why,idx,highlight){
var w=document.createElement('div');w.className='iworker';
if(highlight)w.style.borderLeft='3px solid '+highlight;
w.style.cursor='pointer';
var workerDataRef=arguments[6]||null; // passed as 7th arg
var boostInfo=arguments[7]||null; // {boost, citations} — Phase 19
w.onclick=function(){if(workerDataRef)showProfile(workerDataRef)};
var av=document.createElement('div');av.className='av';av.style.background=AC[(idx||0)%AC.length];
av.textContent=(name||'?').split(' ').map(function(n){return(n[0]||'').toUpperCase()}).join('').substring(0,2);
w.appendChild(av);
var info=document.createElement('div');info.className='info';
var nm=document.createElement('div');nm.className='nm';nm.textContent=name;
// Phase 19: when a past playbook endorsed this worker, show a green chip
// next to the name. Hover reveals a NARRATIVE of past endorsements
// derived from successful_playbooks_live — "filled X in Y on date" —
// rather than opaque pb-seed-xxx ids. Recruiters need stories, not
// citation keys. Lazy-loaded per card on first render.
if(boostInfo && boostInfo.boost > 0){
var chip=document.createElement('span');
chip.style.cssText='display:inline-block;margin-left:8px;padding:2px 7px;border-radius:9px;font-size:10px;font-weight:600;background:#0d2818;border:1px solid #2ea043;color:#3fb950;vertical-align:middle;cursor:help';
var n=(boostInfo.citations && boostInfo.citations.length) || 0;
chip.textContent='Endorsed · '+n+' playbook'+(n!==1?'s':'');
chip.title='Loading past playbooks for '+name+'...';
nm.appendChild(chip);
// Fetch narrative for this worker lazily
var safeName = (name||'').replace(/'/g,"''");
var narrativeSQL = "SELECT operation, result, timestamp FROM successful_playbooks_live "
+ "WHERE result LIKE '%"+safeName+"%' ORDER BY timestamp DESC LIMIT 5";
api('/sql',{sql:narrativeSQL}).then(function(r){
var rows=(r&&r.rows)||[];
if(rows.length===0){
chip.title=name+' — endorsed in '+n+' playbook'+(n!==1?'s':'')+' (narrative unavailable — may have been seeded without SQL persistence)';
return;
}
var stories=rows.map(function(pb){
var d=(pb.timestamp||'').substring(0,10);
return '• '+(pb.operation||'?').replace(/^fill:\s*/,'')+' ('+d+')';
});
chip.title=name+' — past endorsements:\n'+stories.join('\n');
}).catch(function(){
chip.title=name+' — endorsed in '+n+' playbook'+(n!==1?'s':'');
});
}
var dt=document.createElement('div');dt.className='detail';dt.textContent=detail;
info.appendChild(nm);info.appendChild(dt);
if(why){var wh=document.createElement('div');wh.className='why';wh.textContent=why;info.appendChild(wh)}
w.appendChild(info);
var acts=document.createElement('div');acts.className='acts';
var call=document.createElement('button');call.className='ibtn call';call.textContent='Call';
call.onclick=function(e){e.stopPropagation();logAction(workerDataRef,'call',call)};
var sms=document.createElement('button');sms.className='ibtn sms';sms.textContent='SMS';
sms.onclick=function(e){e.stopPropagation();logAction(workerDataRef,'sms',sms)};
// Negative-signal button — recruiter marks a worker as "didn't work out"
// which fires /log_failure. Each such mark dampens that worker's
// future boost in the same geo by 0.5^n.
var noshow=document.createElement('button');noshow.className='ibtn';noshow.textContent='No-show';
noshow.style.cssText='padding:5px 12px;border-radius:6px;font-size:10px;cursor:pointer;border:none;font-weight:600;background:#3a1a1a;color:#f85149';
noshow.onclick=function(e){e.stopPropagation();logAction(workerDataRef,'failure',noshow)};
acts.appendChild(call);acts.appendChild(sms);acts.appendChild(noshow);w.appendChild(acts);
parent.appendChild(w);
}
function addStat(parent,n,l){
var s=document.createElement('div');s.className='stat';
var nn=document.createElement('div');nn.className='n';nn.textContent=typeof n==='number'?n.toLocaleString():n;
var ll=document.createElement('div');ll.className='l';ll.textContent=l;
s.appendChild(nn);s.appendChild(ll);parent.appendChild(s);
}
function pw(text){
var p=(text||'').split(/\u2014|\u2013|—/),nm=p[0]?p[0].trim():'',rest=p[1]?p[1].trim():'';
var rm=rest.match(/^(.+?) in (.+?)\./),sm=rest.match(/Skills: ([^.]+)/),cm=rest.match(/Certs?: ([^.]+)/);
var rr=rest.match(/Reliability: ([\d.]+)/),av=rest.match(/Availability: ([\d.]+)/),ar=rest.match(/Archetype: (\w+)/);
return{nm:nm,role:rm?rm[1]:'',loc:rm?rm[2]:'',
skills:sm?sm[1].split('|').filter(function(s){return s.trim()}):[],
certs:cm?cm[1].split('|').filter(function(c){return c.trim()&&c!=='none'}):[],
rel:rr?parseFloat(rr[1]):0,avail:av?parseFloat(av[1]):0,arch:ar?ar[1]:'',hasM:!!rr}
}
function doSearch(){
var q=document.getElementById('sq').value.trim();if(!q)return;
lastQuery=q;
var st=document.getElementById('sst').value,rl=document.getElementById('srl').value;
// Append dropdown filters to the query so the smart parser picks them up
var fullQ=q;
if(st&&q.indexOf(st)<0)fullQ+=' in '+st;
if(rl&&q.toLowerCase().indexOf(rl.toLowerCase())<0)fullQ+=' '+rl;
var out=document.getElementById('sresults');out.textContent='Finding the best matches...';
fetch(A+'/intelligence/chat',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({message:fullQ})
}).then(function(r){return r.json()}).then(function(d){
out.textContent='';
// Show what the system understood
if(d.understood&&d.understood.length){
var tags=document.createElement('div');tags.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px';
d.understood.forEach(function(u){
var tag=document.createElement('span');tag.style.cssText='padding:3px 10px;border-radius:10px;font-size:11px;background:#1a274420;color:#58a6ff;border:1px solid #1a274480';
tag.textContent=u;tags.appendChild(tag);
});
out.appendChild(tags);
}
var h=document.createElement('div');h.style.cssText='color:#8b949e;font-size:12px;margin-bottom:10px';
h.textContent=(d.sql_matches?d.sql_matches.toLocaleString()+' workers matched — ':'')+'showing best results ('+(d.duration_ms||0)+'ms)';
out.appendChild(h);
// Meta-index signal — ALWAYS render when the system has any memory,
// even if no trait crossed threshold. Silence here would have
// recruiters assume "no signal" when the reality is "threshold
// filtered it out" or "memory is sparse for this geo." Trust
// depends on the system being honest about what it doesn't know.
if(d.pattern_playbooks_matched > 0 || d.discovered_pattern){
var mem=document.createElement('div');
mem.style.cssText='background:#0d2818;border:1px solid #2ea04360;border-radius:6px;padding:8px 12px;margin-bottom:10px;font-size:11px;color:#86efac;line-height:1.5';
var label=document.createElement('span');label.style.cssText='color:#3fb950;font-weight:600;margin-right:6px';
label.textContent='MEMORY ('+(d.pattern_playbooks_matched||0)+' playbook'+(d.pattern_playbooks_matched===1?'':'s')+'):';
mem.appendChild(label);
var pattern = d.discovered_pattern || '';
if(!pattern || pattern.indexOf('No similar')>=0 || pattern.indexOf('0 workers')>=0){
mem.appendChild(document.createTextNode(' memory is sparse for this role+geo — no trait crossed threshold. Will accumulate as fills land.'));
mem.style.color='#6ca885';
} else {
mem.appendChild(document.createTextNode(' '+pattern));
}
out.appendChild(mem);
} else {
// Zero playbooks matched — be explicit
var mem0=document.createElement('div');
mem0.style.cssText='background:#161b22;border:1px solid #21262d;border-radius:6px;padding:6px 12px;margin-bottom:10px;font-size:11px;color:#6e7681';
mem0.textContent='MEMORY: no similar past playbooks yet — first fill of this kind will seed it.';
out.appendChild(mem0);
}
// Render results based on type
var workers=d.sql_results||[];
if(workers.length){
workers.forEach(function(w,i){
var wd={nm:w.name,role:w.role||'',loc:(w.city||'')+', '+(w.state||''),skills:(w.skills||'').split(',').filter(function(s){return s.trim()}),
certs:(w.certifications||'').split(',').filter(function(c){return c.trim()&&c.trim()!=='none'}),
rel:w.rel||0,avail:w.avail||0,arch:w.archetype||'',hasM:true};
var detail=[w.role,w.city+', '+w.state];
if(w.zip)detail.push('ZIP: '+w.zip);
var why='Reliability: '+Math.round((w.rel||0)*100)+'%';
if(w.avail)why+=' · Available: '+Math.round(w.avail*100)+'%';
if(w.archetype)why+=' · '+w.archetype;
// Derive and show implied pay rate client-side so the main search
// surface matches the live-contracts cards. Same formula as Bun.
var rate=computeImpliedPayRate(w.role,w.rel,w.archetype);
if(rate) why+=' · pay $'+rate.toFixed(2)+'/hr';
addWorkerInsight(out,w.name,detail.join(' · '),why,i,null,wd);
});
} else {
// Fall back to vector results
var vr=d.results||d.vector_results||[];
if(!vr.length){out.appendChild(document.createTextNode('No matches found. Try different terms.'));return}
vr.forEach(function(s,i){
var w=pw(s.text||s.chunk_text||'');if(!w.nm)w.nm=s.doc_id;
addWorkerInsight(out,w.nm,[w.role,w.loc].filter(Boolean).join(' · '),
(w.hasM?'Reliability: '+Math.round(w.rel*100)+'% · ':'')+(w.certs.length?'Certs: '+w.certs.join(', '):'AI match: '+Math.round((s.score||0)*100)+'%'),i,null,w);
});
}
}).catch(function(e){out.textContent='Error: '+e.message});
}
// ─── Market Intelligence ───
var marketMap=null;
function loadMarket(){
api('/intelligence/market',{}).then(function(d){
if(d.error||!d.major_permits)return;
var el=document.getElementById('market');el.textContent='';
var card=document.createElement('div');card.className='insight warning';
// Header with live indicator + source link + refresh
var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px';
var lb=document.createElement('div');lb.className='label';lb.style.cssText='font-size:9px;text-transform:uppercase;letter-spacing:1.5px;color:#484f58;display:flex;align-items:center;gap:8px';
lb.textContent='MARKET INTELLIGENCE';
var live=document.createElement('span');live.style.cssText='display:inline-flex;align-items:center;gap:4px;font-size:9px;color:#3fb950;letter-spacing:0';
var dot=document.createElement('span');dot.style.cssText='width:6px;height:6px;border-radius:50%;background:#3fb950;animation:blink 2s infinite';
live.appendChild(dot);live.appendChild(document.createTextNode('LIVE'));
lb.appendChild(live);
var rhs=document.createElement('div');rhs.style.cssText='display:flex;gap:8px;align-items:center';
var ts=document.createElement('span');ts.style.cssText='font-size:9px;color:#484f58';ts.textContent='Updated: '+new Date().toLocaleString();
var srcLink=document.createElement('a');srcLink.href='https://data.cityofchicago.org/Buildings/Building-Permits/ydr8-5enu';
srcLink.target='_blank';srcLink.style.cssText='font-size:9px;color:#58a6ff;text-decoration:none';srcLink.textContent='Verify source';
var refresh=document.createElement('button');refresh.style.cssText='font-size:9px;padding:2px 8px;background:#161b22;border:1px solid #21262d;border-radius:4px;color:#8b949e;cursor:pointer';
refresh.textContent='Refresh';refresh.onclick=function(){loadMarket()};
rhs.appendChild(ts);rhs.appendChild(srcLink);rhs.appendChild(refresh);
hdr.appendChild(lb);hdr.appendChild(rhs);card.appendChild(hdr);
var hl=document.createElement('div');hl.className='headline';hl.textContent='Chicago Construction Pipeline';
var sub=document.createElement('div');sub.className='sub';
sub.textContent='$'+(d.total_construction_value/1e9).toFixed(1)+'B in active permits → '+d.total_estimated_workers.toLocaleString()+' workers needed · Fetched in '+d.duration_ms+'ms';
card.appendChild(hl);card.appendChild(sub);
// MAP — real lat/lng from permit data
var mapWrap=document.createElement('div');mapWrap.style.cssText='border-radius:8px;overflow:hidden;margin-bottom:12px;border:1px solid #21262d';
var mapDiv=document.createElement('div');mapDiv.id='permit-map';mapDiv.style.cssText='height:280px;width:100%;background:#0d1117';
mapWrap.appendChild(mapDiv);card.appendChild(mapWrap);
// Legend
var legend=document.createElement('div');legend.style.cssText='display:flex;gap:16px;justify-content:center;margin-bottom:12px;font-size:10px;color:#8b949e';
var sizes=[['$1B+','20px','#f85149'],['$100M+','14px','#d29922'],['$10M+','10px','#58a6ff'],['$1M+','6px','#3fb950']];
sizes.forEach(function(s){
var item=document.createElement('span');item.style.cssText='display:flex;align-items:center;gap:4px';
var circ=document.createElement('span');circ.style.cssText='width:'+s[1]+';height:'+s[1]+';border-radius:50%;background:'+s[2]+';opacity:0.7;display:inline-block';
item.appendChild(circ);item.appendChild(document.createTextNode(s[0]));legend.appendChild(item);
});
card.appendChild(legend);
// Major permits list
var ph=document.createElement('div');ph.style.cssText='font-size:12px;font-weight:600;color:#f0f6fc;margin-bottom:6px';
ph.textContent='Largest Active Projects';card.appendChild(ph);
d.major_permits.slice(0,5).forEach(function(p){
var row=document.createElement('div');row.style.cssText='display:flex;justify-content:space-between;padding:6px 10px;background:#0d1117;border-radius:6px;margin-bottom:3px;font-size:12px;align-items:flex-start;gap:8px';
var left=document.createElement('div');left.style.cssText='flex:1;min-width:0';
var desc=document.createElement('div');desc.style.cssText='color:#f0f6fc;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis';
desc.textContent=p.description||p.type||'Construction';
var addr=document.createElement('div');addr.style.cssText='color:#484f58;font-size:10px;margin-top:1px';
addr.textContent=p.address+' · '+p.date;
left.appendChild(desc);left.appendChild(addr);
var cost=document.createElement('div');cost.style.cssText='color:#d29922;font-weight:700;font-size:13px;flex-shrink:0';
cost.textContent=p.cost>=1e9?'$'+(p.cost/1e9).toFixed(1)+'B':p.cost>=1e6?'$'+(p.cost/1e6).toFixed(0)+'M':'$'+(p.cost/1e3).toFixed(0)+'K';
row.appendChild(left);row.appendChild(cost);card.appendChild(row);
});
// Bench vs demand
if(d.gaps&&d.gaps.length){
var gh=document.createElement('div');gh.style.cssText='font-size:12px;font-weight:600;color:#f0f6fc;margin:10px 0 6px';
gh.textContent='Your Bench vs. Market Demand (Illinois)';card.appendChild(gh);
var seen={};
d.gaps.forEach(function(g){
if(seen[g.role])return;seen[g.role]=true;
var row=document.createElement('div');row.style.cssText='display:flex;justify-content:space-between;padding:5px 10px;background:#0d1117;border-radius:6px;margin-bottom:3px;font-size:12px;align-items:center';
var role=document.createElement('span');role.style.cssText='color:#f0f6fc;font-weight:500';role.textContent=g.role;
var nums=document.createElement('span');nums.style.cssText='font-size:11px;color:'+(g.available>g.demand?'#3fb950':'#d29922');
nums.textContent=g.available.toLocaleString()+' available / '+g.reliable.toLocaleString()+' reliable ('+g.supply.toLocaleString()+' total)';
row.appendChild(role);row.appendChild(nums);card.appendChild(row);
});
}
var insight=document.createElement('div');insight.style.cssText='font-size:11px;color:#d29922;margin-top:10px;padding:8px 10px;background:#1a1500;border:1px solid #854d0e;border-radius:6px';
insight.textContent='Live from City of Chicago Open Data. Click "Verify source" to see the raw permit database. Each dot is a real permitted project — hover for details. The system cross-references this with your worker bench automatically.';
card.appendChild(insight);
el.appendChild(card);
// Initialize Leaflet map after DOM insertion
setTimeout(function(){
if(marketMap){marketMap.remove();marketMap=null}
marketMap=L.map('permit-map',{zoomControl:true,attributionControl:false}).setView([41.88,-87.7],11);
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',{maxZoom:18}).addTo(marketMap);
// Plot permits as circles sized by cost
d.major_permits.forEach(function(p){
if(!p.lat||!p.lng)return;
var cost=p.cost||0;
var radius=cost>=1e9?20:cost>=1e8?14:cost>=1e7?10:6;
var color=cost>=1e9?'#f85149':cost>=1e8?'#d29922':cost>=1e7?'#58a6ff':'#3fb950';
var costLabel=cost>=1e9?'$'+(cost/1e9).toFixed(1)+'B':cost>=1e6?'$'+(cost/1e6).toFixed(0)+'M':'$'+(cost/1e3).toFixed(0)+'K';
var circle=L.circleMarker([parseFloat(p.lat),parseFloat(p.lng)],{
radius:radius,fillColor:color,color:color,weight:1,opacity:0.8,fillOpacity:0.5
}).addTo(marketMap);
circle.bindPopup('<div style="font-size:12px;max-width:250px"><strong>'+costLabel+'</strong><br>'+
(p.description||'Construction').substring(0,120)+'<br><span style="color:#888">'+p.address+' · '+p.date+'</span></div>');
});
},100);
}).catch(function(e){
var el=document.getElementById('market');
el.textContent='Market data unavailable: '+e.message;
});
}
// ─── Learning Loop ───
// Real recruiter actions feed the Phase 19 feedback chain directly:
// Call/SMS → /log → /vectors/playbook_memory/seed (positive endorsement)
// No-show → /log_failure → /vectors/playbook_memory/mark_failed (penalty)
// Every click trains the system; the next search boosts/dampens accordingly.
function logAction(workerData, kind, btnEl){
if(!workerData)return;
var role=workerData.role||'Worker';
var city=(workerData.loc||'').split(',')[0].trim();
var state=(workerData.loc||'').split(',').pop().trim();
if(!city||!state){flashBtn(btnEl,'no geo');return;}
var op='fill: '+role+' x1 in '+city+', '+state;
if(kind==='failure'){
fetch(A+'/log_failure',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({operation:op,failed_names:[workerData.nm],reason:'marked no-show via UI'})
}).then(function(r){return r.json()}).then(function(d){
flashBtn(btnEl, d&&d.marked?'Flagged':'Ghost');
loadLearning();
}).catch(function(){flashBtn(btnEl,'err')});
} else {
fetch(A+'/log',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({operation:op,approach:kind+' from UI',
result:'1/1 filled → '+workerData.nm,
context:'client=ui query='+(lastQuery||'(direct)').slice(0,40)})
}).then(function(r){return r.json()}).then(function(d){
flashBtn(btnEl, d&&d.seeded?'Logged':'Ghost');
loadLearning();
}).catch(function(){flashBtn(btnEl,'err')});
}
}
function flashBtn(btn,label){
if(!btn)return;
var old=btn.textContent;btn.textContent=label;btn.disabled=true;
setTimeout(function(){btn.textContent=old;btn.disabled=false},1400);
}
// Back-compat shim — any legacy caller still pointing at logSelection.
function logSelection(workerData){ logAction(workerData, 'call', null); }
function loadLearning(){
api('/intelligence/activity',{}).then(function(d){
var el=document.getElementById('learning');
el.textContent='';
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';
var lb=document.createElement('div');lb.className='label';lb.textContent='SYSTEM LEARNING';
var hl=document.createElement('div');hl.className='headline';hl.textContent='The System Gets Smarter With Every Use';
var sub=document.createElement('div');sub.className='sub';
sub.textContent='Every search, every placement, every simulation teaches the system what works. '+total+' operations logged so far.';
card.appendChild(lb);card.appendChild(hl);card.appendChild(sub);
// Stats row
var stats=document.createElement('div');stats.style.cssText='display:flex;gap:16px;margin-bottom:12px';
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
if(d.learned_patterns&&d.learned_patterns.length){
var ph=document.createElement('div');ph.style.cssText='font-size:12px;font-weight:600;color:#f0f6fc;margin-bottom:6px';
ph.textContent='Learned Search Patterns';card.appendChild(ph);
d.learned_patterns.slice(0,5).forEach(function(p){
var row=document.createElement('div');row.style.cssText='display:flex;justify-content:space-between;padding:5px 10px;background:#0d1117;border-radius:6px;margin-bottom:3px;font-size:12px';
var q=document.createElement('span');q.style.color='#c9d1d9';q.textContent='"'+p.query+'"';
var c=document.createElement('span');c.style.cssText='color:#58a6ff;font-weight:600';c.textContent=p.times+'x';
row.appendChild(q);row.appendChild(c);card.appendChild(row);
});
}
// Recent activity feed
if(d.playbooks&&d.playbooks.length){
var ah=document.createElement('div');ah.style.cssText='font-size:12px;font-weight:600;color:#f0f6fc;margin:10px 0 6px';
ah.textContent='Recent Activity';card.appendChild(ah);
d.playbooks.slice(0,5).forEach(function(p){
var row=document.createElement('div');row.style.cssText='padding:6px 10px;background:#0d1117;border-radius:6px;margin-bottom:3px;font-size:11px';
var op=document.createElement('div');op.style.color='#f0f6fc';op.textContent=p.operation||'';
var det=document.createElement('div');det.style.cssText='color:#484f58;margin-top:2px';
det.textContent=(p.result||'')+(p.context?' · '+p.context:'');
var ts=document.createElement('div');ts.style.cssText='color:#2d333b;font-size:10px;margin-top:2px';
ts.textContent=p.timestamp?new Date(p.timestamp).toLocaleString():'';
row.appendChild(op);row.appendChild(det);row.appendChild(ts);card.appendChild(row);
});
}
// Explainer
var ex=document.createElement('div');ex.style.cssText='font-size:11px;color:#484f58;margin-top:10px;font-style:italic;padding:8px;background:#0d1117;border-radius:6px';
ex.textContent='Every time you search and select a worker, the system records what worked. Over time, it learns which workers are best for which situations — turning your decisions into institutional knowledge that never leaves when a staffer does.';
card.appendChild(ex);
el.appendChild(card);
}).catch(function(){});
}
function addLearnStat(parent,n,label,color){
var d=document.createElement('div');d.style.cssText='text-align:center;flex:1';
var num=document.createElement('div');num.style.cssText='font-size:24px;font-weight:800;color:'+color;num.textContent=n;
var lb=document.createElement('div');lb.style.cssText='font-size:10px;color:#484f58';lb.textContent=label;
d.appendChild(num);d.appendChild(lb);parent.appendChild(d);
}
</script></body></html>