lakehouse/tests/multi-agent/gen_staffer_demo.ts
root 6b71c8e9b2 Phase 23 — contract terms + staffer identity + competence-weighted retrieval
Matrix-index the "who handled this" dimension so top staffers become
the training signal and juniors inherit their playbooks automatically
via the boost pipeline. Auto-discovered indicators emerge from
comparing trajectories across staffers on similar contracts — that was
always the architectural point; this wires the last piece.

ContractTerms:
- deadline, budget_total_usd, budget_per_hour_max, local_bonus_per_hour,
  local_bonus_radius_mi, fill_requirement ("paramount" | "preferred")
- Attached to ScenarioSpec, propagated into T3 checkpoint + cloud
  rescue prompts so cloud reasons about trade-offs (pivot within bonus
  radius first; respect per-hour cap; split across cities when
  fill_requirement=paramount).

Staffer:
- {id, name, tenure_months, role: senior|mid|junior|trainee}
- On ScenarioSpec; logged at scenario start; attached to KB outcome
- Recomputed StafferStats written to data/_kb/staffers.jsonl after
  every run: total_runs, fill_rate, avg_turns, avg_citations,
  rescue_rate, competence_score.
- Competence formula: 0.45*fill_rate + 0.20*turn_efficiency +
  0.20*citation_density + 0.15*rescue_rate. Normalized to 0..1.

findNeighbors now returns weighted_score = cosine × best_staffer_competence
(floored at 0.3 so high-similarity low-competence neighbors still
surface). pathway_recommender prompt shows the top staffer's identity
so cloud knows WHOSE playbook it's synthesizing from.

Demo infrastructure:
- tests/multi-agent/gen_staffer_demo.ts: 4 personas (Maria senior,
  James mid, Sam junior, Alex trainee) × 3 contracts (Nashville Welder,
  Joliet Warehouse, Indianapolis Assembly). 12 scenarios total.
- scripts/run_staffer_demo.sh: runs the 12 sequentially with
  LH_OVERVIEW_CLOUD=1. Post-run calls kb_staffer_report.py.
- scripts/kb_staffer_report.py: leaderboard + cross-staffer worker
  overlap (names endorsed by ≥2 staffers → auto-discovered high-value
  workers). Top vs bottom differential.

gen_scenarios.ts (Phase 22 generator) also now emits contract terms
on 70% of generated specs — future KB batches populate with realistic
constraint patterns instead of bare role+city+count.

Stress scenario from item A intentionally NOT the production test.
Real staffing has constraints; Nashville contract + staffer demo is
the honest test of whether the architecture produces measurable
differential between coordinator skill levels.

Demo batch launched — 12 runs × ~3min each ≈ 40min unattended. Report
emitted after batch.
2026-04-20 22:16:09 -05:00

121 lines
4.9 KiB
TypeScript
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.

// Phase 23 demo — 4 staffer personas × 3 contract scenarios each.
// Same contracts run against different staffers to measure competence
// differential. After the batch, findNeighbors should rank top-staffer
// playbooks above junior-staffer playbooks for similar scenarios.
//
// Output: 12 spec files under tests/multi-agent/scenarios/staffer_demo/
import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
const STAFFERS = [
{ id: "S-001", name: "Maria Chen", tenure_months: 48, role: "senior" as const },
{ id: "S-002", name: "James Park", tenure_months: 14, role: "mid" as const },
{ id: "S-003", name: "Sam Torres", tenure_months: 4, role: "junior" as const },
{ id: "S-004", name: "Alex Rivera", tenure_months: 1, role: "trainee" as const },
];
// Three contract shapes — one downtown assembly, one warehouse ramp,
// one emergency recovery. Different cities to vary the sig_hash.
const CONTRACTS = [
{
tag: "nashville_downtown",
client: "Riverline Logistics — Nashville Downtown Build-Out",
city: "Nashville", state: "TN",
contract: {
deadline: "2026-05-19",
budget_total_usd: 180000,
budget_per_hour_max: 32,
local_bonus_per_hour: 4,
local_bonus_radius_mi: 75,
fill_requirement: "paramount" as const,
},
events: [
{ kind: "baseline_fill", at: "07:00", role: "Welder", count: 4 },
{ kind: "expansion", at: "08:30", role: "Packaging Operator", count: 6 },
{ kind: "baseline_fill", at: "09:00", role: "Shipping Clerk", count: 2 },
{ kind: "emergency", at: "13:00", role: "Welder", count: 2, deadline: "15:00" },
{ kind: "misplacement", at: "15:30", role: "Packaging Operator", count: 1, replaces_event: "08:30" },
],
},
{
tag: "joliet_warehouse",
client: "Midway Distribution — Joliet DC Ramp",
city: "Joliet", state: "IL",
contract: {
deadline: "2026-05-12",
budget_total_usd: 120000,
budget_per_hour_max: 28,
local_bonus_per_hour: 3,
local_bonus_radius_mi: 50,
fill_requirement: "preferred" as const,
},
events: [
{ kind: "baseline_fill", at: "07:00", role: "Warehouse Associate", count: 5 },
{ kind: "recurring", at: "10:00", role: "Forklift Operator", count: 3 },
{ kind: "expansion", at: "12:30", role: "Picker", count: 4 },
{ kind: "misplacement", at: "15:00", role: "Forklift Operator", count: 1, replaces_event: "10:00" },
],
},
{
tag: "indianapolis_assembly",
client: "Pioneer Assembly — Indianapolis Plant Expansion",
city: "Indianapolis", state: "IN",
contract: {
deadline: "2026-05-26",
budget_total_usd: 220000,
budget_per_hour_max: 30,
local_bonus_per_hour: 5,
local_bonus_radius_mi: 60,
fill_requirement: "paramount" as const,
},
events: [
{ kind: "baseline_fill", at: "07:30", role: "Assembler", count: 6 },
{ kind: "recurring", at: "09:30", role: "Quality Tech", count: 2 },
{ kind: "expansion", at: "11:00", role: "Machine Operator", count: 5 },
{ kind: "emergency", at: "14:00", role: "Machine Operator", count: 3, deadline: "16:00" },
{ kind: "misplacement", at: "16:00", role: "Assembler", count: 1, replaces_event: "07:30" },
],
},
];
async function main() {
const outDir = "tests/multi-agent/scenarios/staffer_demo";
await mkdir(outDir, { recursive: true });
const manifest: Array<{ file: string; staffer: string; contract: string; client: string }> = [];
let day = 0;
for (const staffer of STAFFERS) {
for (const ct of CONTRACTS) {
day += 1;
const date = new Date(Date.now() + day * 86400000).toISOString().split("T")[0];
const spec = {
client: ct.client,
date,
contract: ct.contract,
staffer,
events: ct.events.map(e => ({
...e,
city: ct.city,
state: ct.state,
shift_start: `${e.at} ${e.at.startsWith("0") ? "AM" : "PM"}`,
scenario_note: `Staffed by ${staffer.name} (${staffer.role}, ${staffer.tenure_months}mo). Contract deadline ${ct.contract.deadline}, fill=${ct.contract.fill_requirement}.`,
})),
};
const fname = `${staffer.id}_${ct.tag}.json`;
await writeFile(join(outDir, fname), JSON.stringify(spec, null, 2));
manifest.push({ file: fname, staffer: staffer.name, contract: ct.tag, client: ct.client });
}
}
await writeFile(
join(outDir, "manifest.json"),
JSON.stringify({ count: manifest.length, scenarios: manifest }, null, 2),
);
console.log(`${manifest.length} staffer-demo specs → ${outDir}/`);
for (const m of manifest) console.log(` ${m.file}${m.staffer} × ${m.contract}`);
}
main().catch(e => {
console.error("gen_staffer_demo failed:", (e as Error).message);
process.exit(1);
});