/* global React */
// AURA Research Labs — admin-only. Every figure on this page comes from /api/research/*,
// i.e. from the research database. Nothing here is simulated agent activity.
const { useState: useStateR, useEffect: useEffectR, useCallback: useCallbackR } = React;
const TABS = [
["overview", "Executive Overview"], ["agents", "Agent Command Centre"], ["lab", "Experiment Laboratory"],
["board", "Strategy Leaderboard"], ["risk", "Risk Laboratory"], ["kb", "Knowledge Base"], ["approvals", "Approval Centre"],
];
const fx = (v, d = 3) => (v == null || Number.isNaN(v) ? "—" : typeof v === "number" ? v.toFixed(d) : String(v));
const when = (s) => (s ? String(s).replace("T", " ").slice(0, 16) : "—");
function DecisionPill({ d }) {
if (!d) return pending;
const cls = d === "REJECTED" ? "sell" : d === "INCONCLUSIVE" ? "neutral" : "buy";
return {d};
}
function Stat({ label, value, meta }) {
return (
{label}
{value}
{meta &&
{meta}
}
);
}
function Panel({ title, right, children, pad = true }) {
return (
);
}
function Overview({ ov, onPause }) {
if (!ov) return null;
const outcomes = ov.experiments_by_outcome || {};
return (
<>
{!ov.audit_chain_intact && Audit chain BROKEN — history may have been edited.
}
{ov.paused ? "Resume" : "Pause"} research} pad={false}>
| Cycle | Status | Started | Finished |
{ov.cycles.map((c) => (
| {c.id} | {c.status} | {when(c.started_at)} | {when(c.finished_at)} |
))}
{!ov.cycles.length && | No cycles yet. |
}
| Budget | Remaining | Daily limit |
{ov.budgets.map((b) => (
| {b.key} | {fx(b.remaining_today, 1)} | {b.daily_limit} {b.unit} |
))}
proposals {JSON.stringify(ov.proposals_by_status)}
tasks {JSON.stringify(ov.task_queue)}
>
);
}
function Agents({ agents, handoffs }) {
return (
<>
| Role | Mandate | Permissions | Last task | Done | Failed | Handoffs |
{agents.map((a) => (
| {a.code} {a.name}{a.uses_llm && LLM-capable} |
{a.mandate} |
{a.permissions.join(", ")} |
{a.last_task ? `${a.last_task.kind} · ${a.last_task.status} · ${a.last_task.cycle}` : "never run"} |
{a.task_counts.done || 0} |
{a.task_counts.failed || 0} |
{a.handoffs_sent}/{a.handoffs_received} |
))}
| At | From → To | Subject | Artifact |
{handoffs.map((h) => (
| {when(h.at)} | {h.from} → {h.to} | {h.subject} |
{JSON.stringify(h.artifact).slice(0, 240)} |
))}
>
);
}
function Lab({ exps }) {
const [sel, setSel] = useStateR(null);
const [detail, setDetail] = useStateR(null);
useEffectR(() => { if (sel) window.api.researchExperiment(sel).then(setDetail).catch(() => setDetail(null)); }, [sel]);
return (
| ID | Kind | Title | Decision | n | PF net |
{exps.map((e) => (
setSel(e.id)} style={{ cursor: "pointer", background: sel === e.id ? "var(--bg-2)" : undefined }}>
| {e.id} | {e.kind} | {e.title} |
| {e.n ?? "—"} | {fx(e.pf_net_live)} |
))}
{!exps.length && | No experiments yet. |
}
{detail ? (
{detail.decision_reason}
| Gate | Pass | Detail |
{Object.entries(detail.gates || {}).map(([k, v]) => (
| {k} | {v.pass ? "yes" : "NO"} | {v.detail} |
))}
Red team: blocking {JSON.stringify(detail.redteam.blocking || [])}; reproduced {String(detail.redteam.checks?.deterministic_rerun)};
independent implementation agrees {String(detail.redteam.checks?.independent_implementation?.agree)}
Risk ($300): suitable {String(detail.risk.suitable_for_300)} — min-lot loss ${fx(detail.risk.feasibility_300?.min_lot_loss_usd, 2)},
equity needed ${fx(detail.risk.feasibility_300?.equity_needed_at_pref_usd, 0)}
Revisit only if: {detail.revisit_when || "—"}
Provenance: {JSON.stringify(detail.result.provenance || {})}
) :
Pre-registration, gates, red-team, risk and lineage appear here.
}
);
}
function Board({ rows }) {
return (
| ID | Hypothesis | Development | Independent holdout | Shadow | Demo | PF net (CI) | t / z req |
{rows.map((r) => (
| {r.id} | {r.title} |
| {r.evidence.independent_holdout} | {r.evidence.shadow} | {r.evidence.demo} |
{fx(r.pf_net_live)} {r.pf_net_live_ci ? `[${fx(r.pf_net_live_ci[0])}, ${fx(r.pf_net_live_ci[1])}]` : ""} |
{fx(r.t_day_clustered, 2)} / {fx(r.z_required, 2)} |
))}
{!rows.length && | No research candidate has been decided yet. An empty leaderboard is an honest one. |
}
);
}
function RiskLab() {
const [eq, setEq] = useStateR(300);
const [rl, setRl] = useStateR(null);
useEffectR(() => { window.api.researchRiskLab(eq).then(setRl).catch(() => setRl(null)); }, [eq]);
if (!rl) return null;
return (
<>
Equity $ setEq(Number(e.target.value) || 300)}
style={{ width: 90, background: "var(--bg-2)", color: "var(--text-1)", border: "1px solid var(--line)", borderRadius: 4, padding: "3px 6px" }} />}>
{Object.entries(rl.limits_usd).map(([k, v]) => `${k} $${v}`).join(" · ")}
approved hash {rl.policy.approved_hash} · assumptions {JSON.stringify(rl.assumptions)}
| Stop (bp) | Stop ($) | Min-lot loss incl. costs | At 0.25% | At 0.50% | Equity needed at 0.25% | Losses to 10% DD halt |
{rl.feasibility.map((f) => (
| {f.stop_bp} | {fx(f.stop_price, 2)} |
${fx(f.min_lot_loss_usd, 2)} | {f.feasible_at_pref ? "yes" : "NO"} | {f.feasible_at_cap ? "yes" : "NO"} |
${Number(f.equity_needed_at_pref_usd).toLocaleString()} | {f.consecutive_min_lot_losses_to_dd_halt} |
))}
P(hit the 10% drawdown halt) = {fx(rl.risk_of_ruin.p_halt, 3)} — {rl.risk_of_ruin.assumptions}; 50% win, 1.5R, 200 trades, 0.25% risk.
>
);
}
function KB() {
const [q, setQ] = useStateR("");
const [kb, setKb] = useStateR(null);
const load = useCallbackR(() => window.api.researchKnowledge(q).then(setKb).catch(() => setKb(null)), [q]);
useEffectR(() => { load(); }, []);
if (!kb) return null;
return (
<>
| Code | Family | Status | Ledger | Why | Reopen requires |
{kb.families.map((f) => (
| {f.code} | {f.name} |
{f.status} |
{f.ledger_count} | {f.why} |
{f.reopen_requires} |
))}
setQ(e.target.value)} placeholder="semantic search, e.g. liquidity sweep"
style={{ background: "var(--bg-2)", color: "var(--text-1)", border: "1px solid var(--line)", borderRadius: 4, padding: "4px 8px", width: 260 }} />
} pad={false}>
| ID | Family | Hypothesis | Outcome | R |
{kb.ledger.map((r, i) => (
| {r.ledger_id} | {r.family} |
{r.hypothesis} |
{r.outcome} | {r.r} |
))}
| ID | Source | Title | Status | Why |
{kb.proposals.map((p) => (
| {p.id} | {p.source} | {p.title} |
{p.status} | {(p.screen.reasons || []).join("; ")} |
))}
>
);
}
function Approvals({ items, reload }) {
const decide = (id, approve) => {
const note = window.prompt(`${approve ? "Approve" : "Reject"} ${id} — note for the audit log:`, "") ?? null;
if (note === null) return;
window.api.researchDecide(id, approve, note).then(reload);
};
return (
| ID | Kind | Subject | Requested by | Status | |
{items.map((a) => (
| {a.id} | {a.kind} | {a.subject} |
{a.requested_by} | {a.status}{a.note ? ` — ${a.note}` : ""} |
{a.status === "pending" &&
} |
))}
{!items.length && | No requests. |
}
);
}
function ResearchLabs() {
const [tab, setTab] = useStateR("overview");
const [data, setData] = useStateR({ ov: null, agents: [], handoffs: [], exps: [], board: [], approvals: [] });
const [error, setError] = useStateR(null);
const load = useCallbackR(() => {
Promise.all([window.api.researchOverview(), window.api.researchAgents(), window.api.researchHandoffs(),
window.api.researchExperiments(), window.api.researchLeaderboard(), window.api.researchApprovals()])
.then(([ov, agents, handoffs, exps, board, approvals]) => { setData({ ov, agents, handoffs, exps, board, approvals }); setError(null); })
.catch((e) => setError(String(e)));
}, []);
useEffectR(() => { load(); const t = setInterval(load, 30000); return () => clearInterval(t); }, [load]);
const pause = () => window.api.researchControl(!data.ov.paused).then(load);
return (
<>
AURA Research Labs
Research only — this area cannot place, change or close a trade.
{error &&
{error}
}
{TABS.map(([k, label]) => (
))}
{tab === "overview" &&
}
{tab === "agents" &&
}
{tab === "lab" &&
}
{tab === "board" &&
}
{tab === "risk" &&
}
{tab === "kb" &&
}
{tab === "approvals" &&
}
>
);
}
window.ResearchLabs = ResearchLabs;