521
522
523 function selectedPhaseObject() {
524 const phases = (state.hubIndex && Array.isArray(state.hubIndex.phases)) ? state.hubIndex.phases : [];
525 return phases.find(function (p) {
526 return normStageKey(p.id || p.stage_key || "") === normStageKey(state.selectedStage);
527 }) || null;
528 }
529
530 function selectedCategoryObject() {
531 if (!state.selectedCategory) return null;
532 const cats = (state.hubIndex && Array.isArray(state.hubIndex.categories)) ? state.hubIndex.categories : [];
533 return cats.find(function (c) {
534 return String(c.id || "") === String(state.selectedCategory);
535 }) || null;
536 }
537
538
539 function renderCenterPhase(ph) {
540 if (!ph) return;
541 if (els.selectedStageName) els.selectedStageName.textContent = ph.name || "Phase";
542 if (els.selectedStageDesc) els.selectedStageDesc.textContent = ph.sub || "Canonical phase view from hub index.";
543 if (els.selectedStageTotal) {
544 const cats = (state.hubIndex && Array.isArray(state.hubIndex.categories))
545 ? state.hubIndex.categories.filter(function (c) { return String(c.phase_id || "") === String(ph.id || ""); })
546 : [];
547 els.selectedStageTotal.innerHTML = cats.length + ' categories
in phase';
548 }
549 if (els.selectedStageDonut) els.selectedStageDonut.innerHTML = donutMarkup(0, "phase");
550 if (els.selectedPipelineSteps) {
551 const cats = (state.hubIndex && Array.isArray(state.hubIndex.categories))
552 ? state.hubIndex.categories.filter(function (c) { return String(c.phase_id || "") === String(ph.id || ""); })
553 : [];
554 els.selectedPipelineSteps.innerHTML = cats.length ? cats.map(function (cat) {
555 return '
' + (cat.title || cat.id || "category") + 'category';
556 }).join("") : 'No categories registeredMISSING';
557 }
558 }
559
560 function renderCenterCategory(ph, cat) {
561 if (!els.selectedStageName) return;
562 if (els.selectedStageName) els.selectedStageName.textContent = cat.title || ph.name || "Category";
563 if (els.selectedStageDesc) els.selectedStageDesc.textContent = cat.sub || "Category view from canonical hub index.";
564 if (els.selectedStageTotal) els.selectedStageTotal.innerHTML = ((cat.docs || []).length) + ' docs
in category';
565 if (els.selectedStageDonut) els.selectedStageDonut.innerHTML = donutMarkup(0, "category");
566 if (els.selectedPipelineSteps) {
567 const docs = Array.isArray(cat.docs) ? cat.docs : [];
568 els.selectedPipelineSteps.innerHTML = docs.length ? docs.map(function (doc) {
569 return '' + (doc.title || doc.label || doc.id || "item") + '' + (doc.type || "doc") + '';
570 }).join("") : 'No documents registeredMISSING';
571 }
572 }
573
574 function renderCenterBucket(ph, cat, bucket) {
575 if (els.selectedStageName) els.selectedStageName.textContent = (cat.title || ph.name || "Category") + " / " + titleize(bucket);
576 if (els.selectedStageDesc) els.selectedStageDesc.textContent = "Bucket detail from canonical category documents.";
577
578 const docs = Array.isArray(cat.docs) ? cat.docs : [];
579 const bucketDocs = bucket === "authority_docs" ? docs : [];
580
581 if (els.selectedStageTotal) els.selectedStageTotal.innerHTML = bucketDocs.length + ' docs
in bucket view';
582 if (els.selectedStageDonut) els.selectedStageDonut.innerHTML = donutMarkup(0, titleize(bucket));
583
584 if (els.selectedPipelineSteps) {
585 els.selectedPipelineSteps.innerHTML = bucketDocs.length ? bucketDocs.map(function (doc) {
586 return [
587 '',
588 '' + (doc.title || doc.label || doc.id || "item") + '',
589 '' + (doc.type || "doc") + '',
590 ''
591 ].join("");
592 }).join("") : 'No documents registered in this bucket.MISSING';
593 }
594 }
595
596 function renderCanonicalSelection() {
597 const ph = selectedPhaseObject();
598 const cat = selectedCategoryObject();
599
600 if (!ph) return;
601
602 if (cat && state.selectedBucket) {
603 renderCenterBucket(ph, cat, state.selectedBucket);
604 return;
605 }
606
607 if (cat) {
608 renderCenterCategory(ph, cat);
609 return;
610 }
611
612 renderCenterPhase(ph);
613 }
614
615
616 function selectStage(stageKey) {
617 state.selectedStage = normStageKey(stageKey);
618 const canonicalPhase = isCanonicalPhaseKey(state.selectedStage);
619 const items = ((state.contentIndex && state.contentIndex.content_index) || []);
620 const navItem = items.find(function (item) {
621 return normStageKey(item.stage_key) === state.selectedStage;
622 }) || null;
623
624 if (canonicalPhase) {
625 const ph = selectedPhaseObject();
626
627 if (els.heroTitle) els.heroTitle.textContent = ph ? (ph.name || titleize(state.selectedStage)) : titleize(state.selectedStage);
628 if (els.heroSubtitle) {
629 els.heroSubtitle.textContent = "Canonical phase view from hub_index. Runtime bridge is not applied at phase namespace.";
630 }
631 if (els.stageOrderBadge) {
632 els.stageOrderBadge.textContent = ph && ph.order ? ("PHASE " + ph.order) : "PHASE";
633 }
634 if (els.selectedRuntimeBadge) {
635 setBadge(els.selectedRuntimeBadge, "N/A", "CANON");
636 }
637
638 renderCanonicalSelection();
639
640 document.querySelectorAll(".stage-btn").forEach(function (btn) {
641 btn.classList.toggle("active", btn.dataset.stageKey === state.selectedStage);
642 });
643 return;
644 }
645
646 const runtimeEligible = hasRuntimeStageKey(state.selectedStage);
647 const pipelineEligible = hasPipelineStageKey(state.selectedStage);
648 const rollup = runtimeEligible ? stageRollup(state.selectedStage) : null;
649 const pct = rollup && typeof rollup.progress_pct === "number" ? Math.round(rollup.progress_pct) : 0;
650 const totalSteps = rollup && Array.isArray(rollup.step_statuses) ? rollup.step_statuses.length : 0;
651
652 if (els.heroTitle) els.heroTitle.textContent = navItem ? navItem.label : titleize(state.selectedStage);
653 if (els.heroSubtitle) {
654 els.heroSubtitle.textContent = "Runtime-backed stage view. Contract and runtime data are rendered from panel exports only.";
655 }
656 if (els.stageOrderBadge) {
657 els.stageOrderBadge.textContent = navItem ? ("ORDER " + navItem.deployment_order) : "—";
658 }
659 if (els.selectedStageName) {
660 els.selectedStageName.textContent = navItem ? navItem.label : titleize(state.selectedStage);
661 }
662 if (els.selectedStageDesc) {
663 els.selectedStageDesc.textContent = runtimeEligible
664 ? "Stage completion is read from runtime_status stage_rollup.progress_pct."
665 : "Runtime stage key missing in exports.";
666 }
667 if (els.selectedStageTotal) {
668 els.selectedStageTotal.innerHTML = runtimeEligible && totalSteps
669 ? (totalSteps + ' required steps
in contract')
670 : 'Runtime contract
missing';
671 }
672 if (els.selectedStageDonut) {
673 els.selectedStageDonut.innerHTML = runtimeEligible
674 ? donutMarkup(pct, "stage completion")
675 : donutMarkup(0, "runtime");
676 }
677 if (els.selectedRuntimeBadge) {
678 if (runtimeEligible && rollup) {
679 setBadge(els.selectedRuntimeBadge, runtimeStatusOf(rollup), runtimeStatusOf(rollup));
680 } else {
681 setBadge(els.selectedRuntimeBadge, "MISSING", "N/A");
682 }
683 }
684
685 if (runtimeEligible || pipelineEligible) {
686 renderSelectedPipeline(state.selectedStage, rollup);
687 } else if (els.selectedPipelineSteps) {
688 els.selectedPipelineSteps.innerHTML = 'No pipeline contract foundmissing';
689 }
690
691 document.querySelectorAll(".stage-btn").forEach(function (btn) {
692 btn.classList.toggle("active", btn.dataset.stageKey === state.selectedStage);
693 });
694 }
695
696 if (els.stageOrderBadge) {
697 els.stageOrderBadge.textContent = navItem ? ("ORDER " + navItem.deployment_order) : "—";
698 }
699 if (els.selectedStageName) {
700 els.selectedStageName.textContent = navItem ? navItem.label : titleize(state.selectedStage);
701 }
702 if (els.selectedStageDesc) {
703 els.selectedStageDesc.textContent = runtimeEligible
704 ? "Stage completion is read from runtime_status stage_rollup.progress_pct."
705 : "Canonical selection view. No sovereign runtime stage binding exists for this phase key.";
706 }
707 if (els.selectedStageTotal) {
708 els.selectedStageTotal.innerHTML = runtimeEligible && totalSteps
709 ? (totalSteps + ' required steps
in contract')
710 : 'Canonical selection
without runtime bridge';
711 }
712 if (els.selectedStageDonut) {
713 els.selectedStageDonut.innerHTML = runtimeEligible
714 ? donutMarkup(pct, "stage completion")
715 : donutMarkup(0, "canonical");
716 }
717 if (els.selectedRuntimeBadge) {
718 if (runtimeEligible && rollup) {
719 setBadge(els.selectedRuntimeBadge, runtimeStatusOf(rollup), runtimeStatusOf(rollup));
720 } else {
721 setBadge(els.selectedRuntimeBadge, "MISSING", "N/A");
722 }
723 }
724
725 if (runtimeEligible || pipelineEligible) {
726 renderSelectedPipeline(state.selectedStage, rollup);
727 } else if (els.selectedPipelineSteps) {
728 els.selectedPipelineSteps.innerHTML = 'Canonical selection has no runtime pipeline bindingn/a';
729 }
730
731 renderCanonicalSelection();
732
733 document.querySelectorAll(".stage-btn").forEach(function (btn) {
734 btn.classList.toggle("active", btn.dataset.stageKey === state.selectedStage);
735 });
736 }
737 }
738
739 boot();
740 })();
741
742
743 document.getElementById('btn-monitor')?.addEventListener('click', () => {
744 window.open('../monitor/', '_blank');
745 });