Restructure sidebar navigation to match product workflows

Major IA restructure based on product-level analysis of Stella Ops core
workflows (release lifecycle, policy gates, evidence chain, security posture).

New 7-group structure (was 5):
- Dashboard: new ungrouped home link (no group header)
- Release Control: Deployments, Releases, Environments (moved from Ops)
- Security: Vulnerabilities, Security Posture (+children), Scan Image
  (Reports removed — will merge into Posture in Phase B)
- Policy: NEW GROUP — Packs, Governance, Simulation, VEX & Exceptions,
  Release Gates, Policy Audit (promoted from buried Operations item)
- Operations: slimmed from 11→8 items (Hub, Jobs, Scripts, Signals,
  Diagnostics, Notifications, Feeds, Watchlist)
- Audit & Evidence: Overview, Capsules, Replay, Export, Audit Log,
  Bundles (route fixed to /evidence/bundles), Trust (merged name)
- Settings: unchanged

Rationale:
- Policy is a release-blocking gate with 6 deep sub-workflows — not an ops utility
- Environments define the promotion graph — belongs in Release Control
- Trust Audit + Trust Analytics merged into single "Trust" item
- Reports removed (duplicated Security Posture content)
- Versions removed from sidebar (will merge into Releases as tab in Phase C)
- Dashboard link ensures users can always navigate home without logo click

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-03-27 14:34:25 +02:00
parent 27b2759b00
commit f08ad767b7

View File

@@ -116,7 +116,7 @@ interface NavSectionGroup {
role="group"
[attr.aria-label]="group.label"
>
@if (!effectiveCollapsed) {
@if (!effectiveCollapsed && group.id !== 'home') {
<button
type="button"
class="sb-group__header"
@@ -626,10 +626,19 @@ export class AppSidebarComponent implements AfterViewInit {
private readonly pendingApprovalsBadgeLoading = signal(false);
/**
* Navigation sections - canonical 5-group IA.
* Groups: Release Control, Security, Operations, Audit & Evidence, Setup & Admin.
* Navigation sections - canonical 7-group IA.
* Groups: Home (ungrouped), Release Control, Security, Policy, Operations, Audit & Evidence, Setup & Admin.
*/
readonly navSections: NavSection[] = [
// ── Home (ungrouped) ───────────────────────────────────────────
{
id: 'dashboard',
label: 'Dashboard',
icon: 'home',
route: '/',
menuGroupId: 'home',
menuGroupLabel: '',
},
// ── Group 1: Release Control ─────────────────────────────────────
{
id: 'deployments',
@@ -660,18 +669,7 @@ export class AppSidebarComponent implements AfterViewInit {
StellaOpsScopes.RELEASE_PUBLISH,
],
},
{
id: 'versions',
label: 'Versions',
icon: 'layers',
route: '/releases/versions',
menuGroupId: 'release-control',
menuGroupLabel: 'Release Control',
requireAnyScope: [
StellaOpsScopes.RELEASE_READ,
StellaOpsScopes.RELEASE_WRITE,
],
},
// Versions merged into Releases page as a tab
// Approvals nav item removed — merged into Deployments page as a tab
// ── Group 2: Security ────────────────────────────────────────────
{
@@ -720,18 +718,7 @@ export class AppSidebarComponent implements AfterViewInit {
menuGroupLabel: 'Security',
requireAnyScope: [StellaOpsScopes.SCANNER_READ],
},
{
id: 'sec-reports',
label: 'Reports',
icon: 'book-open',
route: '/security/reports',
menuGroupId: 'security',
menuGroupLabel: 'Security',
requireAnyScope: [
StellaOpsScopes.SCANNER_READ,
StellaOpsScopes.FINDINGS_READ,
],
},
// Reports merged into Security Posture (export actions moved inline)
// ── Group 3: Operations ──────────────────────────────────────────
{
id: 'ops',
@@ -791,8 +778,8 @@ export class AppSidebarComponent implements AfterViewInit {
label: 'Environments',
icon: 'globe',
route: '/environments/regions',
menuGroupId: 'operations',
menuGroupLabel: 'Operations',
menuGroupId: 'release-control',
menuGroupLabel: 'Release Control',
requireAnyScope: [
StellaOpsScopes.ORCH_READ,
StellaOpsScopes.ORCH_OPERATE,
@@ -800,13 +787,58 @@ export class AppSidebarComponent implements AfterViewInit {
},
{
id: 'ops-policy',
label: 'Policy',
label: 'Packs',
icon: 'clipboard',
route: '/ops/policy',
menuGroupId: 'operations',
menuGroupLabel: 'Operations',
route: '/ops/policy/packs',
menuGroupId: 'policy',
menuGroupLabel: 'Policy',
requireAnyScope: [StellaOpsScopes.POLICY_READ],
},
{
id: 'ops-policy-governance',
label: 'Governance',
icon: 'shield',
route: '/ops/policy/governance',
menuGroupId: 'policy',
menuGroupLabel: 'Policy',
requireAnyScope: [StellaOpsScopes.POLICY_READ],
},
{
id: 'ops-policy-simulation',
label: 'Simulation',
icon: 'play',
route: '/ops/policy/simulation',
menuGroupId: 'policy',
menuGroupLabel: 'Policy',
requireAnyScope: [StellaOpsScopes.POLICY_SIMULATE],
},
{
id: 'ops-policy-vex',
label: 'VEX & Exceptions',
icon: 'file-text',
route: '/ops/policy/vex',
menuGroupId: 'policy',
menuGroupLabel: 'Policy',
requireAnyScope: [StellaOpsScopes.VEX_READ, StellaOpsScopes.EXCEPTION_READ],
},
{
id: 'ops-policy-gates',
label: 'Release Gates',
icon: 'check-square',
route: '/ops/policy/gates',
menuGroupId: 'policy',
menuGroupLabel: 'Policy',
requireAnyScope: [StellaOpsScopes.POLICY_READ],
},
{
id: 'ops-policy-audit',
label: 'Policy Audit',
icon: 'list',
route: '/ops/policy/audit',
menuGroupId: 'policy',
menuGroupLabel: 'Policy',
requireAnyScope: [StellaOpsScopes.POLICY_AUDIT],
},
{
id: 'ops-diagnostics',
label: 'Diagnostics',
@@ -846,15 +878,7 @@ export class AppSidebarComponent implements AfterViewInit {
menuGroupLabel: 'Operations',
requireAnyScope: [StellaOpsScopes.SIGNER_READ],
},
{
id: 'ops-trust-analytics',
label: 'Trust Analytics',
icon: 'trending-up',
route: '/ops/operations/trust-analytics',
menuGroupId: 'operations',
menuGroupLabel: 'Operations',
requireAnyScope: [StellaOpsScopes.SIGNER_READ],
},
// Trust Analytics merged into Trust (Audit & Evidence group)
// ── Group 4: Audit & Evidence ────────────────────────────────────
{
id: 'evidence-overview',
@@ -923,7 +947,7 @@ export class AppSidebarComponent implements AfterViewInit {
id: 'evidence-bundles',
label: 'Bundles',
icon: 'inbox',
route: '/triage/audit-bundles',
route: '/evidence/bundles',
menuGroupId: 'audit-evidence',
menuGroupLabel: 'Audit & Evidence',
requireAnyScope: [
@@ -932,8 +956,8 @@ export class AppSidebarComponent implements AfterViewInit {
],
},
{
id: 'evidence-trust-audit',
label: 'Trust Audit',
id: 'evidence-trust',
label: 'Trust',
icon: 'shield-check',
route: '/evidence/audit-log/trust',
menuGroupId: 'audit-evidence',
@@ -1013,7 +1037,7 @@ export class AppSidebarComponent implements AfterViewInit {
/** Menu groups rendered in deterministic order for scanability */
readonly displaySectionGroups = computed<NavSectionGroup[]>(() => {
const orderedGroups = new Map<string, NavSectionGroup>();
const groupOrder = ['release-control', 'security', 'operations', 'audit-evidence', 'setup-admin', 'misc'];
const groupOrder = ['home', 'release-control', 'security', 'policy', 'operations', 'audit-evidence', 'setup-admin', 'misc'];
for (const groupId of groupOrder) {
orderedGroups.set(groupId, {
@@ -1126,10 +1150,14 @@ export class AppSidebarComponent implements AfterViewInit {
private resolveMenuGroupLabel(groupId: string): string {
switch (groupId) {
case 'home':
return '';
case 'release-control':
return 'Release Control';
case 'security':
return 'Security';
case 'policy':
return 'Policy';
case 'operations':
return 'Operations';
case 'audit-evidence':