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.
This commit is contained in:
parent
a7fc8e2256
commit
6b71c8e9b2
108
scripts/kb_staffer_report.py
Executable file
108
scripts/kb_staffer_report.py
Executable file
@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Phase 23 staffer leaderboard + cross-staffer pattern finder.
|
||||
|
||||
Reads data/_kb/staffers.jsonl + outcomes.jsonl + signatures.jsonl and
|
||||
emits:
|
||||
- Leaderboard sorted by competence_score
|
||||
- Per-staffer breakdown (fill rate, turns, citations, rescue rate)
|
||||
- Cross-staffer common workers: names endorsed by multiple top
|
||||
staffers on similar signatures — candidates for "auto-discovered
|
||||
high-value worker" labels.
|
||||
|
||||
Run after scripts/run_staffer_demo.sh completes.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from collections import defaultdict, Counter
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
KB = ROOT / "data" / "_kb"
|
||||
PLAYBOOKS = ROOT / "tests" / "multi-agent" / "playbooks"
|
||||
|
||||
|
||||
def load_jsonl(p):
|
||||
if not p.exists():
|
||||
return []
|
||||
out = []
|
||||
for line in p.read_text().splitlines():
|
||||
if line.strip():
|
||||
try:
|
||||
out.append(json.loads(line))
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def main():
|
||||
staffers = load_jsonl(KB / "staffers.jsonl")
|
||||
outcomes = load_jsonl(KB / "outcomes.jsonl")
|
||||
|
||||
if not staffers:
|
||||
print("(no staffer stats yet — run scripts/run_staffer_demo.sh first)")
|
||||
return
|
||||
|
||||
staffers.sort(key=lambda s: -s["competence_score"])
|
||||
|
||||
print("=== STAFFER LEADERBOARD ===")
|
||||
print(f"{'rank':4s} {'id':7s} {'name':16s} {'role':8s} {'mo':>4s} {'runs':>4s} {'fill':>6s} {'turns':>6s} {'cites':>6s} {'rescue':>7s} {'score':>6s}")
|
||||
print("-" * 100)
|
||||
for i, s in enumerate(staffers):
|
||||
rank_mark = "★" if i == 0 else " "
|
||||
print(f"{rank_mark}{i+1:3d} {s['id']:7s} {s['name']:16s} {s['role']:8s} {s['tenure_months']:>4d} {s['total_runs']:>4d} {s['fill_rate']*100:>5.1f}% {s['avg_turns_per_event']:>6.1f} {s['avg_citations_per_run']:>6.2f} {s['rescue_rate']*100:>6.1f}% {s['competence_score']:>6.3f}")
|
||||
print()
|
||||
|
||||
# Cross-staffer pattern — workers endorsed on the same sig_hash
|
||||
# across multiple staffers. Auto-discovered "reliable performers".
|
||||
print("=== CROSS-STAFFER WORKER OVERLAP ===")
|
||||
print("(workers endorsed on same sig_hash by ≥2 staffers)")
|
||||
worker_touches = defaultdict(lambda: {"staffers": set(), "sigs": set(), "endorsements": 0})
|
||||
for o in outcomes:
|
||||
run_dir = PLAYBOOKS / o["run_id"]
|
||||
results_file = run_dir / "results.json"
|
||||
if not results_file.exists():
|
||||
continue
|
||||
try:
|
||||
results = json.loads(results_file.read_text())
|
||||
except Exception:
|
||||
continue
|
||||
staffer_id = o.get("staffer", {}).get("id")
|
||||
if not staffer_id:
|
||||
continue
|
||||
for r in results:
|
||||
if not r.get("ok"):
|
||||
continue
|
||||
for f in r.get("fills", []):
|
||||
name = f.get("name")
|
||||
if not name or name.startswith("Candidate "):
|
||||
continue
|
||||
key = (name, r["event"]["role"], r["event"]["city"], r["event"]["state"])
|
||||
worker_touches[key]["staffers"].add(staffer_id)
|
||||
worker_touches[key]["sigs"].add(o["sig_hash"])
|
||||
worker_touches[key]["endorsements"] += 1
|
||||
shared = sorted(
|
||||
[(k, v) for k, v in worker_touches.items() if len(v["staffers"]) >= 2],
|
||||
key=lambda x: -x[1]["endorsements"],
|
||||
)
|
||||
if shared:
|
||||
for (name, role, city, state), v in shared[:15]:
|
||||
print(f" {name:25s} {role:22s} {city:15s} {state}: {v['endorsements']} endorsements across {len(v['staffers'])} staffers")
|
||||
else:
|
||||
print(" (none yet — needs ≥2 staffers on overlapping scenarios)")
|
||||
print()
|
||||
|
||||
# Competence differential — how much does top vs bottom differ?
|
||||
if len(staffers) >= 2:
|
||||
top = staffers[0]
|
||||
bot = staffers[-1]
|
||||
print(f"=== TOP vs BOTTOM DIFFERENTIAL ===")
|
||||
print(f"{top['name']} (top) vs {bot['name']} (bottom):")
|
||||
print(f" fill rate: {top['fill_rate']*100:.1f}% vs {bot['fill_rate']*100:.1f}% (Δ {(top['fill_rate']-bot['fill_rate'])*100:+.1f}pt)")
|
||||
print(f" avg turns: {top['avg_turns_per_event']:.1f} vs {bot['avg_turns_per_event']:.1f} (Δ {top['avg_turns_per_event']-bot['avg_turns_per_event']:+.1f})")
|
||||
print(f" avg citations: {top['avg_citations_per_run']:.2f} vs {bot['avg_citations_per_run']:.2f} (Δ {top['avg_citations_per_run']-bot['avg_citations_per_run']:+.2f})")
|
||||
print(f" rescue rate: {top['rescue_rate']*100:.1f}% vs {bot['rescue_rate']*100:.1f}%")
|
||||
print(f" competence: {top['competence_score']:.3f} vs {bot['competence_score']:.3f}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
46
scripts/run_staffer_demo.sh
Executable file
46
scripts/run_staffer_demo.sh
Executable file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run the 12 staffer-demo scenarios (4 personas × 3 contracts) with
|
||||
# cloud T3 enabled. After each run, KB indexes the outcome and
|
||||
# recomputes that staffer's competence_score. By the end, the top
|
||||
# staffer's playbooks will be surfacing first in neighbor retrieval.
|
||||
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
export OLLAMA_CLOUD_KEY="$(python3 -c "import json; print(json.load(open('/root/llm_team_config.json'))['providers']['ollama_cloud']['api_key'])" 2>/dev/null || echo '')"
|
||||
|
||||
MANIFEST="tests/multi-agent/scenarios/staffer_demo/manifest.json"
|
||||
if [ ! -f "$MANIFEST" ]; then
|
||||
echo "✗ no manifest — run: bun tests/multi-agent/gen_staffer_demo.ts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
START_TS=$(date -Iseconds)
|
||||
LOG_DIR="/tmp/lakehouse_staffer_demo_$(date +%s)"
|
||||
mkdir -p "$LOG_DIR"
|
||||
echo "▶ Staffer demo start: $START_TS, logs → $LOG_DIR"
|
||||
|
||||
python3 -c "
|
||||
import json
|
||||
m = json.load(open('$MANIFEST'))
|
||||
for s in m['scenarios']:
|
||||
print(s['file'], '|', s['staffer'], '|', s['contract'])
|
||||
" | while IFS='|' read -r SCEN STAFFER CONTRACT; do
|
||||
SCEN=$(echo "$SCEN" | xargs)
|
||||
STAFFER=$(echo "$STAFFER" | xargs)
|
||||
CONTRACT=$(echo "$CONTRACT" | xargs)
|
||||
SPEC="tests/multi-agent/scenarios/staffer_demo/$SCEN"
|
||||
BASE=$(basename "$SPEC" .json)
|
||||
LOG="$LOG_DIR/${BASE}.log"
|
||||
echo " ▶ $STAFFER × $CONTRACT"
|
||||
LH_OVERVIEW_CLOUD=1 bun tests/multi-agent/scenario.ts "$SPEC" > "$LOG" 2>&1 || true
|
||||
OK=$(grep -oP '\d+/\d+ events succeeded' "$LOG" | tail -1 || echo "no-result")
|
||||
RESCUES=$(grep -c "cloud rescue requested" "$LOG" || true)
|
||||
RESCUE_OK=$(grep -c "retry outcome: ✓" "$LOG" || true)
|
||||
SIG=$(grep -oP 'KB indexed: sig=\K[a-f0-9]+' "$LOG" | tail -1 || echo "-")
|
||||
echo " → $OK; rescues=$RESCUES (${RESCUE_OK} succeeded); sig=$SIG"
|
||||
done
|
||||
|
||||
echo "▶ Staffer demo done: $(date -Iseconds)"
|
||||
echo "▶ Staffer competence leaderboard:"
|
||||
python3 scripts/kb_staffer_report.py 2>/dev/null || echo "(run scripts/kb_staffer_report.py after batch completes)"
|
||||
@ -147,7 +147,30 @@ function genSpec(rng: () => number, id: number): any {
|
||||
if (events.length > 0) e.replaces_event = events[0].at;
|
||||
events.push(e);
|
||||
}
|
||||
return { client, date, events };
|
||||
|
||||
// Contract terms — most real staffing contracts have these. 70% of
|
||||
// generated specs carry them so KB + T3 learn to reason about budget
|
||||
// and radius trade-offs, not just geography. Distributions are
|
||||
// deliberately varied: 2 week to 45 day deadlines, $22-$38/hr caps,
|
||||
// 25-150mi local radii.
|
||||
const contract = rng() > 0.3 ? (() => {
|
||||
const deadlineDays = 10 + Math.floor(rng() * 35);
|
||||
const deadlineDate = new Date(today.getTime() + (id + deadlineDays) * 86400000)
|
||||
.toISOString().split("T")[0];
|
||||
const budgetPerHour = 22 + Math.floor(rng() * 17);
|
||||
const bonusRadius = 25 + Math.floor(rng() * 125);
|
||||
const bonusPerHour = 2 + Math.floor(rng() * 5);
|
||||
const fill: "paramount" | "preferred" = rng() > 0.4 ? "paramount" : "preferred";
|
||||
return {
|
||||
deadline: deadlineDate,
|
||||
budget_per_hour_max: budgetPerHour,
|
||||
local_bonus_per_hour: bonusPerHour,
|
||||
local_bonus_radius_mi: bonusRadius,
|
||||
fill_requirement: fill,
|
||||
};
|
||||
})() : undefined;
|
||||
|
||||
return { client, date, events, ...(contract ? { contract } : {}) };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
120
tests/multi-agent/gen_staffer_demo.ts
Normal file
120
tests/multi-agent/gen_staffer_demo.ts
Normal file
@ -0,0 +1,120 @@
|
||||
// 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);
|
||||
});
|
||||
@ -34,6 +34,7 @@ const OUTCOMES_FILE = "outcomes.jsonl";
|
||||
const CONFIG_SNAPSHOTS_FILE = "config_snapshots.jsonl";
|
||||
const ERROR_CORRECTIONS_FILE = "error_corrections.jsonl";
|
||||
const RECOMMENDATIONS_FILE = "pathway_recommendations.jsonl";
|
||||
const STAFFERS_FILE = "staffers.jsonl";
|
||||
|
||||
// What a playbook signature looks like — stable hash of the scenario
|
||||
// shape that ignores timestamps and specific worker IDs. Two runs with
|
||||
@ -60,11 +61,19 @@ export interface RunOutcome {
|
||||
overview: string;
|
||||
overview_cloud: boolean;
|
||||
};
|
||||
staffer?: { // Phase 23 — who ran this scenario
|
||||
id: string;
|
||||
name: string;
|
||||
tenure_months: number;
|
||||
role: string;
|
||||
};
|
||||
ok_events: number;
|
||||
total_events: number;
|
||||
total_turns: number;
|
||||
total_gap_signals: number;
|
||||
total_citations: number;
|
||||
rescue_attempts: number; // Phase 22 item B
|
||||
rescue_successes: number;
|
||||
per_event: Array<{
|
||||
at: string;
|
||||
kind: string;
|
||||
@ -78,6 +87,27 @@ export interface RunOutcome {
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
// Per-staffer aggregate. Recomputed from outcomes.jsonl after every
|
||||
// run. Competence_score weights neighbor retrieval so Senior staffers'
|
||||
// playbooks rank higher for similar scenarios than Junior staffers'.
|
||||
export interface StafferStats {
|
||||
id: string;
|
||||
name: string;
|
||||
tenure_months: number;
|
||||
role: string;
|
||||
total_runs: number;
|
||||
total_events_attempted: number;
|
||||
total_events_ok: number;
|
||||
fill_rate: number; // ok / attempted
|
||||
avg_turns_per_event: number;
|
||||
avg_citations_per_run: number;
|
||||
rescue_attempts: number;
|
||||
rescue_successes: number;
|
||||
rescue_rate: number;
|
||||
competence_score: number; // 0.0 - 1.0, see formula below
|
||||
last_updated: string;
|
||||
}
|
||||
|
||||
// The AI-synthesized recommendation written back for future runs.
|
||||
export interface PathwayRecommendation {
|
||||
sig_hash: string;
|
||||
@ -142,7 +172,7 @@ async function readJsonl<T>(file: string): Promise<T[]> {
|
||||
// embedding, appends outcome, updates or inserts signature entry.
|
||||
export async function indexRun(
|
||||
scenarioDir: string,
|
||||
spec: { client: string; date: string; events: Array<any> },
|
||||
spec: { client: string; date: string; events: Array<any>; staffer?: { id: string; name: string; tenure_months: number; role: string } },
|
||||
models: { executor: string; reviewer: string; overview: string; overview_cloud: boolean },
|
||||
elapsed_secs: number,
|
||||
): Promise<{ sig_hash: string; outcome: RunOutcome }> {
|
||||
@ -154,16 +184,21 @@ export async function indexRun(
|
||||
// Read results.json produced by scenario.ts to build the outcome record.
|
||||
const resultsRaw = await readFile(join(scenarioDir, "results.json"), "utf8");
|
||||
const results = JSON.parse(resultsRaw) as any[];
|
||||
const rescue_attempts = results.filter(r => r.retry_remediation).length;
|
||||
const rescue_successes = results.filter(r => r.retry_remediation && r.ok).length;
|
||||
const outcome: RunOutcome = {
|
||||
sig_hash,
|
||||
run_id: scenarioDir.split("/").pop() ?? scenarioDir,
|
||||
date: spec.date,
|
||||
models,
|
||||
staffer: spec.staffer,
|
||||
ok_events: results.filter(r => r.ok).length,
|
||||
total_events: results.length,
|
||||
total_turns: results.reduce((s, r) => s + (r.turns ?? 0), 0),
|
||||
total_gap_signals: results.reduce((s, r) => s + (r.gap_signals?.length ?? 0), 0),
|
||||
total_citations: results.reduce((s, r) => s + (r.playbook_citations?.length ?? 0), 0),
|
||||
rescue_attempts,
|
||||
rescue_successes,
|
||||
per_event: results.map(r => ({
|
||||
at: r.event.at,
|
||||
kind: r.event.kind,
|
||||
@ -178,6 +213,13 @@ export async function indexRun(
|
||||
};
|
||||
await appendFile(join(KB_DIR, OUTCOMES_FILE), JSON.stringify(outcome) + "\n");
|
||||
|
||||
// Phase 23 — recompute this staffer's aggregate + competence_score.
|
||||
// Cheap until we have thousands of runs; swap to streaming aggregate
|
||||
// when that becomes a concern.
|
||||
if (spec.staffer) {
|
||||
await recomputeStafferStats(spec.staffer.id);
|
||||
}
|
||||
|
||||
// Embed + upsert signature. Skip if embedding fails — the outcome is
|
||||
// still persisted, just without neighbor-search hookup.
|
||||
try {
|
||||
@ -221,6 +263,72 @@ export async function indexRun(
|
||||
return { sig_hash, outcome };
|
||||
}
|
||||
|
||||
// Phase 23 — per-staffer aggregate, recomputed from scratch on each
|
||||
// run. Writes the full table to STAFFERS_FILE (not append-only; we
|
||||
// always want the current-state snapshot). Competence score is bounded
|
||||
// 0..1 and combines four dimensions:
|
||||
// fill_rate — how often the staffer completes their events
|
||||
// turn_efficiency — lower turn counts are better
|
||||
// citation_density — signals use of playbook_memory feedback
|
||||
// rescue_rate — cloud rescue successes per attempt (doesn't
|
||||
// penalize staffers who never needed one)
|
||||
// Weights: fill 0.45, turn_eff 0.20, citation 0.20, rescue 0.15.
|
||||
// Rationale: completing the job matters most; everything else is style.
|
||||
export async function recomputeStafferStats(staffer_id: string): Promise<StafferStats | null> {
|
||||
const outcomes = await readJsonl<RunOutcome>(OUTCOMES_FILE);
|
||||
const mine = outcomes.filter(o => o.staffer?.id === staffer_id);
|
||||
if (mine.length === 0) return null;
|
||||
const latest = mine[mine.length - 1].staffer!;
|
||||
const total_events_attempted = mine.reduce((s, o) => s + o.total_events, 0);
|
||||
const total_events_ok = mine.reduce((s, o) => s + o.ok_events, 0);
|
||||
const total_turns = mine.reduce((s, o) => s + o.total_turns, 0);
|
||||
const total_citations = mine.reduce((s, o) => s + o.total_citations, 0);
|
||||
const rescue_attempts = mine.reduce((s, o) => s + (o.rescue_attempts ?? 0), 0);
|
||||
const rescue_successes = mine.reduce((s, o) => s + (o.rescue_successes ?? 0), 0);
|
||||
const fill_rate = total_events_attempted > 0 ? total_events_ok / total_events_attempted : 0;
|
||||
const avg_turns_per_event = total_events_attempted > 0 ? total_turns / total_events_attempted : 0;
|
||||
const avg_citations_per_run = mine.length > 0 ? total_citations / mine.length : 0;
|
||||
const rescue_rate = rescue_attempts > 0 ? rescue_successes / rescue_attempts : 1.0; // never-rescued defaults to perfect
|
||||
// Normalize each axis to 0..1. Caps chosen from observed distributions.
|
||||
const turn_eff = avg_turns_per_event === 0 ? 0 : Math.max(0, 1 - Math.min(avg_turns_per_event / 10, 1));
|
||||
const cite_norm = Math.min(avg_citations_per_run / 3, 1);
|
||||
const competence_score = 0.45 * fill_rate
|
||||
+ 0.20 * turn_eff
|
||||
+ 0.20 * cite_norm
|
||||
+ 0.15 * rescue_rate;
|
||||
const stats: StafferStats = {
|
||||
id: staffer_id,
|
||||
name: latest.name,
|
||||
tenure_months: latest.tenure_months,
|
||||
role: latest.role,
|
||||
total_runs: mine.length,
|
||||
total_events_attempted,
|
||||
total_events_ok,
|
||||
fill_rate,
|
||||
avg_turns_per_event,
|
||||
avg_citations_per_run,
|
||||
rescue_attempts,
|
||||
rescue_successes,
|
||||
rescue_rate,
|
||||
competence_score,
|
||||
last_updated: new Date().toISOString(),
|
||||
};
|
||||
// Rewrite whole file — cheap at O(staffers) scale.
|
||||
const existing = await readJsonl<StafferStats>(STAFFERS_FILE);
|
||||
const others = existing.filter(s => s.id !== staffer_id);
|
||||
others.push(stats);
|
||||
await writeFile(
|
||||
join(KB_DIR, STAFFERS_FILE),
|
||||
others.map(s => JSON.stringify(s)).join("\n") + "\n",
|
||||
);
|
||||
return stats;
|
||||
}
|
||||
|
||||
// Public accessor — used by findNeighbors + the staffer report script.
|
||||
export async function loadStafferStats(): Promise<StafferStats[]> {
|
||||
return readJsonl<StafferStats>(STAFFERS_FILE);
|
||||
}
|
||||
|
||||
// Cosine similarity for neighbor lookup. Float32-in-memory is fine for
|
||||
// O(thousands) signatures; swap to vectord HNSW once the corpus grows.
|
||||
function cosine(a: number[], b: number[]): number {
|
||||
@ -237,9 +345,18 @@ function cosine(a: number[], b: number[]): number {
|
||||
// Find the k nearest-neighbor signatures for a target spec, returning
|
||||
// neighbor sigs + their outcome history. This is what the recommender
|
||||
// feeds to the overview model.
|
||||
//
|
||||
// Phase 23 — weighted by staffer competence. The ranking score is
|
||||
// cosine_similarity * max_competence_among_this_sig's_outcomes. Top
|
||||
// staffers' playbooks surface first even if their scenario was
|
||||
// slightly less similar than a junior's. Sigs with no staffer history
|
||||
// fall back to raw similarity (competence defaults to 0.5).
|
||||
export async function findNeighbors(spec: any, k = 5): Promise<Array<{
|
||||
sig: PlaybookSignature;
|
||||
similarity: number;
|
||||
weighted_score: number;
|
||||
best_staffer_competence: number;
|
||||
best_staffer_id: string | null;
|
||||
outcomes: RunOutcome[];
|
||||
}>> {
|
||||
await ensureKb();
|
||||
@ -256,16 +373,45 @@ export async function findNeighbors(spec: any, k = 5): Promise<Array<{
|
||||
|
||||
const sigs = await readJsonl<PlaybookSignature>(SIGNATURES_FILE);
|
||||
const outcomes = await readJsonl<RunOutcome>(OUTCOMES_FILE);
|
||||
const staffers = await readJsonl<StafferStats>(STAFFERS_FILE);
|
||||
const competenceById = new Map(staffers.map(s => [s.id, s.competence_score]));
|
||||
|
||||
// Per-sig best staffer — the highest-competence coordinator who has
|
||||
// ever run this signature. Used to weight the sig's ranking.
|
||||
function bestStafferFor(sig_hash: string): { id: string | null; competence: number } {
|
||||
const mine = outcomes.filter(o => o.sig_hash === sig_hash && o.staffer?.id);
|
||||
if (mine.length === 0) return { id: null, competence: 0.5 };
|
||||
let bestId: string | null = null;
|
||||
let bestComp = 0;
|
||||
for (const o of mine) {
|
||||
const c = competenceById.get(o.staffer!.id) ?? 0.5;
|
||||
if (c > bestComp) { bestComp = c; bestId = o.staffer!.id; }
|
||||
}
|
||||
return { id: bestId, competence: bestComp };
|
||||
}
|
||||
|
||||
const ranked = sigs
|
||||
.filter(s => s.sig_hash !== targetHash) // don't recommend against self
|
||||
.map(s => ({ sig: s, similarity: cosine(targetVec, s.embedding) }))
|
||||
.sort((a, b) => b.similarity - a.similarity)
|
||||
.map(s => {
|
||||
const similarity = cosine(targetVec, s.embedding);
|
||||
const best = bestStafferFor(s.sig_hash);
|
||||
// Weighted score: similarity multiplied by the best staffer's
|
||||
// competence (floored at 0.3 so a sig with a low-competence
|
||||
// staffer still shows up if similarity is very high).
|
||||
const weight = Math.max(best.competence, 0.3);
|
||||
return {
|
||||
sig: s,
|
||||
similarity,
|
||||
weighted_score: similarity * weight,
|
||||
best_staffer_competence: best.competence,
|
||||
best_staffer_id: best.id,
|
||||
};
|
||||
})
|
||||
.sort((a, b) => b.weighted_score - a.weighted_score)
|
||||
.slice(0, k);
|
||||
|
||||
return ranked.map(r => ({
|
||||
sig: r.sig,
|
||||
similarity: r.similarity,
|
||||
...r,
|
||||
outcomes: outcomes.filter(o => o.sig_hash === r.sig.sig_hash),
|
||||
}));
|
||||
}
|
||||
@ -342,6 +488,8 @@ export async function recommendFor(
|
||||
// Build the prompt. Include target spec digest, neighbor digests with
|
||||
// their outcome stats, and recent error corrections. Ask for
|
||||
// structured output so we can parse it.
|
||||
const staffers = await readJsonl<StafferStats>(STAFFERS_FILE);
|
||||
const stafferById = new Map(staffers.map(s => [s.id, s]));
|
||||
const neighborBlock = neighbors.map(n => {
|
||||
const best = n.outcomes.reduce((a, b) =>
|
||||
a && a.ok_events / Math.max(1, a.total_events) >= b.ok_events / Math.max(1, b.total_events) ? a : b,
|
||||
@ -349,7 +497,11 @@ export async function recommendFor(
|
||||
const avgOk = n.outcomes.length > 0
|
||||
? (n.outcomes.reduce((s, o) => s + o.ok_events, 0) / n.outcomes.length).toFixed(1)
|
||||
: "?";
|
||||
return `- sig ${n.sig.sig_hash} sim=${n.similarity.toFixed(3)} (${n.sig.events_digest.slice(0, 100)}): ${n.outcomes.length} runs, avg ${avgOk}/${best?.total_events ?? "?"} ok; best models: exec=${best?.models.executor ?? "?"} review=${best?.models.reviewer ?? "?"}`;
|
||||
const topStaffer = n.best_staffer_id ? stafferById.get(n.best_staffer_id) : null;
|
||||
const stafferTag = topStaffer
|
||||
? ` [top staffer: ${topStaffer.name} (${topStaffer.role}, competence=${topStaffer.competence_score.toFixed(2)})]`
|
||||
: "";
|
||||
return `- sig ${n.sig.sig_hash} sim=${n.similarity.toFixed(3)} weighted=${n.weighted_score.toFixed(3)}${stafferTag}: ${n.outcomes.length} runs, avg ${avgOk}/${best?.total_events ?? "?"} ok; best models: exec=${best?.models.executor ?? "?"} review=${best?.models.reviewer ?? "?"}`;
|
||||
}).join("\n");
|
||||
|
||||
const correctionBlock = corrections.slice(-3).map(c =>
|
||||
|
||||
@ -122,10 +122,40 @@ interface FillEvent {
|
||||
replaces_event?: string; // misplacement back-ref for reporting
|
||||
}
|
||||
|
||||
// Contract terms describe the business constraints around a scenario:
|
||||
// how much can the client pay, by when, and how is a local candidate
|
||||
// bonused vs an out-of-area one. T3 checkpoint + cloud rescue pass
|
||||
// these into the overseer's prompt so pivots respect budget (not just
|
||||
// geography). Optional — legacy scenarios without a contract degrade
|
||||
// to the original "nearest-city" pivot logic.
|
||||
interface ContractTerms {
|
||||
deadline: string; // ISO date — "2026-05-05"
|
||||
budget_total_usd?: number; // optional total cap across the contract
|
||||
budget_per_hour_max?: number; // per-worker hourly cap
|
||||
local_bonus_per_hour?: number; // extra $/hr for in-radius candidates
|
||||
local_bonus_radius_mi?: number; // radius from the contract city
|
||||
fill_requirement?: "paramount" | "preferred"; // "paramount" = must fill;
|
||||
// "preferred" = best-effort
|
||||
}
|
||||
|
||||
// Phase 23 — staffer identity. When a scenario is assigned to a
|
||||
// specific coordinator, we record their trajectory so KB can
|
||||
// weight future retrieval by competence. Junior staffers inherit
|
||||
// Senior staffers' playbooks; top staffers become the training
|
||||
// signal the system auto-discovers.
|
||||
interface Staffer {
|
||||
id: string; // "S-001"
|
||||
name: string; // "Maria Chen"
|
||||
tenure_months: number;
|
||||
role: "senior" | "mid" | "junior" | "trainee";
|
||||
}
|
||||
|
||||
interface ScenarioSpec {
|
||||
client: string;
|
||||
date: string;
|
||||
events: FillEvent[];
|
||||
contract?: ContractTerms;
|
||||
staffer?: Staffer;
|
||||
}
|
||||
|
||||
interface EventResult {
|
||||
@ -167,6 +197,7 @@ interface EventResult {
|
||||
interface CloudRemediation {
|
||||
retry: boolean;
|
||||
new_city?: string;
|
||||
new_state?: string;
|
||||
new_role?: string;
|
||||
new_count?: number;
|
||||
rationale: string;
|
||||
@ -957,10 +988,25 @@ function extractDiagnostics(log: LogEntry[] | undefined): {
|
||||
return { sql_filters, hybrid_row_counts, sql_errors, drift_reasons };
|
||||
}
|
||||
|
||||
// Render contract terms into a prompt-friendly block. Empty string when
|
||||
// no contract is attached — keeps legacy scenarios unaffected.
|
||||
function contractBlock(contract: ContractTerms | undefined): string {
|
||||
if (!contract) return "";
|
||||
const parts: string[] = [];
|
||||
parts.push(`deadline=${contract.deadline}`);
|
||||
if (contract.fill_requirement) parts.push(`fill=${contract.fill_requirement}`);
|
||||
if (contract.budget_per_hour_max) parts.push(`budget/hr max=$${contract.budget_per_hour_max}`);
|
||||
if (contract.budget_total_usd) parts.push(`total budget=$${contract.budget_total_usd}`);
|
||||
if (contract.local_bonus_per_hour) parts.push(`local bonus=+$${contract.local_bonus_per_hour}/hr`);
|
||||
if (contract.local_bonus_radius_mi) parts.push(`local radius=${contract.local_bonus_radius_mi}mi`);
|
||||
return `\nCONTRACT TERMS: ${parts.join(", ")}`;
|
||||
}
|
||||
|
||||
async function runOverviewCheckpoint(
|
||||
event: FillEvent,
|
||||
result: EventResult,
|
||||
prior: EventResult[],
|
||||
contract?: ContractTerms,
|
||||
): Promise<OverviewCheckpoint | null> {
|
||||
if (T3_DISABLED) return null;
|
||||
const start = Date.now();
|
||||
@ -985,13 +1031,13 @@ async function runOverviewCheckpoint(
|
||||
+ `Gap signals: ${result.gap_signals.join("; ") || "none"}.`;
|
||||
|
||||
const prompt = `You are the overview reviewer for a staffing coordinator agent system. A mid-day checkpoint has been triggered.
|
||||
|
||||
${contractBlock(contract)}
|
||||
Recent events (most recent last):
|
||||
${priorSummary || "(no prior events)"}
|
||||
|
||||
${thisOne}${diagBlock}
|
||||
|
||||
Your job: emit ONE risk flag (≤8 words) and ONE actionable hint (≤40 words) for the NEXT event. Be concrete: name the role, city, worker class, OR a geographic pivot (e.g. "pivot Gary IN → Chicago IL, 40min drive"). Do not restate what happened. If the failure was zero-supply, your hint MUST propose a specific alternative city or role. Think step by step, then output strictly as:
|
||||
Your job: emit ONE risk flag (≤8 words) and ONE actionable hint (≤40 words) for the NEXT event. Be concrete: name the role, city, worker class, OR a geographic pivot (e.g. "pivot to {city}, {state} ({distance}mi) — still in bonus radius"). Do not restate what happened. If the failure was zero-supply, your hint MUST propose a specific alternative city or role. If contract has local_bonus_radius_mi, prefer alternates within that radius before proposing farther pivots. Think step by step, then output strictly as:
|
||||
|
||||
RISK: <flag>
|
||||
HINT: <hint>`;
|
||||
@ -1036,6 +1082,7 @@ HINT: <hint>`;
|
||||
async function requestCloudRemediation(
|
||||
event: FillEvent,
|
||||
result: EventResult,
|
||||
contract?: ContractTerms,
|
||||
): Promise<{ remediation: CloudRemediation; duration_secs: number } | null> {
|
||||
if (T3_DISABLED) return null;
|
||||
const start = Date.now();
|
||||
@ -1054,6 +1101,7 @@ FAILED EVENT:
|
||||
outcome: ${result.error ?? "unknown failure"}
|
||||
turns used: ${result.turns}
|
||||
pool surfaced: ${result.pool_size ?? "n/a"}
|
||||
${contractBlock(contract)}
|
||||
|
||||
RAW DIAGNOSTICS (what the agent actually saw):
|
||||
${diagBlock}
|
||||
@ -1062,6 +1110,7 @@ Respond with a JSON object (NOTHING else, no prose before or after, no markdown)
|
||||
{
|
||||
"retry": true | false,
|
||||
"new_city": "string (same as original if no pivot)",
|
||||
"new_state": "XX (two-letter abbreviation)",
|
||||
"new_role": "string (same as original if no pivot)",
|
||||
"new_count": <integer (same as original if no pivot)>,
|
||||
"rationale": "2-3 sentences explaining the fix or why retry is futile"
|
||||
@ -1072,7 +1121,9 @@ RULES:
|
||||
- If the city has genuine zero supply, pivot to the NEAREST alternate city with comparable labor pool (name a specific one).
|
||||
- If the role is uniquely scarce, either broaden to a synonym role OR reduce count to something achievable.
|
||||
- If no pivot seems viable, set retry=false — wasting a retry is worse than declaring impossible.
|
||||
- Keep new_count realistic. Don't propose 5× in a city that clearly only has 2 workers.`;
|
||||
- Keep new_count realistic. Don't propose 5× in a city that clearly only has 2 workers.
|
||||
- new_city is the CITY name only (e.g. "Clarksville"). Put the two-letter state in new_state. Don't concat them.
|
||||
${contract ? `- CONTRACT AWARENESS: fill_requirement=${contract.fill_requirement ?? "preferred"}. ${contract.fill_requirement === "paramount" ? "MUST fill — splitting across more cities is acceptable if one city can't cover headcount." : "Best-effort — declining is OK if budget can't absorb pivot premium."} ${contract.local_bonus_radius_mi ? `Local bonus radius is ${contract.local_bonus_radius_mi}mi from ${event.city}, ${event.state}; prefer pivots WITHIN that radius even if they have slightly less supply, since those candidates earn the local bonus premium.` : ""} ${contract.budget_per_hour_max ? `Per-hour budget max is $${contract.budget_per_hour_max}.` : ""}` : ""}`;
|
||||
|
||||
let raw = "";
|
||||
try {
|
||||
@ -1108,7 +1159,7 @@ async function runCrossDayLesson(ctx: ScenarioContext, checkpoints: OverviewChec
|
||||
|
||||
const prompt = `You are the end-of-day lesson writer for a staffing coordinator agent system. The day is done. Distill it.
|
||||
|
||||
Client: ${ctx.spec.client} Date: ${ctx.spec.date}
|
||||
Client: ${ctx.spec.client} Date: ${ctx.spec.date}${contractBlock(ctx.spec.contract)}
|
||||
|
||||
Events that ran:
|
||||
${eventDigest}
|
||||
@ -1326,6 +1377,13 @@ async function main() {
|
||||
const checkpoints: OverviewCheckpoint[] = [];
|
||||
|
||||
console.log(`▶ scenario: ${spec.client}, ${spec.date}, ${spec.events.length} events`);
|
||||
if (spec.staffer) {
|
||||
console.log(`▶ staffer: ${spec.staffer.id} ${spec.staffer.name} (${spec.staffer.role}, ${spec.staffer.tenure_months}mo)`);
|
||||
}
|
||||
if (spec.contract) {
|
||||
const c = spec.contract;
|
||||
console.log(`▶ contract: deadline=${c.deadline} fill=${c.fill_requirement ?? "preferred"}${c.budget_per_hour_max ? ` budget=$${c.budget_per_hour_max}/hr` : ""}${c.local_bonus_radius_mi ? ` local_radius=${c.local_bonus_radius_mi}mi+$${c.local_bonus_per_hour ?? 0}` : ""}`);
|
||||
}
|
||||
console.log(`▶ models: exec=${EXECUTOR_MODEL} review=${REVIEWER_MODEL} overview=${T3_DISABLED ? "disabled" : OVERVIEW_MODEL + (OVERVIEW_CLOUD ? " (cloud)" : "")}`);
|
||||
console.log(`▶ out: ${out_dir}\n`);
|
||||
|
||||
@ -1363,26 +1421,21 @@ async function main() {
|
||||
// scenarios.
|
||||
if (!result.ok && RETRY_ON_FAIL && !T3_DISABLED) {
|
||||
console.log(` ▶ cloud rescue requested for ${event.at} ${event.kind}…`);
|
||||
const rescue = await requestCloudRemediation(event, result);
|
||||
const rescue = await requestCloudRemediation(event, result, spec.contract);
|
||||
if (rescue && rescue.remediation.retry) {
|
||||
const r = rescue.remediation;
|
||||
// Sanitize cloud's fields — model sometimes emits "Hammond, IN"
|
||||
// as new_city and "IN" as new_state, producing "Hammond, IN, IN"
|
||||
// downstream. Split on comma and take the first token for city.
|
||||
const sanitizeCity = (c: string | undefined) => (c ?? "").split(",")[0].trim();
|
||||
const sanitizeState = (c: string | undefined, stateFromCity: string) => {
|
||||
const explicit = (c ?? "").trim();
|
||||
// If explicit state is empty or matches original, try to
|
||||
// extract from the city string if it had a trailing ", XX".
|
||||
return explicit || stateFromCity || event.state;
|
||||
};
|
||||
const cityRaw = r.new_city ?? event.city;
|
||||
const cityClean = sanitizeCity(cityRaw);
|
||||
const stateFromCity = (cityRaw.match(/,\s*([A-Z]{2})/) ?? [])[1] ?? "";
|
||||
// Sanitize cloud's fields — model sometimes still packs state
|
||||
// into city ("Hammond, IN") even when prompted to split; guard
|
||||
// by stripping comma+ from new_city and harvesting the trailing
|
||||
// state if new_state is missing.
|
||||
const cityRaw = (r.new_city ?? event.city).trim();
|
||||
const cityParts = cityRaw.split(",").map(s => s.trim());
|
||||
const cityClean = cityParts[0] || event.city;
|
||||
const stateFromCity = cityParts[1]?.match(/^([A-Z]{2})/i)?.[1]?.toUpperCase() ?? "";
|
||||
const newEvent: FillEvent = {
|
||||
...event,
|
||||
city: cityClean || event.city,
|
||||
state: sanitizeState(undefined, stateFromCity) || event.state,
|
||||
city: cityClean,
|
||||
state: (r.new_state?.trim() || stateFromCity || event.state).toUpperCase(),
|
||||
role: r.new_role ?? event.role,
|
||||
count: r.new_count ?? event.count,
|
||||
scenario_note: `[cloud-rescue ${rescue.duration_secs.toFixed(1)}s] ${r.rationale}`,
|
||||
@ -1455,7 +1508,7 @@ async function main() {
|
||||
const nthHit = T3_CHECKPOINT_EVERY > 0 && ((i + 1) % T3_CHECKPOINT_EVERY === 0);
|
||||
const shouldCheckpoint = !T3_DISABLED && (event.kind === "misplacement" || nthHit || isLast);
|
||||
if (shouldCheckpoint) {
|
||||
const cp = await runOverviewCheckpoint(event, result, ctx.results.slice(0, -1));
|
||||
const cp = await runOverviewCheckpoint(event, result, ctx.results.slice(0, -1), spec.contract);
|
||||
if (cp) {
|
||||
checkpoints.push(cp);
|
||||
await appendFile(join(out_dir, "checkpoints.jsonl"), JSON.stringify(cp) + "\n");
|
||||
@ -1532,12 +1585,17 @@ async function main() {
|
||||
// run ends → KB updates → next run reads rec at startup.
|
||||
try {
|
||||
const elapsed = (Date.now() - runStart) / 1000;
|
||||
const { sig_hash } = await indexRun(out_dir, spec, {
|
||||
executor: EXECUTOR_MODEL,
|
||||
reviewer: REVIEWER_MODEL,
|
||||
overview: OVERVIEW_MODEL,
|
||||
overview_cloud: OVERVIEW_CLOUD,
|
||||
}, elapsed);
|
||||
const { sig_hash } = await indexRun(
|
||||
out_dir,
|
||||
{ client: spec.client, date: spec.date, events: spec.events, staffer: spec.staffer },
|
||||
{
|
||||
executor: EXECUTOR_MODEL,
|
||||
reviewer: REVIEWER_MODEL,
|
||||
overview: OVERVIEW_MODEL,
|
||||
overview_cloud: OVERVIEW_CLOUD,
|
||||
},
|
||||
elapsed,
|
||||
);
|
||||
console.log(`▶ KB indexed: sig=${sig_hash} (${elapsed.toFixed(1)}s)`);
|
||||
const newRec = await recommendFor(spec, {
|
||||
overview_model: OVERVIEW_MODEL,
|
||||
|
||||
126
tests/multi-agent/scenarios/manifest.json
Normal file
126
tests/multi-agent/scenarios/manifest.json
Normal file
@ -0,0 +1,126 @@
|
||||
{
|
||||
"count": 20,
|
||||
"seed": 1337,
|
||||
"scenarios": [
|
||||
{
|
||||
"file": "scen_000_Great_Lakes_Mfg_Cincinnati.json",
|
||||
"client": "Great Lakes Mfg",
|
||||
"city": "Cincinnati",
|
||||
"events": 4
|
||||
},
|
||||
{
|
||||
"file": "scen_001_Parallel_Machining_Joliet.json",
|
||||
"client": "Parallel Machining",
|
||||
"city": "Joliet",
|
||||
"events": 2
|
||||
},
|
||||
{
|
||||
"file": "scen_002_Summit_Industrial_Cincinnati.json",
|
||||
"client": "Summit Industrial",
|
||||
"city": "Cincinnati",
|
||||
"events": 3
|
||||
},
|
||||
{
|
||||
"file": "scen_003_Pioneer_Assembly_Chicago.json",
|
||||
"client": "Pioneer Assembly",
|
||||
"city": "Chicago",
|
||||
"events": 1
|
||||
},
|
||||
{
|
||||
"file": "scen_004_Midway_Distribution_Columbus.json",
|
||||
"client": "Midway Distribution",
|
||||
"city": "Columbus",
|
||||
"events": 2
|
||||
},
|
||||
{
|
||||
"file": "scen_005_Apex_Warehouse_Cleveland.json",
|
||||
"client": "Apex Warehouse",
|
||||
"city": "Cleveland",
|
||||
"events": 3
|
||||
},
|
||||
{
|
||||
"file": "scen_006_Pioneer_Assembly_Flint.json",
|
||||
"client": "Pioneer Assembly",
|
||||
"city": "Flint",
|
||||
"events": 5
|
||||
},
|
||||
{
|
||||
"file": "scen_007_Riverfront_Steel_Toledo.json",
|
||||
"client": "Riverfront Steel",
|
||||
"city": "Toledo",
|
||||
"events": 3
|
||||
},
|
||||
{
|
||||
"file": "scen_008_Northland_Logistics_Indianapolis.json",
|
||||
"client": "Northland Logistics",
|
||||
"city": "Indianapolis",
|
||||
"events": 4
|
||||
},
|
||||
{
|
||||
"file": "scen_009_Parallel_Machining_Flint.json",
|
||||
"client": "Parallel Machining",
|
||||
"city": "Flint",
|
||||
"events": 3
|
||||
},
|
||||
{
|
||||
"file": "scen_010_Northland_Logistics_Chicago.json",
|
||||
"client": "Northland Logistics",
|
||||
"city": "Chicago",
|
||||
"events": 2
|
||||
},
|
||||
{
|
||||
"file": "scen_011_Heritage_Foods_Flint.json",
|
||||
"client": "Heritage Foods",
|
||||
"city": "Flint",
|
||||
"events": 3
|
||||
},
|
||||
{
|
||||
"file": "scen_012_Parallel_Machining_Kansas_City.json",
|
||||
"client": "Parallel Machining",
|
||||
"city": "Kansas City",
|
||||
"events": 3
|
||||
},
|
||||
{
|
||||
"file": "scen_013_Horizon_Supply_Flint.json",
|
||||
"client": "Horizon Supply",
|
||||
"city": "Flint",
|
||||
"events": 3
|
||||
},
|
||||
{
|
||||
"file": "scen_014_Midway_Distribution_Indianapolis.json",
|
||||
"client": "Midway Distribution",
|
||||
"city": "Indianapolis",
|
||||
"events": 4
|
||||
},
|
||||
{
|
||||
"file": "scen_015_Cornerstone_Fabrication_Kansas_City.json",
|
||||
"client": "Cornerstone Fabrication",
|
||||
"city": "Kansas City",
|
||||
"events": 4
|
||||
},
|
||||
{
|
||||
"file": "scen_016_Riverfront_Steel_Columbus.json",
|
||||
"client": "Riverfront Steel",
|
||||
"city": "Columbus",
|
||||
"events": 4
|
||||
},
|
||||
{
|
||||
"file": "scen_017_Summit_Industrial_Detroit.json",
|
||||
"client": "Summit Industrial",
|
||||
"city": "Detroit",
|
||||
"events": 2
|
||||
},
|
||||
{
|
||||
"file": "scen_018_Heritage_Foods_Cincinnati.json",
|
||||
"client": "Heritage Foods",
|
||||
"city": "Cincinnati",
|
||||
"events": 4
|
||||
},
|
||||
{
|
||||
"file": "scen_019_Midway_Distribution_Chicago.json",
|
||||
"client": "Midway Distribution",
|
||||
"city": "Chicago",
|
||||
"events": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
66
tests/multi-agent/scenarios/nashville_contract.json
Normal file
66
tests/multi-agent/scenarios/nashville_contract.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"client": "Riverline Logistics — Nashville Downtown Build-Out",
|
||||
"date": "2026-05-05",
|
||||
"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"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:00",
|
||||
"role": "Welder",
|
||||
"count": 4,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "07:00 AM",
|
||||
"scenario_note": "Skilled welding for downtown structural assembly. Prefer local (in 75mi). Contract pays up to $32/hr + $4/hr local bonus. Fill is paramount — the contract is worth $180K total, broken = penalty."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "08:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 6,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "08:30 AM",
|
||||
"scenario_note": "Warehouse packaging on-site for the same project. 6 workers preferred Nashville-local; out-of-area acceptable if local pool is exhausted. Respect budget."
|
||||
},
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "09:00",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 2,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "09:00 AM",
|
||||
"scenario_note": "Administrative/shipping coordination role. Two clerks needed; tightening the load on packaging team."
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "13:00",
|
||||
"role": "Welder",
|
||||
"count": 2,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "13:00 PM",
|
||||
"deadline": "15:00",
|
||||
"scenario_note": "A subcontractor just dropped out — need 2 more Welders by 3pm. Use cloud rescue path if Nashville supply is exhausted; pivot to within-radius first (if any in 75mi), then budget-permitting out-of-area."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 1,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "15:30 PM",
|
||||
"replaces_event": "08:30",
|
||||
"scenario_note": "One packager no-showed. Single refill. Any Nashville-area candidate, budget already covered."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
{
|
||||
"client": "Great Lakes Mfg",
|
||||
"date": "2026-04-21",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "13:00",
|
||||
"role": "Material Handler",
|
||||
"count": 2,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "13:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "14:30",
|
||||
"role": "Assembler",
|
||||
"count": 2,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "14:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "10:30",
|
||||
"role": "Material Handler",
|
||||
"count": 3,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "10:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "13:30",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 1,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "13:30 AM",
|
||||
"replaces_event": "13:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
{
|
||||
"client": "Parallel Machining",
|
||||
"date": "2026-04-22",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "10:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 2,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "10:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "09:30",
|
||||
"role": "Packer",
|
||||
"count": 2,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "09:30 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
{
|
||||
"client": "Summit Industrial",
|
||||
"date": "2026-04-23",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "13:30",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 1,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "13:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "08:00",
|
||||
"role": "Loader",
|
||||
"count": 1,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "08:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "13:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 2,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "13:00 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
{
|
||||
"client": "Pioneer Assembly",
|
||||
"date": "2026-04-24",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "14:00",
|
||||
"role": "Receiving Clerk",
|
||||
"count": 3,
|
||||
"city": "Chicago",
|
||||
"state": "IL",
|
||||
"shift_start": "14:00 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
{
|
||||
"client": "Midway Distribution",
|
||||
"date": "2026-04-25",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "09:30",
|
||||
"role": "Forklift Operator",
|
||||
"count": 1,
|
||||
"city": "Columbus",
|
||||
"state": "OH",
|
||||
"shift_start": "09:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "13:00",
|
||||
"role": "Assembler",
|
||||
"count": 4,
|
||||
"city": "Columbus",
|
||||
"state": "OH",
|
||||
"shift_start": "13:00 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
{
|
||||
"client": "Apex Warehouse",
|
||||
"date": "2026-04-26",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "10:30",
|
||||
"role": "Receiving Clerk",
|
||||
"count": 1,
|
||||
"city": "Cleveland",
|
||||
"state": "OH",
|
||||
"shift_start": "10:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "15:30",
|
||||
"role": "Quality Tech",
|
||||
"count": 2,
|
||||
"city": "Cleveland",
|
||||
"state": "OH",
|
||||
"shift_start": "15:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "14:30",
|
||||
"role": "Machine Operator",
|
||||
"count": 5,
|
||||
"city": "Cleveland",
|
||||
"state": "OH",
|
||||
"shift_start": "14:30 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
{
|
||||
"client": "Pioneer Assembly",
|
||||
"date": "2026-04-27",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "13:30",
|
||||
"role": "CNC Operator",
|
||||
"count": 2,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "13:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "11:30",
|
||||
"role": "Loader",
|
||||
"count": 1,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "11:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "14:00",
|
||||
"role": "Welder",
|
||||
"count": 3,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "14:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "12:30",
|
||||
"role": "Machine Operator",
|
||||
"count": 2,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "12:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "17:00",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 1,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "17:00 AM",
|
||||
"replaces_event": "13:30"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"client": "Riverfront Steel",
|
||||
"date": "2026-04-28",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "11:00",
|
||||
"role": "Assembler",
|
||||
"count": 3,
|
||||
"city": "Toledo",
|
||||
"state": "OH",
|
||||
"shift_start": "11:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "14:00",
|
||||
"role": "Material Handler",
|
||||
"count": 1,
|
||||
"city": "Toledo",
|
||||
"state": "OH",
|
||||
"shift_start": "14:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "08:30",
|
||||
"role": "Packer",
|
||||
"count": 1,
|
||||
"city": "Toledo",
|
||||
"state": "OH",
|
||||
"shift_start": "08:30 AM",
|
||||
"replaces_event": "11:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
{
|
||||
"client": "Northland Logistics",
|
||||
"date": "2026-04-29",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "14:00",
|
||||
"role": "Quality Tech",
|
||||
"count": 1,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "14:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "12:30",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 1,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "12:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "10:00",
|
||||
"role": "Welder",
|
||||
"count": 2,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "10:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "14:30",
|
||||
"role": "Material Handler",
|
||||
"count": 1,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "14:30 AM",
|
||||
"replaces_event": "14:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
{
|
||||
"client": "Parallel Machining",
|
||||
"date": "2026-04-30",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "16:00",
|
||||
"role": "Loader",
|
||||
"count": 3,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "16:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "09:30",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 2,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "09:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "12:30",
|
||||
"role": "Packer",
|
||||
"count": 3,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "12:30 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
{
|
||||
"client": "Northland Logistics",
|
||||
"date": "2026-05-01",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "15:00",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 1,
|
||||
"city": "Chicago",
|
||||
"state": "IL",
|
||||
"shift_start": "15:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "10:00",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 2,
|
||||
"city": "Chicago",
|
||||
"state": "IL",
|
||||
"shift_start": "10:00 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
{
|
||||
"client": "Heritage Foods",
|
||||
"date": "2026-05-02",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "10:00",
|
||||
"role": "Receiving Clerk",
|
||||
"count": 3,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "10:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "14:30",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 4,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "14:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "08:30",
|
||||
"role": "Assembler",
|
||||
"count": 3,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "08:30 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
{
|
||||
"client": "Parallel Machining",
|
||||
"date": "2026-05-03",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "08:00",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 2,
|
||||
"city": "Kansas City",
|
||||
"state": "MO",
|
||||
"shift_start": "08:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "11:30",
|
||||
"role": "Material Handler",
|
||||
"count": 2,
|
||||
"city": "Kansas City",
|
||||
"state": "MO",
|
||||
"shift_start": "11:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "09:00",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 5,
|
||||
"city": "Kansas City",
|
||||
"state": "MO",
|
||||
"shift_start": "09:00 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
{
|
||||
"client": "Horizon Supply",
|
||||
"date": "2026-05-04",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "11:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 1,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "11:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "10:30",
|
||||
"role": "CNC Operator",
|
||||
"count": 1,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "10:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "16:30",
|
||||
"role": "Receiving Clerk",
|
||||
"count": 3,
|
||||
"city": "Flint",
|
||||
"state": "MI",
|
||||
"shift_start": "16:30 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
{
|
||||
"client": "Midway Distribution",
|
||||
"date": "2026-05-05",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "16:00",
|
||||
"role": "Assembler",
|
||||
"count": 2,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "16:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "17:30",
|
||||
"role": "Loader",
|
||||
"count": 2,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "17:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "10:30",
|
||||
"role": "Packer",
|
||||
"count": 5,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "10:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "12:00",
|
||||
"role": "Loader",
|
||||
"count": 4,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "12:00 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
{
|
||||
"client": "Cornerstone Fabrication",
|
||||
"date": "2026-05-06",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "14:00",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 2,
|
||||
"city": "Kansas City",
|
||||
"state": "MO",
|
||||
"shift_start": "14:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "17:30",
|
||||
"role": "Loader",
|
||||
"count": 1,
|
||||
"city": "Kansas City",
|
||||
"state": "MO",
|
||||
"shift_start": "17:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "12:00",
|
||||
"role": "Packer",
|
||||
"count": 3,
|
||||
"city": "Kansas City",
|
||||
"state": "MO",
|
||||
"shift_start": "12:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "16:30",
|
||||
"role": "Assembler",
|
||||
"count": 1,
|
||||
"city": "Kansas City",
|
||||
"state": "MO",
|
||||
"shift_start": "16:30 AM",
|
||||
"replaces_event": "14:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
{
|
||||
"client": "Riverfront Steel",
|
||||
"date": "2026-05-07",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "12:00",
|
||||
"role": "Quality Tech",
|
||||
"count": 3,
|
||||
"city": "Columbus",
|
||||
"state": "OH",
|
||||
"shift_start": "12:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "09:30",
|
||||
"role": "Machine Operator",
|
||||
"count": 1,
|
||||
"city": "Columbus",
|
||||
"state": "OH",
|
||||
"shift_start": "09:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "17:30",
|
||||
"role": "Assembler",
|
||||
"count": 3,
|
||||
"city": "Columbus",
|
||||
"state": "OH",
|
||||
"shift_start": "17:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:30",
|
||||
"role": "Material Handler",
|
||||
"count": 1,
|
||||
"city": "Columbus",
|
||||
"state": "OH",
|
||||
"shift_start": "15:30 AM",
|
||||
"replaces_event": "12:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
{
|
||||
"client": "Summit Industrial",
|
||||
"date": "2026-05-08",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "13:00",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 1,
|
||||
"city": "Detroit",
|
||||
"state": "MI",
|
||||
"shift_start": "13:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "10:30",
|
||||
"role": "Material Handler",
|
||||
"count": 1,
|
||||
"city": "Detroit",
|
||||
"state": "MI",
|
||||
"shift_start": "10:30 AM"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
{
|
||||
"client": "Heritage Foods",
|
||||
"date": "2026-05-09",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "12:00",
|
||||
"role": "Assembler",
|
||||
"count": 1,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "12:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "17:30",
|
||||
"role": "Welder",
|
||||
"count": 1,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "17:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "13:30",
|
||||
"role": "Material Handler",
|
||||
"count": 2,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "13:30 AM"
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "12:00",
|
||||
"role": "Receiving Clerk",
|
||||
"count": 1,
|
||||
"city": "Cincinnati",
|
||||
"state": "OH",
|
||||
"shift_start": "12:00 AM",
|
||||
"replaces_event": "12:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"client": "Midway Distribution",
|
||||
"date": "2026-05-10",
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "11:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 2,
|
||||
"city": "Chicago",
|
||||
"state": "IL",
|
||||
"shift_start": "11:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "17:00",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 2,
|
||||
"city": "Chicago",
|
||||
"state": "IL",
|
||||
"shift_start": "17:00 AM"
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "09:30",
|
||||
"role": "Packer",
|
||||
"count": 1,
|
||||
"city": "Chicago",
|
||||
"state": "IL",
|
||||
"shift_start": "09:30 AM",
|
||||
"replaces_event": "11:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
{
|
||||
"client": "Pioneer Assembly — Indianapolis Plant Expansion",
|
||||
"date": "2026-04-24",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-001",
|
||||
"name": "Maria Chen",
|
||||
"tenure_months": 48,
|
||||
"role": "senior"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:30",
|
||||
"role": "Assembler",
|
||||
"count": 6,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "07:30 AM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "09:30",
|
||||
"role": "Quality Tech",
|
||||
"count": 2,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "09:30 AM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "11:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 5,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "11:00 PM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "14:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 3,
|
||||
"deadline": "16:00",
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "14:00 PM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "16:00",
|
||||
"role": "Assembler",
|
||||
"count": 1,
|
||||
"replaces_event": "07:30",
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "16:00 PM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
{
|
||||
"client": "Midway Distribution — Joliet DC Ramp",
|
||||
"date": "2026-04-23",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-001",
|
||||
"name": "Maria Chen",
|
||||
"tenure_months": 48,
|
||||
"role": "senior"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:00",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 5,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "07:00 AM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "10:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 3,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "10:00 PM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "12:30",
|
||||
"role": "Picker",
|
||||
"count": 4,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "12:30 PM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 1,
|
||||
"replaces_event": "10:00",
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "15:00 PM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
{
|
||||
"client": "Riverline Logistics — Nashville Downtown Build-Out",
|
||||
"date": "2026-04-22",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-001",
|
||||
"name": "Maria Chen",
|
||||
"tenure_months": 48,
|
||||
"role": "senior"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:00",
|
||||
"role": "Welder",
|
||||
"count": 4,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "07:00 AM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "08:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 6,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "08:30 AM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "09:00",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 2,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "09:00 AM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "13:00",
|
||||
"role": "Welder",
|
||||
"count": 2,
|
||||
"deadline": "15:00",
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "13:00 PM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 1,
|
||||
"replaces_event": "08:30",
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "15:30 PM",
|
||||
"scenario_note": "Staffed by Maria Chen (senior, 48mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
{
|
||||
"client": "Pioneer Assembly — Indianapolis Plant Expansion",
|
||||
"date": "2026-04-27",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-002",
|
||||
"name": "James Park",
|
||||
"tenure_months": 14,
|
||||
"role": "mid"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:30",
|
||||
"role": "Assembler",
|
||||
"count": 6,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "07:30 AM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "09:30",
|
||||
"role": "Quality Tech",
|
||||
"count": 2,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "09:30 AM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "11:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 5,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "11:00 PM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "14:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 3,
|
||||
"deadline": "16:00",
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "14:00 PM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "16:00",
|
||||
"role": "Assembler",
|
||||
"count": 1,
|
||||
"replaces_event": "07:30",
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "16:00 PM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
{
|
||||
"client": "Midway Distribution — Joliet DC Ramp",
|
||||
"date": "2026-04-26",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-002",
|
||||
"name": "James Park",
|
||||
"tenure_months": 14,
|
||||
"role": "mid"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:00",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 5,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "07:00 AM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "10:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 3,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "10:00 PM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "12:30",
|
||||
"role": "Picker",
|
||||
"count": 4,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "12:30 PM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 1,
|
||||
"replaces_event": "10:00",
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "15:00 PM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
{
|
||||
"client": "Riverline Logistics — Nashville Downtown Build-Out",
|
||||
"date": "2026-04-25",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-002",
|
||||
"name": "James Park",
|
||||
"tenure_months": 14,
|
||||
"role": "mid"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:00",
|
||||
"role": "Welder",
|
||||
"count": 4,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "07:00 AM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "08:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 6,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "08:30 AM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "09:00",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 2,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "09:00 AM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "13:00",
|
||||
"role": "Welder",
|
||||
"count": 2,
|
||||
"deadline": "15:00",
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "13:00 PM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 1,
|
||||
"replaces_event": "08:30",
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "15:30 PM",
|
||||
"scenario_note": "Staffed by James Park (mid, 14mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
{
|
||||
"client": "Pioneer Assembly — Indianapolis Plant Expansion",
|
||||
"date": "2026-04-30",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-003",
|
||||
"name": "Sam Torres",
|
||||
"tenure_months": 4,
|
||||
"role": "junior"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:30",
|
||||
"role": "Assembler",
|
||||
"count": 6,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "07:30 AM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "09:30",
|
||||
"role": "Quality Tech",
|
||||
"count": 2,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "09:30 AM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "11:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 5,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "11:00 PM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "14:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 3,
|
||||
"deadline": "16:00",
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "14:00 PM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "16:00",
|
||||
"role": "Assembler",
|
||||
"count": 1,
|
||||
"replaces_event": "07:30",
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "16:00 PM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
{
|
||||
"client": "Midway Distribution — Joliet DC Ramp",
|
||||
"date": "2026-04-29",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-003",
|
||||
"name": "Sam Torres",
|
||||
"tenure_months": 4,
|
||||
"role": "junior"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:00",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 5,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "07:00 AM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "10:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 3,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "10:00 PM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "12:30",
|
||||
"role": "Picker",
|
||||
"count": 4,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "12:30 PM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 1,
|
||||
"replaces_event": "10:00",
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "15:00 PM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
{
|
||||
"client": "Riverline Logistics — Nashville Downtown Build-Out",
|
||||
"date": "2026-04-28",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-003",
|
||||
"name": "Sam Torres",
|
||||
"tenure_months": 4,
|
||||
"role": "junior"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:00",
|
||||
"role": "Welder",
|
||||
"count": 4,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "07:00 AM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "08:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 6,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "08:30 AM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "09:00",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 2,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "09:00 AM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "13:00",
|
||||
"role": "Welder",
|
||||
"count": 2,
|
||||
"deadline": "15:00",
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "13:00 PM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 1,
|
||||
"replaces_event": "08:30",
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "15:30 PM",
|
||||
"scenario_note": "Staffed by Sam Torres (junior, 4mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
{
|
||||
"client": "Pioneer Assembly — Indianapolis Plant Expansion",
|
||||
"date": "2026-05-03",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-004",
|
||||
"name": "Alex Rivera",
|
||||
"tenure_months": 1,
|
||||
"role": "trainee"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:30",
|
||||
"role": "Assembler",
|
||||
"count": 6,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "07:30 AM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "09:30",
|
||||
"role": "Quality Tech",
|
||||
"count": 2,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "09:30 AM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "11:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 5,
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "11:00 PM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "14:00",
|
||||
"role": "Machine Operator",
|
||||
"count": 3,
|
||||
"deadline": "16:00",
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "14:00 PM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "16:00",
|
||||
"role": "Assembler",
|
||||
"count": 1,
|
||||
"replaces_event": "07:30",
|
||||
"city": "Indianapolis",
|
||||
"state": "IN",
|
||||
"shift_start": "16:00 PM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-26, fill=paramount."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
{
|
||||
"client": "Midway Distribution — Joliet DC Ramp",
|
||||
"date": "2026-05-02",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-004",
|
||||
"name": "Alex Rivera",
|
||||
"tenure_months": 1,
|
||||
"role": "trainee"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:00",
|
||||
"role": "Warehouse Associate",
|
||||
"count": 5,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "07:00 AM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "recurring",
|
||||
"at": "10:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 3,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "10:00 PM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "12:30",
|
||||
"role": "Picker",
|
||||
"count": 4,
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "12:30 PM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:00",
|
||||
"role": "Forklift Operator",
|
||||
"count": 1,
|
||||
"replaces_event": "10:00",
|
||||
"city": "Joliet",
|
||||
"state": "IL",
|
||||
"shift_start": "15:00 PM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-12, fill=preferred."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
{
|
||||
"client": "Riverline Logistics — Nashville Downtown Build-Out",
|
||||
"date": "2026-05-01",
|
||||
"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"
|
||||
},
|
||||
"staffer": {
|
||||
"id": "S-004",
|
||||
"name": "Alex Rivera",
|
||||
"tenure_months": 1,
|
||||
"role": "trainee"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "07:00",
|
||||
"role": "Welder",
|
||||
"count": 4,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "07:00 AM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "expansion",
|
||||
"at": "08:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 6,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "08:30 AM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "baseline_fill",
|
||||
"at": "09:00",
|
||||
"role": "Shipping Clerk",
|
||||
"count": 2,
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "09:00 AM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "emergency",
|
||||
"at": "13:00",
|
||||
"role": "Welder",
|
||||
"count": 2,
|
||||
"deadline": "15:00",
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "13:00 PM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
},
|
||||
{
|
||||
"kind": "misplacement",
|
||||
"at": "15:30",
|
||||
"role": "Packaging Operator",
|
||||
"count": 1,
|
||||
"replaces_event": "08:30",
|
||||
"city": "Nashville",
|
||||
"state": "TN",
|
||||
"shift_start": "15:30 PM",
|
||||
"scenario_note": "Staffed by Alex Rivera (trainee, 1mo). Contract deadline 2026-05-19, fill=paramount."
|
||||
}
|
||||
]
|
||||
}
|
||||
77
tests/multi-agent/scenarios/staffer_demo/manifest.json
Normal file
77
tests/multi-agent/scenarios/staffer_demo/manifest.json
Normal file
@ -0,0 +1,77 @@
|
||||
{
|
||||
"count": 12,
|
||||
"scenarios": [
|
||||
{
|
||||
"file": "S-001_nashville_downtown.json",
|
||||
"staffer": "Maria Chen",
|
||||
"contract": "nashville_downtown",
|
||||
"client": "Riverline Logistics — Nashville Downtown Build-Out"
|
||||
},
|
||||
{
|
||||
"file": "S-001_joliet_warehouse.json",
|
||||
"staffer": "Maria Chen",
|
||||
"contract": "joliet_warehouse",
|
||||
"client": "Midway Distribution — Joliet DC Ramp"
|
||||
},
|
||||
{
|
||||
"file": "S-001_indianapolis_assembly.json",
|
||||
"staffer": "Maria Chen",
|
||||
"contract": "indianapolis_assembly",
|
||||
"client": "Pioneer Assembly — Indianapolis Plant Expansion"
|
||||
},
|
||||
{
|
||||
"file": "S-002_nashville_downtown.json",
|
||||
"staffer": "James Park",
|
||||
"contract": "nashville_downtown",
|
||||
"client": "Riverline Logistics — Nashville Downtown Build-Out"
|
||||
},
|
||||
{
|
||||
"file": "S-002_joliet_warehouse.json",
|
||||
"staffer": "James Park",
|
||||
"contract": "joliet_warehouse",
|
||||
"client": "Midway Distribution — Joliet DC Ramp"
|
||||
},
|
||||
{
|
||||
"file": "S-002_indianapolis_assembly.json",
|
||||
"staffer": "James Park",
|
||||
"contract": "indianapolis_assembly",
|
||||
"client": "Pioneer Assembly — Indianapolis Plant Expansion"
|
||||
},
|
||||
{
|
||||
"file": "S-003_nashville_downtown.json",
|
||||
"staffer": "Sam Torres",
|
||||
"contract": "nashville_downtown",
|
||||
"client": "Riverline Logistics — Nashville Downtown Build-Out"
|
||||
},
|
||||
{
|
||||
"file": "S-003_joliet_warehouse.json",
|
||||
"staffer": "Sam Torres",
|
||||
"contract": "joliet_warehouse",
|
||||
"client": "Midway Distribution — Joliet DC Ramp"
|
||||
},
|
||||
{
|
||||
"file": "S-003_indianapolis_assembly.json",
|
||||
"staffer": "Sam Torres",
|
||||
"contract": "indianapolis_assembly",
|
||||
"client": "Pioneer Assembly — Indianapolis Plant Expansion"
|
||||
},
|
||||
{
|
||||
"file": "S-004_nashville_downtown.json",
|
||||
"staffer": "Alex Rivera",
|
||||
"contract": "nashville_downtown",
|
||||
"client": "Riverline Logistics — Nashville Downtown Build-Out"
|
||||
},
|
||||
{
|
||||
"file": "S-004_joliet_warehouse.json",
|
||||
"staffer": "Alex Rivera",
|
||||
"contract": "joliet_warehouse",
|
||||
"client": "Midway Distribution — Joliet DC Ramp"
|
||||
},
|
||||
{
|
||||
"file": "S-004_indianapolis_assembly.json",
|
||||
"staffer": "Alex Rivera",
|
||||
"contract": "indianapolis_assembly",
|
||||
"client": "Pioneer Assembly — Indianapolis Plant Expansion"
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user