===== stageRollup / selectStage / selectedCategoryObject / renderCenterCategory / renderCenterBucket =====
480 els.selectedPipelineSteps.innerHTML = '
No pipeline contract foundmissing';
481 return;
482 }
483
484 els.selectedPipelineSteps.innerHTML = seq.map(function (step) {
485 const observed = stepStatuses.find(function (x) {
486 return normStageKey(x.step || x.name) === normStageKey(step);
487 });
488 const status = observed ? safeUpper(observed.status) : "MISSING";
489 return [
490 '',
491 '' + titleize(step) + '',
492 '' + status + '',
493 ''
494 ].join("");
495 }).join("");
496 }
497
498
499 function selectedPhaseObject() {
500 const phases = (state.hubIndex && Array.isArray(state.hubIndex.phases)) ? state.hubIndex.phases : [];
501 return phases.find(function (p) {
502 return normStageKey(p.id || p.stage_key || "") === normStageKey(state.selectedStage);
503 }) || null;
504 }
505
506 function selectedCategoryObject() {
507 if (!state.selectedCategory) return null;
508 const cats = (state.hubIndex && Array.isArray(state.hubIndex.categories)) ? state.hubIndex.categories : [];
509 return cats.find(function (c) {
510 return String(c.id || "") === String(state.selectedCategory);
511 }) || null;
512 }
513
514 function renderCenterCategory(ph, cat) {
515 if (!els.selectedStageName) return;
516 if (els.selectedStageName) els.selectedStageName.textContent = cat.title || ph.name || "Category";
517 if (els.selectedStageDesc) els.selectedStageDesc.textContent = cat.sub || "Category view from canonical hub index.";
518 if (els.selectedStageTotal) els.selectedStageTotal.innerHTML = ((cat.docs || []).length) + ' docs
in category';
519 if (els.selectedStageDonut) els.selectedStageDonut.innerHTML = donutMarkup(0, "category");
520 if (els.selectedPipelineSteps) {
521 const docs = Array.isArray(cat.docs) ? cat.docs : [];
522 els.selectedPipelineSteps.innerHTML = docs.length ? docs.map(function (doc) {
523 return '' + (doc.title || doc.label || doc.id || "item") + '' + (doc.type || "doc") + '';
524 }).join("") : 'No documents registeredMISSING';
525 }
526 }
527
528 function renderCenterBucket(ph, cat, bucket) {
529 if (els.selectedStageName) els.selectedStageName.textContent = (cat.title || ph.name || "Category") + " / " + titleize(bucket);
530 if (els.selectedStageDesc) els.selectedStageDesc.textContent = "Bucket detail from canonical category documents.";
531
532 const docs = Array.isArray(cat.docs) ? cat.docs : [];
533 const bucketDocs = bucket === "authority_docs" ? docs : [];
534
535 if (els.selectedStageTotal) els.selectedStageTotal.innerHTML = bucketDocs.length + ' docs
in bucket view';
536 if (els.selectedStageDonut) els.selectedStageDonut.innerHTML = donutMarkup(0, titleize(bucket));
537
538 if (els.selectedPipelineSteps) {
539 els.selectedPipelineSteps.innerHTML = bucketDocs.length ? bucketDocs.map(function (doc) {
540 return [
541 '',
542 '' + (doc.title || doc.label || doc.id || "item") + '',
543 '' + (doc.type || "doc") + '',
544 ''
545 ].join("");
546 }).join("") : 'No documents registered in this bucket.MISSING';
547 }
548 }
549
550 function renderCanonicalSelection() {
551 const ph = selectedPhaseObject();
552 const cat = selectedCategoryObject();
553
554 if (!ph) return;
555
556 if (cat && state.selectedBucket) {
557 renderCenterBucket(ph, cat, state.selectedBucket);
558 return;
559 }
560
561 if (cat) {
562 renderCenterCategory(ph, cat);
563 return;
564 }
565 }
566
567
568 function selectStage(stageKey) {
569 state.selectedStage = normStageKey(stageKey);
570 const items = ((state.contentIndex && state.contentIndex.content_index) || []);
571 const navItem = items.find(function (item) {
572 return normStageKey(item.stage_key) === state.selectedStage;
573 }) || null;
574 const rollup = stageRollup(state.selectedStage);
575 const pct = rollup && typeof rollup.progress_pct === "number" ? Math.round(rollup.progress_pct) : 0;
576 const totalSteps = rollup && Array.isArray(rollup.step_statuses) ? rollup.step_statuses.length : 0;
577
578 if (els.heroTitle) els.heroTitle.textContent = navItem ? navItem.label : titleize(state.selectedStage);
579 if (els.heroSubtitle) {
580 els.heroSubtitle.textContent = "Runtime-backed stage view. Contract and runtime data are rendered from panel exports only.";
581 }
582 if (els.stageOrderBadge) {
583 els.stageOrderBadge.textContent = navItem ? ("ORDER " + navItem.deployment_order) : "—";
584 }
585 if (els.selectedStageName) {
586 els.selectedStageName.textContent = navItem ? navItem.label : titleize(state.selectedStage);
587 }
588 if (els.selectedStageDesc) {
589 els.selectedStageDesc.textContent = "Stage completion is read from runtime_status stage_rollup.progress_pct.";
590 }
591 if (els.selectedStageTotal) {
592 els.selectedStageTotal.innerHTML = totalSteps
593 ? (totalSteps + ' required steps
in contract')
594 : 'MISSING stage rollup
in runtime';
595 }
596 if (els.selectedStageDonut) {
597 els.selectedStageDonut.innerHTML = donutMarkup(pct, "stage completion");
598 }
599 if (els.selectedRuntimeBadge) {
600 setBadge(els.selectedRuntimeBadge, rollup ? runtimeStatusOf(rollup) : "MISSING", rollup ? runtimeStatusOf(rollup) : "MISSING");
601 }
602
603 renderSelectedPipeline(state.selectedStage, rollup);
604 renderCanonicalSelection();
605
606 document.querySelectorAll(".stage-btn").forEach(function (btn) {
607 btn.classList.toggle("active", btn.dataset.stageKey === state.selectedStage);
608 });
609 }
610
611 function wireButtons() {
612 [
613 ["Manifest", els.btnOpenManifest, state.manifest],
614 ["Project Progress", els.btnOpenContract, state.projectProgress],
615 ["Host Runtime", els.btnOpenHost, state.hostRuntime],
616 ["Docker Runtime", els.btnOpenDocker, state.dockerRuntime],
617 ["Runtime Status", els.btnOpenRuntime, state.runtimeStatus]
618 ].forEach(function (entry) {
619 const label = entry[0];
620 const btn = entry[1];
===== runtime loaders =====
363: const snapshot = ((state.runtimeStatus && state.runtimeStatus.runtime_snapshot) || []);
376: const snapshot = ((state.runtimeStatus && state.runtimeStatus.runtime_snapshot) || []);
448: const items = ((state.runtimeStatus && state.runtimeStatus.runtime_snapshot) || []);
617: ["Runtime Status", els.btnOpenRuntime, state.runtimeStatus]
636: getJsonOptional("subcategory_pipelines.json"),
640: getJsonOptional("runtime_status.json")
651: state.runtimeStatus = core[8];