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 });