fffiloni's picture
Upload 8 files
3331cc9 verified
Raw
History Blame
78.1 kB
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','technical_blocker_boot_only','health_only','partial','partial_validation','completed_with_warnings','demo_usable_full_promise_not_verified','interactive_app_available_smoke_failed','manual_test_required_smoke_failed','validated','validated_after_space_test','validated_after_manual_space_test','manual_validation_passed','repair_success','repair_failed','succeeded','waiting_manual_action','blocked','stale','cancelled','canceled','stopped','auth_refresh_required']);
const PROGRESS_BLOCKING_TERMINAL_STATUSES=new Set(['failed','failure','error','technical_blocker','technical_blocker_boot_only','blocked','manual_hardware_required','generated_needs_manual_hardware','waiting_manual_action','auth_refresh_required','stale','stopped','cancelled','canceled']);
const WORKER_STEP_ORDER=[
'bootstrap','dependencies','auth','model_analysis','workspace','node','pi_install','pi_config','pi_run','pi_model_resolution','pi_verification',
'metadata_sanitize','requirements_sanitize','hardware_strategy','create_space_hardware','create_space','failure_detected','failure_diagnosis','pi_diagnosis','repair_decision','wait_for_logs','factory_rebuild','repair','repair_diagnosis','repair_brief','repair_plan','repair_patch','repair_upload','repair_validation','technical_blocker','manual_hardware_required','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',display_label:'Start',steps:['bootstrap']},
{id:'dependencies',label:'Dependencies',display_label:'Dependencies',steps:['dependencies']},
{id:'auth_model',label:'Auth + model',display_label:'Model access',steps:['auth','model_analysis']},
{id:'workspace_node',label:'Workspace/node',display_label:'Workspace',steps:['workspace','node']},
{id:'pi_setup',label:'Pi setup',display_label:'Agent setup',steps:['pi_install','pi_config']},
{id:'pi_run',label:'Pi run',display_label:'Agent build',steps:['pi_run']},
{id:'pi_verify',label:'Pi verify',display_label:'Agent review',steps:['pi_model_resolution','pi_verification','metadata_sanitize','requirements_sanitize']},
{id:'hardware',label:'Hardware',display_label:'Hardware',steps:['hardware_strategy','create_space_hardware']},
{id:'create_space',label:'Create Space',display_label:'Space',steps:['create_space']},
{id:'upload_runtime',label:'Upload/runtime',display_label:'Deploy',steps:['upload_files','space_runtime','space_logs','live_wait']},
{id:'validation',label:'Validation',display_label:'Live test',steps:['api_validation','generation_smoke','inference_gate']},
{id:'repair',label:'Repair recovery',display_label:'Recovery',steps:['failure_detected','failure_diagnosis','pi_diagnosis','repair_decision','wait_for_logs','factory_rebuild','repair','repair_diagnosis','repair_brief','repair_plan','repair_patch','repair_upload','repair_validation','technical_blocker','manual_hardware_required']},
{id:'report',label:'Report',display_label:'Report',steps:['report_write','done']},
{id:'failure',label:'Failure',display_label:'Failure',terminal_only:true,steps:['failure']}
];
const progressMotionState={lastProgress:null,lastStatus:null,lastStep:null};
const WORKER_STEP_DISPLAY_LABELS={
bootstrap:'start',dependencies:'dependencies',auth:'auth',model_analysis:'model card',workspace:'workspace',node:'node setup',
pi_install:'agent install',pi_config:'agent config',pi_run:'agent building',pi_model_resolution:'model routing',pi_verification:'agent check',
metadata_sanitize:'metadata check',requirements_sanitize:'requirements check',hardware_strategy:'hardware plan',create_space_hardware:'hardware request',
create_space:'space created',upload_files:'files uploaded',space_runtime:'runtime check',space_logs:'logs checked',live_wait:'space live',
api_validation:'API check',generation_smoke:'live generation',inference_gate:'inference verdict',failure_detected:'failure detected',
failure_diagnosis:'diagnosis',pi_diagnosis:'agent diagnosis',repair_decision:'decision',wait_for_logs:'waiting for logs',
factory_rebuild:'same-code rebuild',repair:'repair started',repair_diagnosis:'patch allowed',repair_brief:'repair brief',repair_plan:'repair plan',
repair_patch:'patching',repair_upload:'repair uploaded',repair_validation:'repair test',technical_blocker:'blocker',manual_hardware_required:'manual hardware',
report_write:'report written',done:'done',failure:'failed'
};
function workerStepDisplayLabel(step){return WORKER_STEP_DISPLAY_LABELS[String(step||'')]||String(step||'').replace(/_/g,' ')}
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'||s==='technical_blocker_boot_only'||s==='blocked';
}
function isSuccessStatus(status){
const s=String(status||'').toLowerCase();
if(PROGRESS_BLOCKING_TERMINAL_STATUSES.has(s)||s.includes('technical_blocker')||s.includes('blocked')||s.includes('failed')||s.includes('error'))return false;
return s.includes('success')||s.includes('succeed')||s.includes('passed')||s==='done'||s==='completed'||s==='repair_success'||s==='full_inference_success';
}
function isPartialStatus(status){
const s=String(status||'').toLowerCase();
return s==='full_inference_candidate_health_passed'||s==='health_only'||s==='partial'||s==='partial_validation'||s==='completed_with_warnings'||s==='demo_usable_full_promise_not_verified'||s==='interactive_app_available_smoke_failed'||s==='manual_test_required_smoke_failed';
}
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(isPartialStatus(status))return'warn';
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 humanizeHardwareReason(text){
const raw=String(text||'').replace(/\s+/g,' ').trim();
const zero=raw.match(/ZeroGPU Spaces are currently limited to\s*(\d+)\s*per user,?\s*and there are already\s*(\d+)\s*Spaces running on ZeroGPU/i);
if(zero){
return `ZeroGPU unavailable: namespace limit reached. ${zero[2]} ZeroGPU Spaces are already running; current limit is ${zero[1]}.`;
}
return raw;
}
function compactReasonText(value,max=190){
let text=String(value||'').replace(/\s+/g,' ').trim();
if(!text)return '';
if(text.startsWith('{')){
try{
const parsed=JSON.parse(text);
text=parsed.server_message||parsed.hf_error_message||parsed.error||parsed.message||text;
}catch(_){}
}
text=text.replace(/^Bad request:\s*/i,'').trim();
text=text.replace(/^\(Request ID:[^)]+\)\s*/i,'').trim();
text=humanizeHardwareReason(text);
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<first))first=idx;
});
return first;
}
function firstProblemGroupKey(items=[],overallStatus=''){
const first=firstFailedGroupIndex(overallStatus);
if(first>=0)return COMPACT_TIMELINE_GROUPS[first]?.id||'';
const byStep=new Map((items||[]).map(item=>[String(item.step||item.id||item.label||''),item]));
const groups=visibleCompactTimelineGroups(items,overallStatus);
const failedFromTimeline=groups.find(group=>group.steps.some(step=>isFailureStatus(byStep.get(step)?.status)));
if(failedFromTimeline)return failedFromTimeline.id||'';
if(visualStatusClass(overallStatus)==='error'){
const lastNonPending=[...groups].reverse().find(group=>group.steps.some(step=>byStep.get(step)&&String(byStep.get(step).status||'').toLowerCase()!=='pending'));
return lastNonPending?.id||'repair';
}
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 terminalFailureAnchorGroupIndex(items=[],overallStatus=''){
if(visualStatusClass(overallStatus)!=='error')return -1;
const events=activityFeedState.lastItems||[];
const groups=visibleCompactTimelineGroups(items,overallStatus).filter(g=>g.id!=='failure'&&g.id!=='report');
const groupIndexById=new Map(groups.map((g,i)=>[g.id,i]));
// Prefer the latest explicit failed worker step that belongs to a product milestone.
for(const event of [...events].reverse()){
const step=String(event.step||'');
if(!step||step==='failure'||!isFailureStatus(event.status))continue;
const group=COMPACT_TIMELINE_GROUPS.find(g=>g.id!=='failure'&&g.steps.includes(step));
if(group&&groupIndexById.has(group.id))return groupIndexById.get(group.id);
}
// If the terminal failure event is generic, anchor the failure to the latest
// meaningful non-terminal event before it. This prevents backend-completed
// future milestones from all turning green after a worker exception.
for(const event of [...events].reverse()){
const step=String(event.step||'');
if(!step||step==='failure'||step==='done'||step==='report_write')continue;
const group=COMPACT_TIMELINE_GROUPS.find(g=>g.id!=='failure'&&g.id!=='report'&&g.steps.includes(step));
if(group&&groupIndexById.has(group.id))return groupIndexById.get(group.id);
}
const byStep=new Map((items||[]).map(item=>[String(item.step||item.id||item.label||''),item]));
for(let i=groups.length-1;i>=0;i--){
const group=groups[i];
if(group.steps.some(step=>byStep.has(step)&&String(byStep.get(step).status||'').toLowerCase()!=='pending'))return i;
}
return -1;
}
function terminalFailureReason(){
const events=activityFeedState.lastItems||[];
const failed=[...events].reverse().find(event=>isFailureStatus(event.status)||String(event.step||'')==='failure');
return failed?eventReason(failed):'';
}
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==='warning'||status==='warn')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','health_only','partial','partial_validation','completed_with_warnings'].includes(overall);
if(status==='failed'||status==='error'||status==='failure')return'status-failed';
if(status==='fallback')return'status-fallback';
if(status==='warning'||status==='warn')return'status-warn';
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 friendlyTerminalEventMessage(event={}){
const step=String(event.step||'').toLowerCase();
const status=String(event.status||'').toLowerCase();
const msg=String(event.message||'').trim();
if(step==='api_validation'&&(status==='waiting'||status==='started')&&(msg.toLowerCase().includes('not ready')||msg.toLowerCase().includes('waiting'))){
return 'Waiting for Space startup';
}
if(step==='anonymous_eval'&&status==='success'){
return 'Wrote local eval record';
}
if(step==='done'&&(status==='full_inference_candidate_health_passed'||status==='health_only'||status==='partial_validation')){
return 'Universal model-card builder completed · Partial validation';
}
if(step==='inference_gate'&&(status==='full_inference_candidate_health_passed'||status==='health_only'||status==='partial_validation')){
return 'Partial validation · health passed, generation smoke failed';
}
return msg||event.step||'Waiting for worker events.';
}
function eventMessage(event){
return friendlyTerminalEventMessage(event||{});
}
function hasCanonicalTimelineModel(model){
return Boolean(model&&model.schema_version==='run_timeline_model.v1'&&Array.isArray(model.phases));
}
function normalizeTerminalUiStatus(value){
const s=String(value||'').trim().toLowerCase();
if(!s)return'';
if(PROGRESS_BLOCKING_TERMINAL_STATUSES.has(s))return s==='cancelled'||s==='canceled'?'stopped':s;
if(['success','succeeded','done','completed','passed','repair_success'].includes(s))return'full_inference_success';
if(['cancelled','canceled'].includes(s))return'stopped';
if(['partial','health_only','full_inference_candidate_health_passed','completed_with_warnings'].includes(s))return'partial_validation';
if(PROGRESS_TERMINAL_STATUSES.has(s))return s;
return'';
}
function sourceHasBlockingTerminal(src={}){
const keys=['status','final_status','display_status','effective_status','effective_verdict','verdict','result_status','validation_status','ui_status','gate_status'];
return keys.some(k=>PROGRESS_BLOCKING_TERMINAL_STATUSES.has(String(src?.[k]||'').trim().toLowerCase()));
}
function authoritativeTerminalUiStatus(payload={}){
const view=payload.view||{};
const summary=payload.summary||{};
const highPriority=[payload.final_status_reconciliation,view.final_status_reconciliation,summary,view.summary,view.effective_run_status,payload.effective_run_status,summary.effective_run_status].filter(x=>x&&typeof x==='object');
const lowPriority=[view.timeline_model?.progress,payload.timeline_model?.progress,payload.inference_gate,payload.generation_smoke].filter(x=>x&&typeof x==='object');
const keys=['status','final_status','display_status','effective_status','effective_verdict','verdict','result_status','validation_status'];
let sawBlocking=false;
for(const src of highPriority){
if(sourceHasBlockingTerminal(src))sawBlocking=true;
for(const key of keys){
const normalized=normalizeTerminalUiStatus(src[key]);
if(normalized){
if(PROGRESS_BLOCKING_TERMINAL_STATUSES.has(normalized))return normalized;
if(normalized==='full_inference_success'&&sawBlocking)continue;
return normalized;
}
}
}
for(const src of lowPriority){
if(sourceHasBlockingTerminal(src))return normalizeTerminalUiStatus(src.status||src.final_status||src.display_status||src.effective_status||src.verdict)||'technical_blocker';
for(const key of keys){
const normalized=normalizeTerminalUiStatus(src[key]);
if(normalized){
if(PROGRESS_BLOCKING_TERMINAL_STATUSES.has(normalized))return normalized;
if(normalized==='full_inference_success'&&sawBlocking)continue;
return normalized;
}
}
if(!sawBlocking&&(src.ok===true||src.generation_smoke_passed===true||src.implementation_signals?.generation_smoke_passed===true))return'full_inference_success';
}
return'';
}
function canonicalRunUiState(payload={}){
const view=payload.view||{};
const summary=payload.summary||{};
const statePayload=payload.state||{};
const header=view.header||{};
const statusModel=view.status_model||payload.status_model||{};
const modelProgress=(view.timeline_model&&view.timeline_model.progress)||payload.timeline_model?.progress||{};
const effectiveRunStatus=view.effective_run_status||payload.effective_run_status||summary.effective_run_status||{};
const authoritativeStatus=authoritativeTerminalUiStatus(payload);
const effectiveStatusLower=String(authoritativeStatus||effectiveRunStatus.display_status||effectiveRunStatus.effective_status||'').toLowerCase();
const effectiveTerminalStatuses=new Set(['full_inference_success','partial_validation','manual_hardware_required','technical_blocker','technical_blocker_boot_only','failed','failure','error','stale','stopped','cancelled','canceled','auth_refresh_required','validated_after_space_test','validated_after_manual_space_test','recovered_by_space_test','recovered_by_manual_validation','manual_validated','manual_validation_passed']);
const terminal=Boolean(authoritativeStatus||modelProgress.terminal===true||statusModel.is_terminal===true||effectiveTerminalStatuses.has(effectiveStatusLower));
const statusCandidates=[
authoritativeStatus,
terminal?effectiveRunStatus.display_status:'',
terminal?modelProgress.verdict:'',
terminal?statusModel.effective_status:'',
terminal?statusModel.verdict:'',
terminal?statusModel.status:'',
terminal?header.display_status:'',
terminal?header.status:'',
payload.effective_status,summary.effective_status,
payload.validation_status,payload.result_status,summary.validation_status,summary.result_status,
payload.inference_gate?.status,summary.gate_status,
header.status,header.raw_status,
payload.status,summary.status,statePayload.status,
];
let status=statusCandidates.map(x=>String(x||'').trim()).find(Boolean)||'running';
const lower=status.toLowerCase();
const visual=String(modelProgress.visual_status||statusModel.visual_status||payload.visual_status||'').toLowerCase() ||
(['partial','partial_validation','full_inference_candidate_health_passed','health_only','manual_hardware_required','completed_with_warnings','demo_usable_full_promise_not_verified','interactive_app_available_smoke_failed','manual_test_required_smoke_failed'].includes(lower)?'warn':
(lower.includes('fail')||lower.includes('error')||lower.includes('block')?'error':
(lower.includes('success')||lower==='succeeded'||lower==='done'?'success':lower)));
let label=effectiveRunStatus.display_label||modelProgress.label||header.display_label||header.status_label||badgeLabel(status);
if(terminal&&String(label||'').toLowerCase()==='running')label=badgeLabel(status);
return {status, statusLower:lower, label, visual_status:visual, isTerminal:terminal||PROGRESS_TERMINAL_STATUSES.has(lower)};
}
function visibleCanonicalTimelinePhases(model={}){
const phases=Array.isArray(model.phases)?model.phases:[];
const terminal=model.progress?.terminal===true||PROGRESS_TERMINAL_STATUSES.has(String(model.progress?.verdict||'').toLowerCase());
// v198.26.7: keep the terminal Done marker visible once a final verdict
// exists. Active Run must explicitly reassure users that final reconciliation
// reached success/partial/fail instead of stopping at an intermediate phase.
return phases.filter(p=>terminal||String(p.id||'').toLowerCase()!=='done').map(p=>{
if(String(p.id||'').toLowerCase()==='hardware')return {...p,label:'GPU'};
return p;
});
}
function compactPhaseSublabel(phase={}){
const id=String(phase.id||'').toLowerCase();
const s=String(phase.status||'pending').toLowerCase();
if(s==='complete'||s==='done'||s==='success')return id==='archive'?'Published':'Done';
if(s==='warning'||s==='warn')return id==='hardware'?'Fallback':'Needs attention';
if(s==='running'||s==='waiting')return 'Running';
if(s==='failed'||s==='error')return 'Failed';
if(s==='stopped'||s==='cancelled'||s==='blocked')return 'Stopped';
if(s==='not_needed'||s==='skipped')return 'Not needed';
return 'Pending';
}
function compactTimelineSublabel(status='',groupId=''){
const s=String(status||'pending').toLowerCase();
const id=String(groupId||'').toLowerCase();
if(s==='done'||s==='complete'||s==='success')return id==='archive'?'Published':'Done';
if(s==='fallback')return 'Fallback';
if(s==='recovered')return 'Recovered';
if(s==='warning'||s==='warn')return 'Needs attention';
if(s==='running'||s==='waiting')return 'Running';
if(s==='failed'||s==='error')return 'Failed';
if(s==='stopped'||s==='cancelled'||s==='blocked')return 'Stopped';
if(s==='skipped'||s==='not_needed')return 'Not needed';
return 'Pending';
}
function hideEventReason(item={}){
const step=String(item.step||'').toLowerCase();
const status=String(item.status||'').toLowerCase();
if(step==='api_validation'&&(status==='waiting'||status==='started'||status==='running'))return true;
return false;
}
function canonicalPhaseToTimelineItem(phase={}){
const raw=String(phase.status||'pending').toLowerCase();
let status=raw;
if(raw==='complete')status='done';
if(raw==='warning')status='warning';
if(raw==='failed')status='failed';
if(raw==='stopped')status='stopped';
if(raw==='not_needed'||raw==='skipped')status='skipped';
const details=Array.isArray(phase.details)?phase.details:[];
return {
id:phase.id||phase.label,
step:phase.id||phase.label,
label:phase.label||phase.id||'Phase',
status,
summary:phase.summary||'',
sublabel:compactPhaseSublabel(phase),
details,
percent:status==='done'||status==='warning'||status==='skipped'?100:(status==='running'?50:0),
ring_progress:status==='done'||status==='warning'||status==='skipped'?100:(status==='running'?50:0),
};
}
function timelinePhaseById(phases=[],id=''){
return phases.find(p=>String(p.id||'')===id)||null;
}
function firstPhaseWithStatus(phases=[],statuses=[]){
const wanted=new Set(statuses.map(s=>String(s||'').toLowerCase()));
return phases.find(p=>wanted.has(String(p.status||'').toLowerCase()))||null;
}
function firstPhaseWithIdsAndStatus(phases=[],ids=[],statuses=[]){
const wantedIds=new Set(ids);
const wantedStatuses=new Set(statuses.map(s=>String(s||'').toLowerCase()));
return phases.find(p=>wantedIds.has(String(p.id||''))&&wantedStatuses.has(String(p.status||'').toLowerCase()))||null;
}
function timelineModelActivePhase(model={}){
const phases=visibleCanonicalTimelinePhases(model);
if(!phases.length)return null;
const progress=model.progress||{};
const visual=String(progress.visual_status||'').toLowerCase();
const verdict=String(progress.verdict||'').toLowerCase();
const terminal=progress.terminal===true||['success','warn','warning','error','stopped'].includes(visual)||PROGRESS_TERMINAL_STATUSES.has(verdict);
const failed=firstPhaseWithStatus(phases,['failed','stopped']);
if(failed)return failed;
const attention=firstPhaseWithIdsAndStatus(phases,['live_validation','archive','recovery','done'],['warning','failed'])||firstPhaseWithStatus(phases,['warning']);
if(terminal&&attention)return attention;
if(terminal&&['partial','partial_validation','full_inference_candidate_health_passed','health_only','completed_with_warnings','demo_usable_full_promise_not_verified','interactive_app_available_smoke_failed','manual_test_required_smoke_failed'].includes(verdict)){
return firstPhaseWithIdsAndStatus(phases,['live_validation','archive','done'],['complete','warning'])||attention||[...phases].reverse().find(p=>String(p.status||'').toLowerCase()!=='pending')||phases[0];
}
if(terminal&&visual==='success'){
return firstPhaseWithIdsAndStatus(phases,['live_validation'],['complete','warning'])||
firstPhaseWithIdsAndStatus(phases,['archive'],['complete','warning'])||
timelinePhaseById(phases,'done')||
[...phases].reverse().find(p=>String(p.status||'').toLowerCase()!=='pending')||phases[0];
}
if(terminal&&(visual==='warn'||visual==='warning')){
return attention||firstPhaseWithIdsAndStatus(phases,['live_validation','archive','done'],['complete'])||
[...phases].reverse().find(p=>String(p.status||'').toLowerCase()!=='pending')||phases[0];
}
const recoveryRunning=firstPhaseWithIdsAndStatus(phases,['recovery'],['running']);
if(recoveryRunning)return recoveryRunning;
const liveRunning=firstPhaseWithIdsAndStatus(phases,['live_validation'],['running']);
if(liveRunning)return liveRunning;
const agentRunning=firstPhaseWithIdsAndStatus(phases,['agent'],['running']);
if(agentRunning)return agentRunning;
const running=firstPhaseWithStatus(phases,['running']);
if(running)return running;
const actionableWarning=phases.find(p=>['live_validation','recovery','archive','done'].includes(String(p.id||''))&&String(p.status||'').toLowerCase()==='warning');
if(actionableWarning)return actionableWarning;
return [...phases].reverse().find(p=>String(p.status||'').toLowerCase()!=='pending')||phases[0]||null;
}
function renderTimelineModelWarnings(model={}){
const note=$('timelineTraceNote');
if(!note)return;
const warnings=Array.isArray(model.warnings)?model.warnings:[];
if(!warnings.length){note.hidden=true;note.innerHTML='';note.className='timeline-trace-note';return;}
const rows=warnings.slice(0,4).map(w=>{
const code=String(w.code||'');
const label=escapeHtml(code==='pi_model_changed'?'Pi assistant mismatch':code==='hardware_fallback_used'?'GPU fallback':(w.label||'Run note'));
const detail=escapeHtml(w.detail||'');
return `<li><strong>${label}</strong>${detail?`<span>${detail}</span>`:''}</li>`;
}).join('');
note.hidden=false;
note.className='timeline-trace-note fallback canonical-warning run-notes';
note.innerHTML=`<strong><span>ℹ</span>Run notes</strong><ul>${rows}</ul>`;
}
function timelineDetailStatusLabel(status=''){
const s=String(status||'').toLowerCase();
if(s==='complete'||s==='done'||s==='success')return'Complete';
if(s==='selected')return'Selected';
if(s==='uploaded')return'Uploaded';
if(s==='ready')return'Ready';
if(s==='verified')return'Verified';
if(s==='fallback')return'Fallback';
if(s==='building')return'Building';
if(s==='queued')return'Queued';
if(s==='blocked')return'Blocked';
if(s==='warning'||s==='warn')return'Needs attention';
if(s==='running'||s==='waiting')return'In progress';
if(s==='not_needed'||s==='skipped')return'Not needed';
if(s==='failed'||s==='error')return'Failed';
if(s==='stopped'||s==='cancelled'||s==='blocked')return'Stopped';
return badgeLabel(s||'pending');
}
function timelinePhaseDetailRows(phase={}){
const details=Array.isArray(phase.details)?phase.details.filter(Boolean):[];
if(details.length){
return details.slice(0,6).map(d=>{
const label=escapeHtml(d.label||d.title||'Detail');
const value=escapeHtml(d.value||d.detail||d.summary||d.status||'');
const status=String(d.status||'').toLowerCase();
const cls=status?` ${statusClass(status)}`:'';
return `<li class="timeline-detail-row${cls}"><span>${label}</span>${value?`<strong>${value}</strong>`:''}</li>`;
}).join('');
}
const summary=String(phase.summary||'').trim();
return summary?`<li class="timeline-detail-row neutral"><span>Summary</span><strong>${escapeHtml(summary)}</strong></li>`:'';
}
function renderTimelineModelDetails(model={},activePhase=null){
const root=$('timelinePhaseDetails');
if(!root)return;
if(!hasCanonicalTimelineModel(model)){root.hidden=true;root.innerHTML='';root.className='timeline-phase-details';return;}
const phase=activePhase||timelineModelActivePhase(model)||{};
if(!phase||!(phase.id||phase.label)){root.hidden=true;root.innerHTML='';return;}
const status=String(phase.status||'pending').toLowerCase();
const tone=timelineStatus(status).replace(/^status-/,'');
const warnings=Array.isArray(model.warnings)?model.warnings:[];
const details=Array.isArray(phase.details)?phase.details:[];
const phaseId=String(phase.id||'');
const isRecovery=String(phase.id||'')==='recovery';
const actionable=status.includes('fail')||status.includes('error')||status.includes('warn')||status.includes('manual')||isRecovery||warnings.length>0||details.some(d=>{const st=String(d?.status||'').toLowerCase();return st.includes('fail')||st.includes('warn')||st.includes('manual')||st==='info';});
if(!actionable){root.hidden=true;root.innerHTML='';root.className='timeline-phase-details';return;}
let rows=timelinePhaseDetailRows(phase);
let summary=phase.summary||'';
if(String(phase.id||'')==='recovery'){
// Recovery has its own dedicated Agent recovery panel below the timeline.
// Keep the phase details as a compact pointer instead of duplicating
// Diagnose / Decide / Patch / Revalidate rows here.
summary=summary||'Recovery status is available below.';
// See Agent recovery below.
rows='';
}
root.hidden=false;
root.className=`timeline-phase-details ${tone}${isRecovery?' recovery-compact':''}`;
const compactPointer=isRecovery?`<p class="timeline-detail-pointer">See Agent recovery below for diagnosis, patch and blocker details.</p>`:'';
root.innerHTML=`<div class="timeline-detail-head"><span>${escapeHtml(isRecovery?'Current focus':(phase.label||'Phase'))}</span><strong>${escapeHtml(timelineDetailStatusLabel(status))}</strong></div>${summary?`<p>${escapeHtml(summary)}</p>`:''}${compactPointer}${rows?`<ul>${rows}</ul>`:''}`;
}
function renderTimelineModel(model={},overallStatus=''){
const root=$('timeline');
if(!root)return false;
if(!hasCanonicalTimelineModel(model))return false;
if(!root.dataset.scrollBound){root.dataset.scrollBound='1';root.addEventListener('scroll',()=>{if(root.dataset.autoScrolling==='1')return;root.dataset.userScrolled='1'},{passive:true});}
const list=visibleCanonicalTimelinePhases(model).map(canonicalPhaseToTimelineItem);
const wideTimeline=typeof window==='undefined'||!window.matchMedia||window.matchMedia('(min-width: 521px)').matches;
root.style.gridTemplateColumns=wideTimeline&&list.length?`repeat(${list.length}, minmax(0, 1fr))`:'';
root.style.setProperty('--timeline-phase-count', String(Math.max(1,list.length||8)));
root.classList.add('timeline-canonical');
root.classList.remove('trace-failed','trace-recovered','trace-fallback');
renderTimelineModelWarnings(model);
const active=timelineModelActivePhase(model)||{};
renderTimelineModelDetails(model,active);
const currentKey=String(active.id||active.label||'');
const signature=list.map((x,i)=>`${timelineStepKey(x,i)}:${x.status}:${x.label}:${x.summary}:${overallStatus}`).join('|');
if(root.dataset.signature===signature){
if(root.dataset.visibleStep!==currentKey&&root.dataset.userScrolled!=='1')ensureTimelineStepVisible(currentKey);
return true;
}
const previousKey=root.dataset.currentStep||'';
if(previousKey!==currentKey)root.dataset.userScrolled='';
root.dataset.signature=signature;
root.dataset.currentStep=currentKey||'';
const nextState={};
root.innerHTML=list.map((x,i)=>{
const key=timelineStepKey(x,i);
const st=timelineStatus(x.status,overallStatus);
const prev=timelineRenderState[key];
const changed=prev&&prev!==st;
nextState[key]=st;
const label=st.replace(/^status-/,'');
const summary=x.summary||label;
const sublabel=x.sublabel||label;
const details=(x.details||[]).map(d=>d&&d.label).filter(Boolean);
const detailTitle=details.length?details.join(' · '):summary;
const title=` title="${escapeHtml([x.label,summary,detailTitle].filter(Boolean).join(' · '))}"`;
const aria=`${x.label}: ${label}${summary?`, ${summary}`:''}`;
return `<div class="step ${st} canonical-phase${changed?' status-changed':''}" data-step="${escapeHtml(key)}" data-status="${label}" aria-label="${escapeHtml(aria)}" style="--step-progress:${Number(x.percent||0)}%"${title}><div class="step-dot"><span>${timelineIcon(x.status)}</span></div><div><strong>${escapeHtml(x.label)}</strong><small>${escapeHtml(sublabel)}</small></div></div>`;
}).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);animateClass(step.querySelector('.step-dot'),'dot-pop',700);});
if(!root.dataset.userScrolled||previousKey!==currentKey){root.dataset.autoScrolling='1';const scrollOpts={instant:!root.dataset.visibleStep,force:previousKey!==currentKey};(typeof requestAnimationFrame==='function'?requestAnimationFrame:(fn)=>setTimeout(fn,0))(()=>(typeof requestAnimationFrame==='function'?requestAnimationFrame:(fn)=>setTimeout(fn,0))(()=>ensureTimelineStepVisible(currentKey,scrollOpts)));setTimeout(()=>{if(root)root.dataset.autoScrolling=''},360);}
return true;
}
function timelineStepBounds(root,target){
const rootBox=root.getBoundingClientRect?root.getBoundingClientRect():{left:0,right:root.clientWidth,width:root.clientWidth};
const targetBox=target.getBoundingClientRect?target.getBoundingClientRect():{left:target.offsetLeft-root.scrollLeft,right:target.offsetLeft-root.scrollLeft+target.offsetWidth,width:target.offsetWidth};
return {
rootLeft:rootBox.left,
rootRight:rootBox.right,
targetLeft:targetBox.left,
targetRight:targetBox.right,
targetWidth:targetBox.width||target.offsetWidth||0,
};
}
function ensureTimelineStepVisible(stepKey,opts={}){
const early=new Set(['bootstrap','dependencies','auth_model']);
const root=$('timeline');
if(!root||!stepKey)return;
const safeStep=(typeof CSS!=='undefined'&&CSS.escape)?CSS.escape(stepKey):String(stepKey).replace(/"/g,'\"');
const target=root.querySelector(`[data-step="${safeStep}"]`);
if(!target)return;
const margin=28;
// Keep-visible behavior: do not center. Only move the minimum amount needed.
const behavior='auto';
const visibleLeft=root.scrollLeft;
const visibleRight=visibleLeft+root.clientWidth;
const targetLeft=target.offsetLeft;
const targetRight=targetLeft+target.offsetWidth;
const fullyVisible=targetLeft>=visibleLeft+margin&&targetRight<=visibleRight-margin;
if(fullyVisible){root.dataset.visibleStep=stepKey;return;}
if(early.has(stepKey)&&root.scrollLeft<=1&&targetLeft>=0&&targetRight<=root.clientWidth){root.dataset.visibleStep=stepKey;return;}
let nextLeft=visibleLeft;
if(targetLeft<visibleLeft+margin){
nextLeft=targetLeft-margin;
}else if(targetRight>visibleRight-margin){
nextLeft=targetRight-root.clientWidth+margin;
}
const maxScroll=Math.max(0,root.scrollWidth-root.clientWidth);
nextLeft=Math.max(0,Math.min(maxScroll,Math.round(nextLeft)));
if(opts.force&&target.offsetWidth<root.clientWidth&&targetRight>visibleLeft&&targetLeft<visibleRight){
// keep-visible, not center: only nudge if the active dot is too close to an edge
const center=targetLeft+(target.offsetWidth/2);
if(center>visibleRight-margin*2)nextLeft=Math.min(maxScroll,Math.round(targetRight-root.clientWidth+margin*2));
if(center<visibleLeft+margin*2)nextLeft=Math.max(0,Math.round(targetLeft-margin*2));
}
if(Math.abs(nextLeft-root.scrollLeft)>1){
const behavior=opts.instant?'auto':'smooth';
try{root.scrollTo({left:nextLeft,behavior});}
catch(_){root.scrollLeft=nextLeft;}
}
root.dataset.visibleStep=stepKey;
}
function hasSpecificTimelineProblem(items=[],overallStatus=''){
const byStep=new Map((items||[]).map(item=>[String(item.step||item.id||item.label||''),item]));
const overall=visualStatusClass(overallStatus);
return COMPACT_TIMELINE_GROUPS.some(group=>{
if(group.id==='failure')return false;
if(group.id==='repair')return group.steps.some(step=>byStep.has(step))&&overall==='error';
return group.steps.some(step=>isFailureStatus(byStep.get(step)?.status))||failedEventInfoForGroup(group);
});
}
function visibleCompactTimelineGroups(items=[],overallStatus=''){
const byStep=new Map((items||[]).map(item=>[String(item.step||item.id||item.label||''),item]));
const overall=visualStatusClass(overallStatus);
const specificProblem=hasSpecificTimelineProblem(items,overallStatus);
return COMPACT_TIMELINE_GROUPS.filter(group=>{
const hasItem=group.steps.some(step=>byStep.has(step));
if(group.id==='repair')return hasItem;
// Failure is a terminal fallback anchor, not a normal product milestone.
// Prefer marking the precise stage that failed; only show Failure when the
// run has no better failure/recovery context.
if(group.id==='failure')return hasItem&&!specificProblem;
return true;
});
}
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 terminalSuccess=overall==='success'||['success','succeeded','done','completed','full_inference_success','repair_success'].includes(String(overallStatus||'').toLowerCase());
let firstFailedIdx=firstFailedGroupIndex(overallStatus);
const visibleGroups=visibleCompactTimelineGroups(items,overallStatus);
const terminalAnchorIdx=firstFailedIdx>=0?firstFailedIdx:terminalFailureAnchorGroupIndex(items,overallStatus);
if(overall==='error'&&firstFailedIdx<0)firstFailedIdx=terminalAnchorIdx;
return visibleGroups.map((group,groupIndex)=>{
const groupItems=group.steps.map(step=>byStep.get(step)).filter(Boolean);
const explicitItems=groupItems.filter(item=>String(item.step||item.id||item.label||''));
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'&&terminalAnchorIdx>=0&&groupIndex>terminalAnchorIdx)status='pending';
else if(overall==='error'&&terminalAnchorIdx>=0&&groupIndex===terminalAnchorIdx)status='failed';
else if(active)status=String(active.status||'running').toLowerCase();
else if(groupItems.length&&doneCount>=group.steps.length)status='done';
else if(doneCount>0)status=terminalSuccess?'done':'running';
if(overall==='error'&&status==='done'&&group.steps.includes('failure'))status='failed';
const failedInfo=failedEventInfoForGroup(group)||itemFailure;
const reason=historicalFailure==='fallback'?fallbackReasonForGroup(group):(status==='failed'?(failureReasonForGroup(group)||terminalFailureReason()):(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==='pending')percent=0;
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);
const clampedPercent=Math.max(0,Math.min(100,percent));
const substepLabel=workerStepDisplayLabel(activeSubstep);
const progressDone=status==='done'?group.steps.length:Math.min(group.steps.length,Math.max(doneCount,clampedPercent>=100?group.steps.length:Math.ceil((clampedPercent/100)*group.steps.length)));
const showProgressLabel=group.steps.length>1&&(groupItems.length>0||status!=='pending');
return {
step:group.id,
id:group.id,
label:group.display_label||group.label,
status,
full_steps:group.steps,
done_count:doneCount,
total_count:showProgressLabel?group.steps.length:0,
percent:clampedPercent,
ring_progress:clampedPercent,
active_substep:activeSubstep,
active_substep_label:substepLabel,
grouped:group.steps.length>1,
progress_label:showProgressLabel?`${progressDone}/${group.steps.length}`:'',
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?`<p class="trace-reason"><b>Reason:</b> ${escapeHtml(trace.reason)}</p>`:'';
note.innerHTML=`<strong><span>${icon}</span>${escapeHtml(title)}</strong><p>${escapeHtml(trace.message)}</p>${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 renderTimelinePlaceholder(message='Waiting for canonical run timeline…'){
const root=$('timeline');
if(!root)return;
root.dataset.signature='canonical-placeholder';
root.classList.remove('timeline-canonical','trace-failed','trace-recovered','trace-fallback');
root.classList.add('timeline-placeholder');
root.style.gridTemplateColumns='';
root.innerHTML=`<div class="timeline-skeleton">${escapeHtml(message)}</div>`;
const note=$('timelineTraceNote');
if(note){note.hidden=true;note.innerHTML='';note.className='timeline-trace-note';}
const details=$('timelinePhaseDetails');
if(details){details.hidden=true;details.innerHTML='';details.className='timeline-phase-details';}
}
function shouldSuppressLegacyTimeline(items=[]){
const list=Array.isArray(items)?items:[];
if(!list.length)return true;
const labels=list.map(x=>String(x.label||'').toLowerCase());
const legacyNeedles=['bootstrap','dependencies','pi install','pi config','requirements sanitize','generation smoke','inference gate'];
const legacyHits=legacyNeedles.filter(n=>labels.includes(n)).length;
return legacyHits>=4&&list.length>=12;
}
function renderTimeline(items,overallStatus=''){
const root=$('timeline');
if(!root)return;
if(shouldSuppressLegacyTimeline(items)){renderTimelinePlaceholder('Waiting for canonical run timeline…');return;}
if(!root.dataset.scrollBound){root.dataset.scrollBound='1';root.addEventListener('scroll',()=>{if(root.dataset.autoScrolling==='1')return;root.dataset.userScrolled='1'},{passive:true});}
root.classList.remove('timeline-canonical','timeline-placeholder');
const canonicalDetails=$('timelinePhaseDetails');
if(canonicalDetails){canonicalDetails.hidden=true;canonicalDetails.innerHTML='';canonicalDetails.className='timeline-phase-details';}
const fullList=items||[];
const list=compactTimelineItems(fullList,overallStatus);
// Keep the compact timeline on a single row. The number of visible groups is
// dynamic (repair/failure groups appear only for recovery/failures), so a
// fixed CSS repeat count can wrap dots onto a second line. Set the exact
// column count at render time for desktop/tablet layouts and let mobile CSS
// switch back to the vertical stacked timeline.
const wideTimeline=typeof window==='undefined'||!window.matchMedia||window.matchMedia('(min-width: 521px)').matches;
root.style.gridTemplateColumns=wideTimeline&&list.length?`repeat(${list.length}, minmax(96px, 1fr))`:'';
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('|');
// v68 legacy invariant reference: if(root.dataset.signature===signature){ensureTimelineStepVisible(currentKey);return;}
if(root.dataset.signature===signature){
if(root.dataset.visibleStep!==currentKey&&root.dataset.userScrolled!=='1')ensureTimelineStepVisible(currentKey);
return;
}
const previousKey=root.dataset.currentStep||'';
if(previousKey!==currentKey)root.dataset.userScrolled='';
root.dataset.signature=signature;
root.dataset.currentStep=currentKey||'';
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=compactTimelineSublabel(rawStatus,x.id||x.step||'');
const traceClass=traceClassForStep(trace,i);
const reason=reasonForTimelineItem(x);
const reasonLine='';
const titleParts=[x.label||`Step ${i+1}`,meta,reason].filter(Boolean);
const title=` title="${escapeHtml(titleParts.join(' · '))}"`;
const groupBadge=x.total_count>1?`<em class="step-count" aria-label="${escapeHtml((x.progress_label||'')+' substeps')}">${escapeHtml(x.progress_label||'')}</em>`:'';
const aria=`${x.label||`Step ${i+1}`}: ${label}${x.total_count>1?`, ${x.done_count||0} of ${x.total_count} substeps`:''}`;
return `<div class="step ${st} ${traceClass}${x.grouped?' is-grouped':''}${changed?' status-changed':''}" data-step="${escapeHtml(key)}" data-status="${label}" aria-label="${escapeHtml(aria)}" style="--step-progress:${percent}%"${title}><div class="step-dot"><span>${timelineIcon(rawStatus)}</span>${groupBadge}</div><div><strong>${escapeHtml(x.label||`Step ${i+1}`)}</strong><small>${escapeHtml(meta)}</small>${reasonLine}</div></div>`;
}).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);
});
if(!root.dataset.userScrolled||previousKey!==currentKey){root.dataset.autoScrolling='1';const scrollOpts={instant:!root.dataset.visibleStep,force:previousKey!==currentKey};(typeof requestAnimationFrame==='function'?requestAnimationFrame:(fn)=>setTimeout(fn,0))(()=>(typeof requestAnimationFrame==='function'?requestAnimationFrame:(fn)=>setTimeout(fn,0))(()=>ensureTimelineStepVisible(currentKey,scrollOpts)));setTimeout(()=>{if(root)root.dataset.autoScrolling=''},360);}
}
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 eventIconFor(item={},klass=''){
const step=String(item.step||'').toLowerCase();
const status=String(item.status||'').toLowerCase();
if(klass.includes('error')||status.includes('fail')||status.includes('error'))return'alert';
if(klass.includes('hardware-fallback')||step.includes('hardware'))return'chip';
if(step.includes('repair_decision')||step.includes('pi_diagnosis')||step.includes('failure_diagnosis'))return'search';
if(step.includes('factory_rebuild'))return'rocket';
if(step.includes('repair')||step.includes('blocker'))return'wand';
if(step.includes('upload'))return'upload';
if(step.includes('trace'))return'files';
if(step.includes('report'))return'document';
if(step.includes('auth'))return'shield';
if(step.includes('model')||step.includes('analysis'))return'search';
if(step.includes('space')||step.includes('create'))return'rocket';
if(step.includes('api')||step.includes('smoke')||step.includes('inference')||step.includes('validation'))return'bolt';
if(step.includes('pi_')||step.includes('pi'))return'bot';
if(status.includes('running')||status.includes('started')||status.includes('waiting')||status.includes('pending'))return'clock';
if(status.includes('success')||status==='done')return'check';
return'circle';
}
const EVENT_ICON_PATHS={
alert:'M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z',
bolt:'M13 2 4 14h7l-1 8 10-13h-7l0-7Z',
bot:'M12 5V3m-7 8a5 5 0 0 1 5-5h4a5 5 0 0 1 5 5v5a3 3 0 0 1-3 3H8a3 3 0 0 1-3-3v-5Zm4 1h2m-8 0h2m-2 4h4',
check:'m5 13 4 4L19 7',
chip:'M9 3v3m6-3v3M9 18v3m6-3v3M3 9h3m-3 6h3m12-6h3m-3 6h3M8 6h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2Zm2 4h4v4h-4v-4Z',
circle:'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Z',
clock:'M12 8v5l3 2m6-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z',
document:'M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7l-5-5Zm0 0v5h5M8 13h8M8 17h6',
files:'M9 7h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2Zm0 0V5a2 2 0 0 1 2-2h4l4 4v2M11 11h4M11 15h5',
rocket:'M5 19c1.5-.5 2.8-1.5 4-3m-4 3c-.8.8-1.8 1.1-3 1 .1-1.2.5-2.2 1.2-3M13 7l4 4m-6 6-4-4 4-7c2-2 5-3 8-3 0 3-1 6-3 8l-5 6Z',
search:'M21 21l-4.35-4.35M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Z',
shield:'M12 3 5 6v6c0 4.4 3 7.4 7 9 4-1.6 7-4.6 7-9V6l-7-3Zm-3 9 2 2 4-5',
upload:'M12 16V4m0 0 4 4m-4-4-4 4M4 16v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3',
wand:'M4 20 20 4m-3 0 3 3m-6-1 4 4M5 6h.01M9 3h.01M3 10h.01M18 18h.01'
};
function renderEventIcon(name){
const key=EVENT_ICON_PATHS[name]?name:'circle';
return `<span class="activity-icon" data-event-icon="${key}" aria-hidden="true"><svg viewBox="0 0 24 24" focusable="false"><path d="${EVENT_ICON_PATHS[key]}"></path></svg></span>`;
}
// v105 keeps v64 compact-list invariant: const visible=[...list].reverse()
function eventOriginFor(item={}){
const step=String(item.step||'').toLowerCase();
const message=String(item.message||'').toLowerCase();
if(['pi_install','pi_config','pi_run','pi_model_resolution','pi_verification','pi_diagnosis','repair_plan','repair_patch'].some(s=>step.includes(s)))return'pi';
if(['bootstrap','token_context','dependencies','node'].some(s=>step.includes(s))||message.includes('worker started'))return'job';
if(['create_space','create_space_hardware','hardware_strategy','upload_files'].some(s=>step.includes(s)))return'hub';
if(['space_runtime','space_logs','live_wait'].some(s=>step.includes(s))||message.includes('runtime_error')||message.includes('app_starting'))return'space';
if(['api_validation','generation_smoke','inference_gate','repair_validation'].some(s=>step.includes(s))||message.includes('gradio'))return'gradio';
if(['failure_diagnosis','repair_decision','technical_blocker','failure','done','report_write'].some(s=>step.includes(s)))return'factory';
return'factory';
}
function renderActivityFeed(items){
const root=$('activityFeed');
if(!root)return;
const list=mergeStableEvents(String(state.runId||''),items||[]);
const visible=[...list].slice(-30).reverse();
const signature=visible.map(item=>`${item.ts||''}:${item.step||''}:${item.status||''}:${item.message||''}`).join('|');
if(root.dataset.signature===signature)return;
const shouldKeepLatestVisible=root.scrollTop<24||!root.dataset.signature;
root.dataset.signature=signature;
root.classList.add('show-all');
if(!list.length){
root.innerHTML='<div class="empty">No activity yet. Waiting for the first worker event.</div>';
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=!hideEventReason(item)&&reason&&reason!==String(item.message||'').trim()?`<p class="event-reason">${escapeHtml(reason)}</p>`:'';
const icon=eventIconFor(item,klass);
const step=String(item.step||'event').replace(/_/g,' ');
const origin=eventOriginFor(item);
return `<div class="activity-row ${klass}"><time>${escapeHtml(formatTime(item.ts))}</time>${renderEventIcon(icon)}<div class="activity-copy"><strong title="${escapeHtml(item.message||'Event received')}">${escapeHtml(item.message||'Event received')}</strong><p title="${escapeHtml(step+' · '+(label||''))}"><span>${escapeHtml(step)}</span><em>${escapeHtml(label||'')}</em></p>${reasonHtml}</div><span class="event-origin ${origin}">${escapeHtml(origin)}</span></div>`;
}).join('');
if(shouldKeepLatestVisible)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])=>`<span class="status-chip ${statusClass(v)}"><strong>${escapeHtml(k)}</strong><em>${escapeHtml(badgeLabel(v||'pending'))}</em></span>`).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=>`<li class="${statusClass(r.status)}"><span>${escapeHtml(r.label)}</span><strong>${escapeHtml(r.status||'pending')}</strong></li>`).join('');
}
function renderSpaceTestPreview(s={}){
const policy=s.policy||s.space_test_policy||{};
const metrics=s.validation_metrics||{};
const status=policy.mode||s.status||'pending';
const st=$('spaceTestStatus');
if(st){
st.textContent=policy.label||badgeLabel(status);
st.className='badge '+(policy.enabled?statusClass(status):'neutral');
}
const endpointLabel=policy.requires_endpoint_discovery?'Discovery first':(s.endpoint||policy.endpoint||metrics.api_name||s.output_artifact||'—');
setText('spaceTestEndpoint',endpointLabel);
setText('spaceTestOutput',metrics.output_artifact||s.output_artifact||'Not launched');
const latency=metrics.latency_seconds||metrics.observed_latency_seconds||policy.smoke_latency_seconds||s.smoke_latency_seconds||s.latency_seconds||'';
if(latency)setText('spaceTestLatency',`${formatLatencySeconds(latency)} from ${metrics.source||'automatic smoke'}`);
setText('spaceTestVerdict',policy.message||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-warn','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 progressProcessLabel(progress={},percent=0){
const terminal=Boolean(progress.terminal);
const status=String(progress.process_status||'').toLowerCase();
if(terminal||Number(percent)>=100)return'Completed';
if(status.includes('running')||status.includes('waiting')||status.includes('pending'))return'In progress';
return status?badgeLabel(status):'In progress';
}
function progressVerdictLabel(progress={},visualStatus=''){
const label=String(progress.label||'').trim();
const verdict=String(progress.verdict||visualStatus||'').trim();
if(label&&!(progress.terminal===true&&label.toLowerCase()==='running'))return label;
return verdict?badgeLabel(verdict):'Pending verdict';
}
function renderSemanticProgressSummary(progress={},percent=0,visualStatus=''){
const root=$('progressSemantics');
const caption=$('progressCaptionLabel');
const bar=$('progressBar');
if(caption)caption.textContent=progress&&progress.verdict?'Process progress':'Overall progress';
if(bar){
const value=Math.max(0,Math.min(100,Number(percent||0)));
bar.setAttribute('aria-valuenow',String(Math.round(value)));
bar.setAttribute('aria-label',progress&&progress.verdict?`Process progress ${Math.round(value)} percent, result ${progressVerdictLabel(progress,visualStatus)}`:`Overall progress ${Math.round(value)} percent`);
}
if(!root)return;
if(!progress||!progress.verdict){root.hidden=true;root.innerHTML='';return;}
const process=progressProcessLabel(progress,percent);
const verdict=progressVerdictLabel(progress,visualStatus);
const tone=visualStatusClass(visualStatus||progress.visual_status||progress.verdict);
const subtitle=String(progress.subtitle||'').trim();
root.hidden=false;
root.className=`progress-semantics ${tone}`;
root.innerHTML=`<span><strong>Process</strong> ${escapeHtml(process)}</span><span><strong>Result</strong> ${escapeHtml(verdict)}</span>${subtitle?`<em>${escapeHtml(subtitle)}</em>`:''}`;
}
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'].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-canonical');timeline.scrollLeft=0;}const note=$('timelineTraceNote');if(note){note.hidden=true;note.innerHTML='';note.className='timeline-trace-note';}const details=$('timelinePhaseDetails');if(details){details.hidden=true;details.innerHTML='';details.className='timeline-phase-details';}
}
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:workerStepDisplayLabel(activeItem?.step||activeItem?.id)||activeItem?.label||''
};
}
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 timelineModel=view.timeline_model||p.timeline_model||null;
const modelProgress=hasCanonicalTimelineModel(timelineModel)?(timelineModel.progress||{}):null;
const stable=stabilizeProgressPayload(p,view);
const canonical=canonicalRunUiState({view,...p});
const progress=Number(modelProgress?.percent??stable.progress??(canonical.isTerminal?100:(header.status==='succeeded'?100:0)));
const runStatus=canonical.status;
const visualRunStatus=canonical.visual_status||runStatus;
const activePhase=hasCanonicalTimelineModel(timelineModel)?timelineModelActivePhase(timelineModel):null;
// Legacy invariant: animateProgressBar(progress,visualRunStatus,stable.currentStep) remains true for non-canonical payloads.
animateProgressBar(progress,visualRunStatus,activePhase?.id||stable.currentStep);
setText('progressPercent',`${Math.round(progress)}%`);
renderSemanticProgressSummary(modelProgress,progress,visualRunStatus);
setText('progressTitle',canonical.label||header.title||view.run_id||'Live job progress');
const statusEl=$('progressStatus');
if(statusEl){
statusEl.textContent=canonical.label||badgeLabel(runStatus||'Running');
statusEl.className='badge '+statusClass(visualRunStatus);
animateStatusBadge(visualRunStatus);
}
const latestEvent=stableEvents[stableEvents.length-1];
// Legacy invariant: setText('currentStep',stable.currentStepLabel remains the fallback path when no canonical phase is available.
setText('currentStep',activePhase?`${activePhase.label}: ${activePhase.summary||activePhase.status}`:(stable.currentStepLabel||p.current_step_label||'—'));
setText('lastEvent',modelProgress?.subtitle||eventMessage(latestEvent)||p.last_event||'Waiting for worker events.');
setElapsedClock(p.elapsed_seconds??header.elapsed_seconds,canonical.statusLower||runStatus);
setText('lastPolled',new Date().toLocaleTimeString());
setText('detailSpace',header.space||'—');
setText('detailJobStatus',canonical.label||badgeLabel(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();
const terminal=TERMINAL_STATUSES.has(s)||progressIsTerminalStatus(s);
cancel.hidden=Boolean(terminal);
cancel.disabled=!(state.runId&&view.links?.job_url&&!terminal);
}
if(!renderTimelineModel(timelineModel,runStatus))renderTimelinePlaceholder('Waiting for canonical run timeline…');
renderActivityFeed(Array.isArray(view.activity)&&view.activity.length?view.activity:stableEvents);
renderDiagnostics(view.diagnostics||{},header);
renderSpaceTestPreview(view.space_test||{});
renderManualAction({view,...p});
if(typeof renderAgentRecovery==='function')renderAgentRecovery(p);
if(typeof renderRunDocuments==='function')renderRunDocuments(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)}%`);
renderSemanticProgressSummary(null,progress,p.visual_status||p.status);
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());
renderTimelinePlaceholder('Waiting for canonical run timeline…');
if(typeof renderAgentRecovery==='function')renderAgentRecovery(p);
}
// v189.2 compatibility anchors for older progress tests:
// const visualRunStatus=p.visual_status||runStatus
// setText('currentStep',stable.currentStepLabel
// Legacy test anchors retained for timeline invariants:
// currentStepLabel:activeItem?.label
// x.done_count||0
// v164 compatibility anchors for older timeline tests:
// display_label:'Model card'
// active_substep_label:workerStepDisplayLabel(activeSubstep)
// title="${escapeHtml(reason)}"
// v165 compatibility anchors for historical timeline tests:
// progress_label:group.steps.length>1
// total_count:group.steps.length
// overall==='error'&&firstFailedIdx>=0&&groupIndex>firstFailedIdx
// v165 historical scroll/test anchors kept as comments after the offset-math refactor:
// requestAnimationFrame(()=>requestAnimationFrame(()=>ensureTimelineStepVisible
// status='after_fail'
// root.scrollLeft-(bounds.rootLeft+margin-bounds.targetLeft)
// root.scrollLeft+(bounds.targetRight-(bounds.rootRight-margin))
// v189.3 semantic progress anchors:
// renderSemanticProgressSummary(modelProgress,progress,visualRunStatus)
// progressCaptionLabel = Process progress when timeline_model progress verdict is present
// progress bar color follows visual_status, so 100% warn is not success
// v190.18 compatibility anchor: Endpoint discovery required
// v189.2 legacy anchor: if(!renderTimelineModel(timelineModel,runStatus))renderTimeline(stable.timeline,runStatus);
// v189.2 compatibility anchors retained for string-based release tests:
// const runStatus=modelProgress?.verdict||p.status||header.status;
// setText('progressTitle',modelProgress?.label||header.title||view.run_id||'Live job progress');
// v189.2 compatibility anchors retained for string-based release tests:
// const visualRunStatus=modelProgress?.visual_status||p.visual_status||runStatus;
// statusEl.textContent=modelProgress?.label||badgeLabel(runStatus||'Running');