const timelineRenderState={}; const activityFeedState={lastItems:[],runId:null}; const PROGRESS_TERMINAL_STATUSES=new Set(['done','success','failed','full_inference_success','full_inference_candidate_health_passed','manual_hardware_required','generated_needs_manual_hardware','technical_blocker','health_only','repair_success','repair_failed','succeeded','waiting_manual_action','blocked','stale','cancelled','canceled']); const WORKER_STEP_ORDER=[ 'bootstrap','dependencies','auth','model_analysis','workspace','node','pi_install','pi_config','pi_run','pi_verification', 'metadata_sanitize','requirements_sanitize','hardware_strategy','create_space_hardware','create_space','repair','upload_files', 'space_runtime','space_logs','api_validation','live_wait','generation_smoke','inference_gate','report_write','done','failure' ]; const COMPACT_TIMELINE_GROUPS=[ {id:'bootstrap',label:'Bootstrap',steps:['bootstrap']}, {id:'dependencies',label:'Dependencies',steps:['dependencies']}, {id:'auth_model',label:'Auth + model',steps:['auth','model_analysis']}, {id:'workspace_node',label:'Workspace/node',steps:['workspace','node']}, {id:'pi_setup',label:'Pi setup',steps:['pi_install','pi_config']}, {id:'pi_run',label:'Pi run',steps:['pi_run']}, {id:'pi_verify',label:'Pi verify',steps:['pi_verification','metadata_sanitize','requirements_sanitize']}, {id:'hardware',label:'Hardware',steps:['hardware_strategy','create_space_hardware']}, {id:'create_space',label:'Create Space',steps:['create_space']}, {id:'upload_runtime',label:'Upload/runtime',steps:['upload_files','space_runtime','space_logs','repair']}, {id:'validation',label:'Validation',steps:['api_validation','live_wait','generation_smoke','inference_gate']}, {id:'report',label:'Report',steps:['report_write','done']} ]; const progressMotionState={lastProgress:null,lastStatus:null,lastStep:null}; const progressStability={runId:null,progress:0,timeline:null,furthestIndex:-1,status:'',currentStep:null}; const elapsedClock={baseSeconds:0,baseAt:0,isRunning:false}; function isFailureStatus(status){ const s=String(status||'').toLowerCase(); return s.includes('failed')||s.includes('error')||s==='failure'||s==='technical_blocker'; } function isSuccessStatus(status){ const s=String(status||'').toLowerCase(); return s.includes('success')||s.includes('succeed')||s.includes('passed')||s==='done'||s==='completed'||s==='health_only'||s==='repair_success'||s==='full_inference_candidate_health_passed'||s==='full_inference_success'; } function isStoppedStatus(status){ const s=String(status||'').toLowerCase(); return s==='cancelled'||s==='canceled'||s==='stopped'||s==='blocked'||s==='stale'; } function visualStatusClass(status){ if(isFailureStatus(status))return'error'; if(isStoppedStatus(status))return'stopped'; if(isSuccessStatus(status))return'success'; const s=String(status||'').toLowerCase(); if(s.includes('running')||s.includes('queued')||s.includes('pending')||s.includes('waiting')||s.includes('building'))return'running'; return'neutral'; } function groupForWorkerStep(step){ return COMPACT_TIMELINE_GROUPS.find(g=>g.steps.includes(String(step||'')))||null; } function latestEventIndexForGroup(group){ if(!group)return -1; let idx=-1; (activityFeedState.lastItems||[]).forEach((event,i)=>{ if(group.steps.includes(String(event.step||'')))idx=i; }); return idx; } function failedEventInfoForGroup(group){ if(!group)return null; let info=null; (activityFeedState.lastItems||[]).forEach((event,i)=>{ if(group.steps.includes(String(event.step||''))&&isFailureStatus(event.status)){ info={event,index:i}; } }); return info; } function failedItemInfoForGroup(group,byStep){ if(!group||!byStep)return null; let info=null; group.steps.forEach((step,i)=>{ const item=byStep.get(step); if(item&&isFailureStatus(item.status))info={event:item,index:i,fromTimeline:true}; }); return info; } function textOfEvent(event={}){ return `${event.step||''} ${event.status||''} ${event.message||''} ${event.hardware||''} ${event.selected_hardware||''} ${event.fallback_hardware||''}`.toLowerCase(); } function isHardwareFallbackEvent(event={}){ const text=textOfEvent(event); return text.includes('fallback')||text.includes('zerogpu')||text.includes('zero gpu')||text.includes('zero-a10g')||text.includes('hardware_fallback')||text.includes('a10g')||text.includes('l40')||text.includes('h200'); } function groupHasHardwareFallback(group){ if(!group||group.id!=='hardware')return false; return (activityFeedState.lastItems||[]).some(event=>group.steps.includes(String(event.step||''))&&isHardwareFallbackEvent(event)); } function isExpectedFallbackFailure(group,failed){ return Boolean(group&&group.id==='hardware'&&failed&&isHardwareFallbackEvent(failed.event)); } function compactReasonText(value,max=190){ const text=String(value||'').replace(/\s+/g,' ').trim(); if(!text)return ''; return text.length>max?text.slice(0,max-1)+'…':text; } function eventReason(event={}){ const data=event.data||{}; const raw= event.reason|| event.error|| event.error_message|| event.exception|| event.details|| data.reason|| data.hf_error_detail|| data.hf_error_message|| data.error_detail|| data.response_text|| data.response_body|| data.error|| data.exception|| data.details|| event.message|| ''; return compactReasonText(typeof raw==='string'?raw:JSON.stringify(raw)); } function zeroGpuReason(event={}){ const reason=eventReason(event); const lower=reason.toLowerCase(); if(lower.includes('quota')||lower.includes('limit')||lower.includes('capacity')||lower.includes('usage')||lower.includes('exceeded')){ return reason; } if(isHardwareFallbackEvent(event)){ return reason||'ZeroGPU was unavailable for this user/session, so the fallback GPU was used.'; } return reason; } function failureReasonForGroup(group){ const failed=failedEventInfoForGroup(group); if(failed)return eventReason(failed.event); return ''; } function fallbackReasonForGroup(group){ if(!group||group.id!=='hardware')return ''; const failed=failedEventInfoForGroup(group); if(failed&&isExpectedFallbackFailure(group,failed))return zeroGpuReason(failed.event); const event=(activityFeedState.lastItems||[]).find(e=>group.steps.includes(String(e.step||''))&&isHardwareFallbackEvent(e)); return event?zeroGpuReason(event):''; } function reasonForTimelineItem(item={}){ if(item.status==='fallback')return item.reason||'Fallback GPU route was used.'; if(item.status==='failed'||item.status==='recovered')return item.reason||''; return ''; } function failedTimelineStatusForGroup(group,overallStatus){ const failed=failedEventInfoForGroup(group); if(!failed)return ''; const latestIdx=latestEventIndexForGroup(group); const overall=visualStatusClass(overallStatus); const superseded=latestIdx>failed.index; if(isExpectedFallbackFailure(group,failed)&&superseded)return 'fallback'; if(overall==='error')return 'failed'; return superseded?'recovered':'failed'; } function firstFailedGroupIndex(overallStatus){ const overall=visualStatusClass(overallStatus); let first=-1; COMPACT_TIMELINE_GROUPS.forEach((group,idx)=>{ const failed=failedEventInfoForGroup(group); if(!failed)return; const historical=failedTimelineStatusForGroup(group,overallStatus); if(historical==='fallback')return;if((overall==='error'||historical==='failed')&&(first<0||idx=0)return COMPACT_TIMELINE_GROUPS[first]?.id||''; const recovered=COMPACT_TIMELINE_GROUPS.find(group=>failedTimelineStatusForGroup(group,overallStatus)==='recovered'); if(recovered)return recovered.id||''; const fallback=COMPACT_TIMELINE_GROUPS.find(group=>failedTimelineStatusForGroup(group,overallStatus)==='fallback'||groupHasHardwareFallback(group)); return fallback?.id||''; } function stepOrderIndex(step){ const idx=WORKER_STEP_ORDER.indexOf(String(step||'')); return idx<0?-1:idx; } function normalizeTimelineOverallStatus(status){ const s=String(status||'').toLowerCase(); if(PROGRESS_TERMINAL_STATUSES.has(s))return s; return s; } function timelineIcon(status){ status=String(status||'pending'); if(status==='done'||status==='completed')return'✓'; if(status==='running')return'•'; if(status==='failed')return'!'; if(status==='fallback')return'⇄'; if(status==='recovered'||status==='superseded')return'↺'; if(status==='blocked'||status==='cancelled'||status==='stopped'||status==='after_fail')return'■'; if(status==='skipped')return'↷'; return'○'; } function timelineStatus(status,overallStatus=''){ status=String(status||'pending').toLowerCase(); const overall=normalizeTimelineOverallStatus(overallStatus); const stopped=overall&&PROGRESS_TERMINAL_STATUSES.has(overall)&&!['success','succeeded','done','full_inference_success','full_inference_candidate_health_passed'].includes(overall); if(status==='failed'||status==='error'||status==='failure')return'status-failed'; if(status==='fallback')return'status-fallback'; if(status==='recovered'||status==='superseded')return'status-warn'; if(status==='after_fail'||status==='post_failure')return'status-after-fail'; if(status==='blocked'||status==='cancelled'||status==='canceled'||status==='stopped')return'status-stopped'; if(status==='done'||status==='completed'||status==='success')return'status-done'; if(status==='running'||status==='waiting')return stopped?'status-stopped':'status-running'; if(status==='skipped')return'status-skipped'; return stopped?'status-muted':'status-pending'; } function timelineStepKey(x,i){ return String(x.step||x.id||x.label||`step-${i}`); } function animateClass(el,klass,duration=900){ if(!el)return; el.classList.remove(klass); void el.offsetWidth; el.classList.add(klass); window.setTimeout(()=>el.classList.remove(klass),duration); } function eventStableKey(event={}){ return `${event.ts||''}|${event.step||''}|${event.status||''}|${event.message||''}`; } function mergeStableEvents(runId,items){ const incoming=(items||[]).filter(Boolean); if(activityFeedState.runId!==runId){ activityFeedState.runId=runId; activityFeedState.lastItems=[]; } const byKey=new Map(); [...activityFeedState.lastItems,...incoming].forEach(event=>{ const key=eventStableKey(event); if(key.trim()!=='|||')byKey.set(key,event); }); const merged=[...byKey.values()].sort((a,b)=>String(a.ts||'').localeCompare(String(b.ts||''))); activityFeedState.lastItems=merged; return merged; } function activeTimelineStepKey(items=[]){ const active=(items||[]).find(x=>['running','stopped','failed','blocked','cancelled','canceled'].includes(String(x.status||'').toLowerCase())); if(active)return String(active.step||active.id||active.label||''); const done=[...(items||[])].reverse().find(x=>['done','completed','success'].includes(String(x.status||'').toLowerCase())); return done?String(done.step||done.id||done.label||''):''; } function activeTimelineItem(items=[]){ const key=activeTimelineStepKey(items); return (items||[]).find(x=>String(x.step||x.id||x.label||'')===key)||null; } function eventMessage(event){ return event?.message||event?.step||'Waiting for worker events.'; } function ensureTimelineStepVisible(stepKey){ const root=$('timeline'); if(!root||!stepKey)return; if(!root.dataset.userScrolled&&root.scrollLeft===0){const early=new Set(['bootstrap','dependencies','auth_model']);if(early.has(stepKey)){root.dataset.visibleStep=stepKey;return;}} const safeStep=(typeof CSS!=='undefined'&&CSS.escape)?CSS.escape(stepKey):stepKey.replace(/"/g,'\\"'); const target=root.querySelector(`[data-step="${safeStep}"]`); if(!target)return; const margin=18; const visibleLeft=root.scrollLeft; const visibleRight=visibleLeft+root.clientWidth; const targetLeft=target.offsetLeft; const targetRight=targetLeft+target.offsetWidth; let nextLeft=null; if(targetLeftvisibleRight-margin)nextLeft=Math.max(0,targetRight-root.clientWidth+margin); if(nextLeft!==null){ try{root.scrollTo({left:nextLeft,behavior:'smooth'});} catch(_){root.scrollLeft=nextLeft;} } root.dataset.visibleStep=stepKey; } function compactTimelineItems(items=[],overallStatus=''){ const byStep=new Map((items||[]).map(item=>[String(item.step||item.id||item.label||''),item])); const terminalRaw=new Set(['failed','blocked','stopped','cancelled','canceled','recovered','fallback','after_fail','post_failure']); const overall=visualStatusClass(overallStatus); const firstFailedIdx=firstFailedGroupIndex(overallStatus); return COMPACT_TIMELINE_GROUPS.map((group,groupIndex)=>{ const groupItems=group.steps.map(step=>byStep.get(step)).filter(Boolean); const doneCount=groupItems.filter(item=>['done','completed','success'].includes(String(item.status||'').toLowerCase())).length; const active=groupItems.find(item=>['running','waiting','stopped','failed','blocked','cancelled','canceled'].includes(String(item.status||'').toLowerCase())); const itemFailure=failedItemInfoForGroup(group,byStep); const historicalFailure=failedTimelineStatusForGroup(group,overallStatus)||(itemFailure?(groupHasHardwareFallback(group)?'fallback':(overall==='error'?'failed':'recovered')):''); let status='pending'; if(historicalFailure)status=historicalFailure; else if(overall==='error'&&firstFailedIdx>=0&&groupIndex>firstFailedIdx&&groupItems.length)status='after_fail'; else if(active)status=String(active.status||'running').toLowerCase(); else if(groupItems.length&&doneCount>=group.steps.length)status='done'; else if(doneCount>0)status='running'; const failedInfo=failedEventInfoForGroup(group)||itemFailure; const reason=historicalFailure==='fallback'?fallbackReasonForGroup(group):(historicalFailure?failureReasonForGroup(group):''); const activeSubstep=historicalFailure==='fallback'?'fallback GPU':failedInfo&&historicalFailure?(failedInfo.event.step||failedInfo.event.message||'failed'):active?.step||active?.id||active?.label||groupItems[groupItems.length-1]?.step||''; const activeIndex=activeSubstep?Math.max(0,group.steps.indexOf(activeSubstep)):-1; let percent=0; if(status==='done')percent=100; else if(status==='after_fail')percent=doneCount?Math.round((doneCount/group.steps.length)*100):0; else if(activeIndex>=0)percent=Math.round(((activeIndex+0.45)/group.steps.length)*100); else if(doneCount>0)percent=Math.round((doneCount/group.steps.length)*100); if(terminalRaw.has(status))percent=Math.max(percent,doneCount?Math.round((doneCount/group.steps.length)*100):8); return { step:group.id, id:group.id, label:group.label, status, full_steps:group.steps, done_count:doneCount, total_count:group.steps.length, percent:Math.max(0,Math.min(100,percent)), active_substep:activeSubstep, reason }; }); } function currentCompactGroupKey(fullItems=[],overallStatus=''){ const problem=firstProblemGroupKey(fullItems,overallStatus); if(problem)return problem; const activeKey=activeTimelineStepKey(fullItems); const group=COMPACT_TIMELINE_GROUPS.find(g=>g.steps.includes(activeKey)); return group?.id||activeKey; } function timelineTraceModel(list=[],overallStatus=''){ const overall=visualStatusClass(overallStatus); const failedIndex=list.findIndex(item=>String(item.status||'').toLowerCase()==='failed'); const fallbackIndex=list.findIndex(item=>String(item.status||'').toLowerCase()==='fallback'); const recoveredIndex=list.findIndex(item=>String(item.status||'').toLowerCase()==='recovered'); if(failedIndex>=0){ return {mode:'failed',index:failedIndex,key:list[failedIndex]?.step||'',label:list[failedIndex]?.label||'failed step',reason:reasonForTimelineItem(list[failedIndex]),message:`Failed at ${list[failedIndex]?.label||'this stage'}. Later steps are post-failure cleanup or reporting, not a successful build path.`}; } if(fallbackIndex>=0){ return {mode:'fallback',index:fallbackIndex,key:list[fallbackIndex]?.step||'',label:list[fallbackIndex]?.label||'hardware fallback',reason:reasonForTimelineItem(list[fallbackIndex]),message:`ZeroGPU was not used for this run. The factory switched to the fallback GPU and continued successfully.`}; } if(recoveredIndex>=0){ return {mode:'recovered',index:recoveredIndex,key:list[recoveredIndex]?.step||'',label:list[recoveredIndex]?.label||'recovered step',reason:reasonForTimelineItem(list[recoveredIndex]),message:`Recovered after an incident at ${list[recoveredIndex]?.label||'this stage'}. The trace continues after recovery.`}; } return {mode:'clean',index:-1,key:'',label:'',message:''}; } function renderTimelineTraceNote(trace){ const note=$('timelineTraceNote'); if(!note)return; if(!trace||trace.mode==='clean'){ note.hidden=true; note.className='timeline-trace-note'; note.innerHTML=''; return; } note.hidden=false; note.className=`timeline-trace-note ${trace.mode}`; const icon=trace.mode==='failed'?'✕':(trace.mode==='fallback'?'⇄':'↺'); const title=trace.mode==='failed'?'Failure trace':(trace.mode==='fallback'?'Hardware fallback':'Recovered incident'); const reason=trace.reason?`

Reason: ${escapeHtml(trace.reason)}

`:''; note.innerHTML=`${icon}${escapeHtml(title)}

${escapeHtml(trace.message)}

${reason}`; } function traceClassForStep(trace,index){ if(!trace||trace.mode==='clean')return''; if(index===trace.index)return trace.mode==='failed'?'trace-breakpoint':(trace.mode==='fallback'?'trace-fallback':'trace-recovered'); if(trace.mode==='failed'&&index>trace.index)return'trace-post-failure'; return''; } function renderTimeline(items,overallStatus=''){ const root=$('timeline'); if(!root)return; if(!root.dataset.scrollBound){root.dataset.scrollBound='1';root.addEventListener('scroll',()=>{root.dataset.userScrolled='1'},{passive:true});} const fullList=items||[]; const list=compactTimelineItems(fullList,overallStatus); const trace=timelineTraceModel(list,overallStatus); renderTimelineTraceNote(trace); root.classList.toggle('trace-failed',trace.mode==='failed'); root.classList.toggle('trace-recovered',trace.mode==='recovered'); root.classList.toggle('trace-fallback',trace.mode==='fallback'); const currentKey=trace.key||currentCompactGroupKey(fullList,overallStatus); const signature=list.map((x,i)=>`${timelineStepKey(x,i)}:${timelineStatus(x.status,overallStatus)}:${x.label||''}:${overallStatus||''}:${trace.mode}:${trace.index}`).join('|'); if(root.dataset.signature===signature){ensureTimelineStepVisible(currentKey);return;} root.dataset.signature=signature; const nextState={}; root.innerHTML=list.map((x,i)=>{ const key=timelineStepKey(x,i); const st=timelineStatus(x.status,overallStatus); const rawStatus=String(x.status||'pending').toLowerCase(); const prev=timelineRenderState[key]; const changed=prev&&prev!==st; nextState[key]=st; const label=st.replace(/^status-/,''); const percent=Number(x.percent||0); const meta=x.total_count>1?`${x.done_count||0}/${x.total_count}${x.active_substep?' · '+x.active_substep:''}`:label; const traceClass=traceClassForStep(trace,i); const reason=reasonForTimelineItem(x); const reasonLine=reason?`${escapeHtml(reason)}`:''; const title=reason?` title="${escapeHtml(reason)}"`:''; return `
${timelineIcon(rawStatus)}
${escapeHtml(x.label||`Step ${i+1}`)}${escapeHtml(meta)}${reasonLine}
`; }).join(''); Object.keys(timelineRenderState).forEach(k=>delete timelineRenderState[k]); Object.assign(timelineRenderState,nextState); root.querySelectorAll('.step.status-changed').forEach(step=>{ animateClass(step,'status-flash',1000); const dot=step.querySelector('.step-dot'); animateClass(dot,'dot-pop',700); }); ensureTimelineStepVisible(currentKey); } function contextualEventClass(item={},furthestIdx=-1){ const raw=String(item.status||'').toLowerCase(); const idx=stepOrderIndex(item.step); if(isFailureStatus(raw)&&isHardwareFallbackEvent(item))return'hardware-fallback'; if(isFailureStatus(raw)&&idx>=0&&furthestIdx>idx)return'warn recovered'; if(isFailureStatus(raw))return'error'; if(isStoppedStatus(raw))return'warn'; if(isSuccessStatus(raw))return'success'; if(raw.includes('running')||raw.includes('started')||raw.includes('waiting')||raw.includes('pending'))return'running'; return statusClass(raw); } function contextualEventLabel(item={},klass=''){ const raw=String(item.status||'').toLowerCase(); if(klass.includes('hardware-fallback'))return`${raw||'fallback'} · fallback route`; if(klass.includes('recovered'))return`${raw||'failed'} · superseded`; return raw; } function renderActivityFeed(items){ const root=$('activityFeed'); if(!root)return; const list=mergeStableEvents(String(state.runId||''),items||[]); const visible=[...list].reverse(); const signature=visible.map(item=>`${item.ts||''}:${item.step||''}:${item.status||''}:${item.message||''}`).join('|'); if(root.dataset.signature===signature)return; root.dataset.signature=signature; root.classList.add('show-all'); if(!list.length){ root.innerHTML='
No activity yet. Waiting for the first worker event.
'; return; } const furthestIdx=timelineFurthestIndex(progressStability.timeline||[]); root.innerHTML=visible.map(item=>{ const klass=contextualEventClass(item,furthestIdx); const label=contextualEventLabel(item,klass); const reason=eventReason(item); const reasonHtml=reason&&reason!==String(item.message||'').trim()?`

${escapeHtml(reason)}

`:''; return `
${escapeHtml(item.message||'Event received')}

${escapeHtml(item.step||'event')} · ${escapeHtml(label||'')}

${reasonHtml}
${escapeHtml(item.agent||'Factory Agent')}
`; }).join(''); root.scrollTop=0; } function renderDiagnostics(d={},header={}){ setText('diagBuildStatus',badgeLabel(d.build_status||'pending')); setText('diagBuildStep',header.current_phase_label||'Waiting for run data'); const chips=$('statusChips'); if(chips){ const data=[['Build',d.build_status],['API',d.api_status],['Tests',d.tests_status],['Verdict',d.verdict_status]]; chips.innerHTML=data.map(([k,v])=>`${escapeHtml(k)}${escapeHtml(badgeLabel(v||'pending'))}`).join(''); } const issue=$('issueCard'); if(issue){ const has=Boolean(d.issue&&d.issue.title); issue.hidden=!has; if(has){ issue.className='issue-card '+statusClass(d.issue.status||d.issue.title); setText('issueTitle',d.issue.title); setText('issueDetail',d.issue.detail||d.issue.status||'Review logs.'); } } const rules=$('zeroGpuChecklist'); if(rules)rules.innerHTML=(d.zerogpu_rules||[]).map(r=>`
  • ${escapeHtml(r.label)}${escapeHtml(r.status||'pending')}
  • `).join(''); } function renderSpaceTestPreview(s={}){ const st=$('spaceTestStatus'); if(st){ st.textContent=badgeLabel(s.status||'pending'); st.className='badge '+statusClass(s.status); } setText('spaceTestOutput',s.output_artifact||s.endpoint||'Waiting for API test'); setText('spaceTestVerdict',s.verdict||'pending'); } function animateProgressBar(progress,status,currentStep){ const fill=$('progressFill'); if(!fill)return; const value=Math.max(0,Math.min(100,Number(progress||0))); const previous=progressMotionState.lastProgress; let klass=visualStatusClass(status);if(value>=100&&klass==='neutral')klass='success'; fill.style.setProperty('--progress-target',`${value}%`); fill.style.width=`${value}%`; fill.classList.remove('is-success','is-error','is-running-state','is-stopped','is-neutral'); fill.classList.add(`is-${klass==='running'?'running-state':klass}`); const s=String(status||'').toLowerCase();fill.classList.toggle('is-running',klass==='running'||s==='pending'||s==='scheduled'||s==='waiting'); if(previous!==null&&Math.round(previous)!==Math.round(value)){ animateClass(fill,'progress-bump',650); const pct=$('progressPercent'); animateClass(pct,'value-bump',650); } progressMotionState.lastProgress=value; progressMotionState.lastStep=currentStep||progressMotionState.lastStep; } function animateStatusBadge(status){ const el=$('progressStatus'); if(!el)return; const next=String(status||'').toLowerCase(); if(progressMotionState.lastStatus&&progressMotionState.lastStatus!==next){ animateClass(el,'status-bump',800); } progressMotionState.lastStatus=next; } function setElapsedClock(seconds,status){ const s=String(status||'').toLowerCase(); elapsedClock.baseSeconds=Math.max(0,Number(seconds||0)); elapsedClock.baseAt=Date.now(); elapsedClock.isRunning=Boolean(state.runId)&&!TERMINAL_STATUSES.has(s); setText('elapsed',fmtSeconds(elapsedClock.baseSeconds)); } window.setInterval(()=>{ if(!elapsedClock.isRunning)return; const live=elapsedClock.baseSeconds+Math.floor((Date.now()-elapsedClock.baseAt)/1000); setText('elapsed',fmtSeconds(live)); },1000); function progressIsSuccessStatus(status){ const s=String(status||'').toLowerCase(); return ['success','succeeded','done','full_inference_success','full_inference_candidate_health_passed'].includes(s); } function progressIsTerminalStatus(status){ const s=String(status||'').toLowerCase(); return PROGRESS_TERMINAL_STATUSES.has(s); } function timelineFurthestIndex(items){ const list=items||[]; let max=-1; list.forEach((item,i)=>{ const st=String(item.status||'pending').toLowerCase(); if(!['pending','status-pending','muted','status-muted'].includes(st))max=i; }); return max; } function cloneTimeline(items){ return (items||[]).map(x=>({...x})); } function resetProgressStability(runId=null){ progressStability.runId=runId; progressStability.progress=0; progressStability.timeline=null; progressStability.furthestIndex=-1; progressStability.status=''; progressStability.currentStep=null; progressMotionState.lastProgress=null; progressMotionState.lastStatus=null; progressMotionState.lastStep=null; const timeline=$('timeline'); if(timeline){timeline.dataset.signature='';timeline.dataset.visibleStep='';timeline.dataset.userScrolled='';timeline.classList.remove('trace-failed','trace-recovered','trace-fallback');timeline.scrollLeft=0;}const note=$('timelineTraceNote');if(note){note.hidden=true;note.innerHTML='';note.className='timeline-trace-note';} } function stabilizeProgressPayload(p={},view=null){ const runId=String(p.run_id||view?.run_id||state.runId||''); if(progressStability.runId!==runId)resetProgressStability(runId); const status=String(view?.header?.status||p.status||p.state?.status||'').toLowerCase(); const terminal=progressIsTerminalStatus(status); const success=progressIsSuccessStatus(status); let rawProgress=Number(p.progress||0); let rawTimeline=cloneTimeline(p.timeline||view?.pipeline||[]); const rawIndex=timelineFurthestIndex(rawTimeline); if(success){ progressStability.progress=100; progressStability.timeline=rawTimeline; progressStability.furthestIndex=Math.max(progressStability.furthestIndex,rawIndex); }else if(terminal){ progressStability.progress=Math.max(progressStability.progress,rawProgress); if(rawIndex>=progressStability.furthestIndex||!progressStability.timeline){ progressStability.timeline=rawTimeline; progressStability.furthestIndex=rawIndex; }else{ rawTimeline=cloneTimeline(progressStability.timeline); } }else{ if(rawProgress>=progressStability.progress){ progressStability.progress=rawProgress; }else{ rawProgress=progressStability.progress; } if(rawIndex>=progressStability.furthestIndex||!progressStability.timeline){ progressStability.timeline=rawTimeline; progressStability.furthestIndex=rawIndex; }else{ rawTimeline=cloneTimeline(progressStability.timeline); } } const activeItem=activeTimelineItem(rawTimeline); progressStability.currentStep=activeItem?.step||activeItem?.id||activeItem?.label||progressStability.currentStep; progressStability.status=status; return { progress:success?100:Math.max(rawProgress,progressStability.progress), timeline:rawTimeline, status, currentStep:activeItem?.step||activeItem?.id||activeItem?.label||'', currentStepLabel:activeItem?.label||activeItem?.step||activeItem?.id||'' }; } function renderRunViewModel(view,p={}){ const header=view.header||{}; const sm=view.status_model||{}; const stableEvents=mergeStableEvents(String(p.run_id||view.run_id||state.runId||''),p.events||[]); const stable=stabilizeProgressPayload(p,view); const progress=Number(stable.progress??(header.status==='succeeded'?100:0)); const runStatus=p.status||header.status; const visualRunStatus=p.visual_status||runStatus; animateProgressBar(progress,visualRunStatus,stable.currentStep); setText('progressPercent',`${Math.round(progress)}%`); setText('progressTitle',header.title||view.run_id||'Live job progress'); const statusEl=$('progressStatus'); if(statusEl){ statusEl.textContent=badgeLabel(runStatus||'Running'); statusEl.className='badge '+statusClass(visualRunStatus); animateStatusBadge(visualRunStatus); } const latestEvent=stableEvents[stableEvents.length-1]; setText('currentStep',stable.currentStepLabel||p.current_step_label||'—'); setText('lastEvent',eventMessage(latestEvent)||p.last_event||'Waiting for worker events.'); setElapsedClock(p.elapsed_seconds??header.elapsed_seconds,runStatus); setText('lastPolled',new Date().toLocaleTimeString()); setText('detailSpace',header.space||'—'); setText('detailJobStatus',header.raw_status||runStatus||'—'); setText('detailGateStatus',sm.verdict||'—'); setText('detailTraces',Array.isArray(p.events)?`${p.events.length} events`:'—'); const cancel=$('cancelRun'); if(cancel){ const s=String(runStatus||'').toLowerCase(); cancel.disabled=!(state.runId&&view.links?.job_url&&!TERMINAL_STATUSES.has(s)); } renderTimeline(stable.timeline,runStatus); renderActivityFeed(stableEvents); renderDiagnostics(view.diagnostics||{},header); renderSpaceTestPreview(view.space_test||{}); renderManualAction({view,...p}); } function renderProgress(p){ if(p.view){ renderRunViewModel(p.view,p); return; } const stableEvents=mergeStableEvents(String(p.run_id||state.runId||''),p.events||[]); const stable=stabilizeProgressPayload(p,null); const progress=Number(stable.progress||0); animateProgressBar(progress,p.visual_status||p.status,stable.currentStep); setText('progressPercent',`${Math.round(progress)}%`); const latestEvent=stableEvents[stableEvents.length-1]; setText('currentStep',stable.currentStepLabel||p.current_step_label||'—'); setText('lastEvent',eventMessage(latestEvent)||p.last_event||'—'); setElapsedClock(p.elapsed_seconds,p.status); setText('progressTitle',p.current_step_label||'Live job progress'); const statusEl=$('progressStatus'); if(statusEl){ statusEl.textContent=p.status||'running'; statusEl.className='badge '+statusClass(p.visual_status||p.status); animateStatusBadge(p.visual_status||p.status); } setText('lastPolled',new Date().toLocaleTimeString()); renderTimeline(stable.timeline,p.status); }