Fix manifest update: always rebuild tree, add debug logs, fix status handling

- Always rebuild progress tree HTML instead of incremental updates (simpler, more reliable)
- Add console.log for debugging manifest updates
- Fix status normalization (backend sends 'Running', 'Completed' etc)
- Fix updateStatusSection to handle different manifest structures
- Log section and child details for debugging
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-01-01 10:42:25 -03:00
parent f5ece0d410
commit 4f05af4d04

View file

@ -441,15 +441,32 @@ function handleWebSocketMessage(data) {
break; break;
case "manifest_update": case "manifest_update":
console.log("[Tasks WS] MANIFEST_UPDATE for task:", data.task_id); console.log(
"[Tasks WS] MANIFEST_UPDATE for task:",
data.task_id,
"selected:",
TasksState.selectedTaskId,
);
// Update the progress log section with manifest data // Update the progress log section with manifest data
if (data.details) { if (data.details) {
try { try {
const manifestData = JSON.parse(data.details); const manifestData = JSON.parse(data.details);
console.log(
"[Tasks WS] Manifest parsed, sections:",
manifestData.sections?.length,
"status:",
manifestData.status,
);
renderManifestProgress(data.task_id, manifestData); renderManifestProgress(data.task_id, manifestData);
} catch (e) { } catch (e) {
console.error("[Tasks WS] Failed to parse manifest:", e); console.error(
"[Tasks WS] Failed to parse manifest:",
e,
data.details?.substring(0, 200),
);
} }
} else {
console.warn("[Tasks WS] manifest_update received but no details");
} }
break; break;
} }
@ -459,8 +476,16 @@ function handleWebSocketMessage(data) {
const pendingManifestUpdates = new Map(); const pendingManifestUpdates = new Map();
function renderManifestProgress(taskId, manifest, retryCount = 0) { function renderManifestProgress(taskId, manifest, retryCount = 0) {
console.log(
"[Manifest] renderManifestProgress called for task:",
taskId,
"selected:",
TasksState.selectedTaskId,
);
// Only update if this is the selected task // Only update if this is the selected task
if (TasksState.selectedTaskId !== taskId) { if (TasksState.selectedTaskId !== taskId) {
console.log("[Manifest] Skipping - not selected task");
return; return;
} }
@ -471,6 +496,7 @@ function renderManifestProgress(taskId, manifest, retryCount = 0) {
} }
if (!progressLog) { if (!progressLog) {
console.log("[Manifest] No progress log element found, retry:", retryCount);
// If task is selected but element not yet loaded, retry after a delay // If task is selected but element not yet loaded, retry after a delay
if (retryCount < 5) { if (retryCount < 5) {
pendingManifestUpdates.set(taskId, manifest); pendingManifestUpdates.set(taskId, manifest);
@ -491,29 +517,28 @@ function renderManifestProgress(taskId, manifest, retryCount = 0) {
pendingManifestUpdates.delete(taskId); pendingManifestUpdates.delete(taskId);
if (!manifest || !manifest.sections) { if (!manifest || !manifest.sections) {
console.log("[Manifest] No sections in manifest");
return; return;
} }
console.log("[Manifest] Rendering", manifest.sections.length, "sections");
const totalSteps = manifest.progress?.total || 60; const totalSteps = manifest.progress?.total || 60;
// Update STATUS section if exists // Update STATUS section if exists
updateStatusSection(manifest); updateStatusSection(manifest);
// Update or create progress tree // Always rebuild the tree to ensure children are shown
let tree = progressLog.querySelector(".taskmd-tree"); // This is simpler and more reliable than incremental updates
if (!tree) { const html = buildProgressTreeHTML(manifest, totalSteps);
// First render - create full HTML progressLog.innerHTML = html;
progressLog.innerHTML = buildProgressTreeHTML(manifest, totalSteps);
// Auto-expand running sections // Auto-expand running sections
progressLog progressLog
.querySelectorAll(".tree-section.running, .tree-child.running") .querySelectorAll(".tree-section.running, .tree-child.running")
.forEach((el) => { .forEach((el) => {
el.classList.add("expanded"); el.classList.add("expanded");
}); });
} else {
// Incremental update - only update changed elements
updateProgressTree(tree, manifest, totalSteps);
}
// Update terminal stats // Update terminal stats
updateTerminalStats(taskId, manifest); updateTerminalStats(taskId, manifest);
@ -521,69 +546,116 @@ function renderManifestProgress(taskId, manifest, retryCount = 0) {
function updateStatusSection(manifest) { function updateStatusSection(manifest) {
const statusContent = document.querySelector(".taskmd-status-content"); const statusContent = document.querySelector(".taskmd-status-content");
if (!statusContent) return; if (!statusContent) {
console.log("[Manifest] No status content element found");
return;
}
// Update current action // Update current action
const actionText = statusContent.querySelector( const actionText = statusContent.querySelector(
".status-current .status-text", ".status-current .status-text",
); );
if (actionText && manifest.status?.current_action) { const currentAction =
actionText.textContent = manifest.status.current_action; manifest.status?.current_action ||
manifest.current_status?.current_action ||
"Processing...";
if (actionText) {
actionText.textContent = currentAction;
} }
// Update runtime // Update runtime
const runtimeEl = statusContent.querySelector(".status-main .status-time"); const runtimeEl = statusContent.querySelector(".status-main .status-time");
if (runtimeEl && manifest.status?.runtime_display) { const runtime =
runtimeEl.innerHTML = `Runtime: ${manifest.status.runtime_display} <span class="status-indicator"></span>`; manifest.status?.runtime_display || manifest.runtime || "Not started";
if (runtimeEl) {
runtimeEl.innerHTML = `Runtime: ${runtime} <span class="status-indicator"></span>`;
} }
// Update estimated // Update estimated
const estimatedEl = statusContent.querySelector( const estimatedEl = statusContent.querySelector(
".status-current .status-time", ".status-current .status-time",
); );
if (estimatedEl && manifest.status?.estimated_display) { const estimated =
estimatedEl.innerHTML = `Estimated: ${manifest.status.estimated_display} <span class="status-gear">⚙</span>`; manifest.status?.estimated_display ||
(manifest.estimated_seconds
? `${manifest.estimated_seconds} sec`
: "calculating...");
if (estimatedEl) {
estimatedEl.innerHTML = `Estimated: ${estimated} <span class="status-gear">⚙</span>`;
} }
console.log(
"[Manifest] Status updated - action:",
currentAction,
"runtime:",
runtime,
"estimated:",
estimated,
);
} }
function buildProgressTreeHTML(manifest, totalSteps) { function buildProgressTreeHTML(manifest, totalSteps) {
let html = '<div class="taskmd-tree">'; let html = '<div class="taskmd-tree">';
for (const section of manifest.sections) { for (const section of manifest.sections) {
const statusClass = section.status?.toLowerCase() || "pending"; // Normalize status - backend sends "Running", "Completed", etc.
const rawStatus = section.status || "Pending";
const statusClass = rawStatus.toLowerCase();
const isRunning = statusClass === "running"; const isRunning = statusClass === "running";
const globalCurrent = const globalCurrent =
section.progress?.global_current || section.progress?.current || 0; section.progress?.global_current || section.progress?.current || 0;
const statusText = section.status || "Pending";
console.log(
"[Manifest] Section:",
section.name,
"status:",
rawStatus,
"children:",
section.children?.length,
);
html += ` html += `
<div class="tree-section ${statusClass}${isRunning ? " expanded" : ""}" data-section-id="${section.id}"> <div class="tree-section ${statusClass}${isRunning ? " expanded" : ""}" data-section-id="${section.id}">
<div class="tree-row tree-level-0" onclick="this.parentElement.classList.toggle('expanded')"> <div class="tree-row tree-level-0" onclick="this.parentElement.classList.toggle('expanded')">
<span class="tree-name">${escapeHtml(section.name)}</span> <span class="tree-name">${escapeHtml(section.name)}</span>
<span class="tree-step-badge">Step ${globalCurrent}/${totalSteps}</span> <span class="tree-step-badge">Step ${globalCurrent}/${totalSteps}</span>
<span class="tree-status ${statusClass}">${statusText}</span> <span class="tree-status ${statusClass}">${rawStatus}</span>
<span class="tree-section-dot ${statusClass}"></span> <span class="tree-section-dot ${statusClass}"></span>
</div> </div>
<div class="tree-children">`; <div class="tree-children">`;
// Children // Children (e.g., "Database Schema Design" under "Database & Models")
if (section.children?.length > 0) { if (section.children && section.children.length > 0) {
for (const child of section.children) { for (const child of section.children) {
const childStatus = child.status?.toLowerCase() || "pending"; const childRawStatus = child.status || "Pending";
const childStatus = childRawStatus.toLowerCase();
const childIsRunning = childStatus === "running"; const childIsRunning = childStatus === "running";
console.log(
"[Manifest] Child:",
child.name,
"status:",
childRawStatus,
"items:",
(child.item_groups?.length || 0) + (child.items?.length || 0),
);
html += ` html += `
<div class="tree-child ${childStatus}${childIsRunning ? " expanded" : ""}" data-child-id="${child.id}"> <div class="tree-child ${childStatus}${childIsRunning ? " expanded" : ""}" data-child-id="${child.id}">
<div class="tree-row tree-level-1" onclick="this.parentElement.classList.toggle('expanded')"> <div class="tree-row tree-level-1" onclick="this.parentElement.classList.toggle('expanded')">
<span class="tree-item-dot ${childStatus}"></span> <span class="tree-item-dot ${childStatus}"></span>
<span class="tree-name">${escapeHtml(child.name)}</span> <span class="tree-name">${escapeHtml(child.name)}</span>
<span class="tree-step-badge">Step ${child.progress?.current || 0}/${child.progress?.total || 1}</span> <span class="tree-step-badge">Step ${child.progress?.current || 0}/${child.progress?.total || 1}</span>
<span class="tree-status ${childStatus}">${child.status || "Pending"}</span> <span class="tree-status ${childStatus}">${childRawStatus}</span>
</div> </div>
<div class="tree-items">`; <div class="tree-items">`;
// Items // Items within child (e.g., "email, password_hash, email_verified")
const items = [...(child.item_groups || []), ...(child.items || [])]; const childItems = [
for (const item of items) { ...(child.item_groups || []),
...(child.items || []),
];
for (const item of childItems) {
html += buildItemHTML(item); html += buildItemHTML(item);
} }
@ -591,7 +663,7 @@ function buildProgressTreeHTML(manifest, totalSteps) {
} }
} }
// Section-level items // Section-level items (items directly under section, not in children)
const sectionItems = [ const sectionItems = [
...(section.item_groups || []), ...(section.item_groups || []),
...(section.items || []), ...(section.items || []),