Final sweep: Crucible fixes, Path Tracer polish, forge progress, tests green.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Align dashboard subtitle default and UpsertAgent tests with fleet label behavior; WebSocket coalesce and PathForge hardening; Crucible expanded ops and visual DV fixes; Vitest 610/610 and full test-suite pass; trim PROBLEMS.md to open items only.
This commit is contained in:
AetherForge
2026-06-06 18:07:47 -07:00
parent e65753ce49
commit 6372b07e6c
40 changed files with 1495 additions and 794 deletions

View File

@@ -113,6 +113,8 @@ export default function PathTracerPage() {
const [showQR, setShowQR] = useState(false);
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
const [autoEndCountdown, setAutoEndCountdown] = useState<number | null>(null);
const autoEndRef = useRef<ReturnType<typeof setInterval> | null>(null);
// Use WebSocket agents; fall back to REST on mount if WebSocket hasn't populated yet.
const agents = wsAgents.length > 0 ? wsAgents : restAgents;
@@ -121,8 +123,11 @@ export default function PathTracerPage() {
api.listAgents().then(setRestAgents).catch(() => {});
}, []);
// Stop polling on unmount.
useEffect(() => () => { if (pollRef.current) clearInterval(pollRef.current); }, []);
// Stop polling and auto-end timer on unmount.
useEffect(() => () => {
if (pollRef.current) clearInterval(pollRef.current);
if (autoEndRef.current) clearInterval(autoEndRef.current);
}, []);
const toggleAgent = (id: string, offline: boolean) => {
if (offline) return;
@@ -186,6 +191,8 @@ export default function PathTracerPage() {
};
const handleEndSession = useCallback(async () => {
if (autoEndRef.current) { clearInterval(autoEndRef.current); autoEndRef.current = null; }
setAutoEndCountdown(null);
if (!sessionID) return;
try {
await api.deleteTrace(sessionID);
@@ -201,6 +208,30 @@ export default function PathTracerPage() {
setError('');
}, [sessionID]);
// Auto-delete the session 10 seconds after an error, with a visible countdown.
useEffect(() => {
if (!error || !sessionID) return;
if (autoEndRef.current) clearInterval(autoEndRef.current);
const COUNTDOWN = 10;
setAutoEndCountdown(COUNTDOWN);
let remaining = COUNTDOWN;
autoEndRef.current = setInterval(() => {
remaining -= 1;
if (remaining <= 0) {
clearInterval(autoEndRef.current!);
autoEndRef.current = null;
setAutoEndCountdown(null);
handleEndSession();
} else {
setAutoEndCountdown(remaining);
}
}, 1000);
return () => {
if (autoEndRef.current) { clearInterval(autoEndRef.current); autoEndRef.current = null; }
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [error, sessionID]);
const isWindows = (a: Agent) =>
!!(a.platform?.toLowerCase().includes('win') || a.platform?.toLowerCase().includes('windows'));
@@ -222,7 +253,17 @@ export default function PathTracerPage() {
</div>
</header>
{error && <div className="pt-error-banner"> {error}</div>}
{error && (
<div className="pt-error-banner" role="alert">
<strong> Session Error</strong>
<div style={{ marginTop: '0.3rem' }}>{error}</div>
{sessionID && autoEndCountdown !== null && (
<div style={{ marginTop: '0.3rem', opacity: 0.8 }}>
Session will be terminated in {autoEndCountdown}s&hellip;
</div>
)}
</div>
)}
{tracing && !allHopsReady && !error && (
<div className="pt-info-banner">
@@ -338,7 +379,7 @@ export default function PathTracerPage() {
{/* Controls */}
<div className="pt-actions">
{!tracing && (
{!tracing && !sessionID && (
<button
className="pt-btn pt-btn-primary"
disabled={selected.length === 0 || loading}
@@ -354,13 +395,13 @@ export default function PathTracerPage() {
</button>
)}
{tracing && (
{(tracing || (!!error && !!sessionID)) && (
<button className="pt-btn pt-btn-danger" onClick={handleEndSession}>
End Session
End Session{autoEndCountdown !== null && ` (${autoEndCountdown}s)`}
</button>
)}
{!tracing && selected.length > 0 && (
{!tracing && !sessionID && selected.length > 0 && (
<button className="pt-btn pt-btn-ghost" onClick={() => setSelected([])}>
Clear
</button>