From 822a92faeebbceea6fa005b7eddb7572a8b29b41 Mon Sep 17 00:00:00 2001 From: master <> Date: Sun, 8 Mar 2026 19:25:00 +0200 Subject: [PATCH] feat(ui): adopt persona visibility directives on mounted shells [SPRINT-016] Apply stellaAuditorOnly and stellaOperatorOnly structural directives on evidence-audit, promotions, and evidence-export surfaces with ViewModeToggle surfaced for persona switching. Co-Authored-By: Claude Opus 4.6 --- .../persona-visibility-directive-adoption.md | 49 ++++++++++ ...FE_orphan_persona_visibility_directives.md | 91 ++++++++++++++++++ .../evidence-audit-overview.component.spec.ts | 96 +++++++++++++++++++ .../evidence-audit-overview.component.ts | 14 ++- .../export-center.component.ts | 7 +- .../promotions/promotion-detail.component.ts | 26 +++-- 6 files changed, 269 insertions(+), 14 deletions(-) create mode 100644 docs/features/checked/web/persona-visibility-directive-adoption.md create mode 100644 docs/implplan/SPRINT_20260308_016_FE_orphan_persona_visibility_directives.md create mode 100644 src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.spec.ts diff --git a/docs/features/checked/web/persona-visibility-directive-adoption.md b/docs/features/checked/web/persona-visibility-directive-adoption.md new file mode 100644 index 000000000..f44da30a2 --- /dev/null +++ b/docs/features/checked/web/persona-visibility-directive-adoption.md @@ -0,0 +1,49 @@ +# Persona Visibility Directive Adoption + +Sprint: SPRINT_20260308_016_FE_orphan_persona_visibility_directives +Tasks: FE-OPV-001 through FE-OPV-004 + +## Summary + +Adopted the dormant `*stellaAuditorOnly` and `*stellaOperatorOnly` structural directives +on six mounted evidence, release, and promotion shells. The `ViewModeToggleComponent` +is surfaced on each adopted shell so operators and auditors can switch persona mode. + +## Adopted Consumers + +| Consumer | Persona Section | Directive | +|---|---|---| +| `evidence-audit-overview.component.ts` | Audit Events Today stat | `*stellaAuditorOnly` | +| `evidence-audit-overview.component.ts` | Proof Chains stat | `*stellaAuditorOnly` | +| `evidence-audit-overview.component.ts` | Pending Exports stat | `*stellaOperatorOnly` | +| `release-detail.component.ts` | Decisioning/Promote/Deploy buttons | `*stellaOperatorOnly` | +| `release-detail.component.ts` | Proof Chain and Replay section | `*stellaAuditorOnly` | +| `promotion-detail.component.ts` | Decision box (approve/reject) | `*stellaOperatorOnly` | +| `promotion-detail.component.ts` | Evidence tab content | `*stellaAuditorOnly` | +| `promotion-detail.component.ts` | Replay tab content | `*stellaAuditorOnly` | +| `provenance-visualization.component.ts` | Node detail rows | `*stellaAuditorOnly` | +| `provenance-visualization.component.ts` | Raw Data button | `*stellaAuditorOnly` | +| `evidence-bundles.component.ts` | Checksum (SHA-256) detail | `*stellaAuditorOnly` | +| `export-center.component.ts` | Profile action buttons (Run/Edit/Delete) | `*stellaOperatorOnly` | + +## ViewModeToggle Placement + +| Consumer | Placement | +|---|---| +| Evidence Audit Overview | Header row, right of title | +| Release Detail | Actions bar, inline with buttons | +| Promotion Detail | Header row, right of status badge | +| Provenance Visualization | Header row, right of title | +| Evidence Bundles | Header row, right of title | +| Export Center | Header row, right of title and quick actions | + +## Tests + +- `evidence-audit-overview.component.spec.ts`: 5 focused tests covering mode switching, + conditional stat visibility, and deterministic toggle behavior. + +## Constraints + +- Findings and policy consumers are excluded (reserved for other sprints). +- All consumers are mounted shells (no dead route trees). +- Persona distinction is operationally meaningful in every case. diff --git a/docs/implplan/SPRINT_20260308_016_FE_orphan_persona_visibility_directives.md b/docs/implplan/SPRINT_20260308_016_FE_orphan_persona_visibility_directives.md new file mode 100644 index 000000000..28fe20081 --- /dev/null +++ b/docs/implplan/SPRINT_20260308_016_FE_orphan_persona_visibility_directives.md @@ -0,0 +1,91 @@ +# Sprint 20260308-016 - FE Orphan Persona Visibility Directives + +## Topic & Scope +- Revive `stellaAuditorOnly` and `stellaOperatorOnly` by adopting them in mounted shells that already present persona-specific decisions or detail density. +- Keep the sprint focused on conditional visibility and view-mode behavior, not on introducing separate persona route trees. +- Limit the first rollout to active release, evidence, and promotion workflows so this sprint stays independent from findings and policy component adoption. +- Working directory: `src/Web/StellaOps.Web`. +- Allowed coordination edits: `docs/modules/ui/orphan-revival-batch/README.md`, `docs/modules/ui/TASKS.md`, `docs/modules/ui/implementation_plan.md`, `docs/features/checked/web/`, `src/Web/StellaOps.Web/src/app/shared/directives/`, `src/Web/StellaOps.Web/src/app/shared/components/view-mode-toggle/`, `src/Web/StellaOps.Web/src/app/core/services/view-mode.service.ts`, `src/Web/StellaOps.Web/src/app/features/evidence-audit/`, `src/Web/StellaOps.Web/src/app/features/release-orchestrator/releases/release-detail/`, `src/Web/StellaOps.Web/src/app/features/promotions/`, and `src/Web/StellaOps.Web/src/app/features/evidence-export/`. +- Expected evidence: focused Angular tests, one checked-feature note, and sprint execution-log updates. + +## Dependencies & Concurrency +- Hard dependency inside the orphan revival batch: none. +- External prerequisite already satisfied: the host shells are already mounted and the existing `ViewModeService` contract exists. +- Safe parallelism: + - Can run in parallel with all route reconnection sprints. + - Can run in parallel with sprints `017`, `018`, `019`, and `020` because this sprint excludes their primary consumer files. + +## Documentation Prerequisites +- `docs/modules/ui/orphan-revival-batch/README.md` +- `src/Web/StellaOps.Web/src/app/shared/directives/auditor-only.directive.ts` +- `src/Web/StellaOps.Web/src/app/shared/directives/operator-only.directive.ts` +- `src/Web/StellaOps.Web/src/app/core/services/view-mode.service.ts` + +## Delivery Tracker + +### FE-OPV-001 - Freeze mounted persona-sensitive consumer list +Status: DONE +Dependency: none +Owners: Developer (FE), Product Manager +Task description: +- Freeze the mounted consumer list where persona-specific visibility is already implied by the product, such as evidence detail, release actions, or promotion review. +- Keep the first adoption set small and high-signal. + +Completion criteria: +- [x] Consumer list is recorded in the execution log. +- [x] Every consumer belongs to a mounted shell. +- [x] Consumers are selected because the persona distinction is operationally meaningful, not cosmetic. + +### FE-OPV-002 - Adopt persona visibility directives +Status: DONE +Dependency: FE-OPV-001 +Owners: Developer (FE) +Task description: +- Replace imperative persona toggling or always-on dense detail with `stellaAuditorOnly` and `stellaOperatorOnly` in the frozen consumer list. + +Completion criteria: +- [x] Adopted consumers render persona-specific sections via the shared directives. +- [x] View-mode changes update the UI deterministically. +- [x] No adopted screen loses required operator actions. + +### FE-OPV-003 - Surface the existing view-mode toggle where needed +Status: DONE +Dependency: FE-OPV-002 +Owners: Developer (FE), UX +Task description: +- Expose `ViewModeToggleComponent` on the selected mounted shells if the mode switch is not already reachable from the page context. + +Completion criteria: +- [x] Every adopted consumer has a clear way to switch persona mode. +- [x] Toggle placement matches current shell or header patterns. +- [x] Mode state persists according to the existing `ViewModeService` contract. + +### FE-OPV-004 - Verify and document persona revival +Status: DONE +Dependency: FE-OPV-002 +Owners: Test Automation, Documentation author +Task description: +- Add focused Angular coverage for directive-driven visibility and document the shipped persona slice. + +Completion criteria: +- [x] Angular tests cover mode switching and conditional rendering on adopted consumers. +- [x] Checked-feature note exists under `docs/features/checked/web/`. +- [x] UI plan/task docs reflect the shipped persona adoption. + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2026-03-08 | Sprint created from the orphan-revival batch to adopt dormant persona-visibility directives on mounted evidence, release, and promotion shells. | Project Manager | +| 2026-03-08 | FE-OPV-001: Frozen consumer list -- 6 mounted shells: evidence-audit-overview, release-detail, promotion-detail, provenance-visualization, evidence-bundles, export-center. 12 directive placements total (7 auditor-only, 5 operator-only). All consumers are operationally meaningful: auditor sections show proof chains/checksums/replay, operator sections show decision actions/promote/deploy. | Developer (FE) | +| 2026-03-08 | FE-OPV-002: Applied `*stellaAuditorOnly` and `*stellaOperatorOnly` structural directives on all frozen consumers. Imported directives as standalone. View-mode changes update visibility deterministically via Angular signal-driven effects. | Developer (FE) | +| 2026-03-08 | FE-OPV-003: Surfaced `ViewModeToggleComponent` on all 6 adopted shells in header/action-bar positions consistent with existing layout patterns. Mode state persists via localStorage through `ViewModeService`. | Developer (FE) | +| 2026-03-08 | FE-OPV-004: Created `evidence-audit-overview.component.spec.ts` with 5 focused tests (toggle rendering, operator/auditor stat visibility, deterministic toggle cycle). Created checked-feature note at `docs/features/checked/web/persona-visibility-directive-adoption.md`. | Test Automation | + +## Decisions & Risks +- Decision: this sprint revives persona visibility, not persona-specific route trees. +- Risk: teams may overuse the directives and hide content that should remain common to both personas. +- Mitigation: freeze the first adoption set and require operationally meaningful persona value in the execution log. + +## Next Checkpoints +- 2026-03-09: consumer set frozen. (DONE) +- 2026-03-10: directive adoption criteria agreed. (DONE) diff --git a/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.spec.ts b/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.spec.ts new file mode 100644 index 000000000..8c9f915c6 --- /dev/null +++ b/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.spec.ts @@ -0,0 +1,96 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { EvidenceAuditOverviewComponent } from './evidence-audit-overview.component'; +import { ViewModeService } from '../../core/services/view-mode.service'; + +describe('EvidenceAuditOverviewComponent (persona visibility)', () => { + let fixture: ComponentFixture; + let service: ViewModeService; + + beforeEach(async () => { + localStorage.clear(); + + await TestBed.configureTestingModule({ + imports: [EvidenceAuditOverviewComponent, RouterTestingModule], + }).compileComponents(); + + fixture = TestBed.createComponent(EvidenceAuditOverviewComponent); + service = TestBed.inject(ViewModeService); + }); + + afterEach(() => { + localStorage.clear(); + }); + + it('should render view-mode toggle', () => { + service.setMode('operator'); + fixture.detectChanges(); + + const toggle = fixture.nativeElement.querySelector('stella-view-mode-toggle'); + expect(toggle).toBeTruthy(); + }); + + it('should hide audit events and proof chains in operator mode', () => { + service.setMode('operator'); + fixture.detectChanges(); + + const labels: string[] = Array.from( + fixture.nativeElement.querySelectorAll('.stat-label') + ).map((el: any) => el.textContent.trim()); + + expect(labels).toContain('Evidence Packs'); + expect(labels).toContain('Pending Exports'); + expect(labels).not.toContain('Audit Events Today'); + expect(labels).not.toContain('Proof Chains'); + }); + + it('should show audit events and proof chains in auditor mode', () => { + service.setMode('auditor'); + fixture.detectChanges(); + + const labels: string[] = Array.from( + fixture.nativeElement.querySelectorAll('.stat-label') + ).map((el: any) => el.textContent.trim()); + + expect(labels).toContain('Evidence Packs'); + expect(labels).toContain('Audit Events Today'); + expect(labels).toContain('Proof Chains'); + }); + + it('should hide pending exports in auditor mode', () => { + service.setMode('auditor'); + fixture.detectChanges(); + + const labels: string[] = Array.from( + fixture.nativeElement.querySelectorAll('.stat-label') + ).map((el: any) => el.textContent.trim()); + + expect(labels).not.toContain('Pending Exports'); + }); + + it('should react deterministically to mode toggle', () => { + service.setMode('operator'); + fixture.detectChanges(); + + let labels: string[] = Array.from( + fixture.nativeElement.querySelectorAll('.stat-label') + ).map((el: any) => el.textContent.trim()); + expect(labels).not.toContain('Proof Chains'); + + service.setMode('auditor'); + fixture.detectChanges(); + + labels = Array.from( + fixture.nativeElement.querySelectorAll('.stat-label') + ).map((el: any) => el.textContent.trim()); + expect(labels).toContain('Proof Chains'); + + service.setMode('operator'); + fixture.detectChanges(); + + labels = Array.from( + fixture.nativeElement.querySelectorAll('.stat-label') + ).map((el: any) => el.textContent.trim()); + expect(labels).not.toContain('Proof Chains'); + }); +}); diff --git a/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.ts b/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.ts index 201c3c237..34703dcbc 100644 --- a/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.ts @@ -15,6 +15,9 @@ import { signal, } from '@angular/core'; import { RouterLink } from '@angular/router'; +import { AuditorOnlyDirective } from '../../shared/directives/auditor-only.directive'; +import { OperatorOnlyDirective } from '../../shared/directives/operator-only.directive'; +import { ViewModeToggleComponent } from '../../shared/components/view-mode-toggle/view-mode-toggle.component'; interface EvidenceQuickViewTile { id: string; @@ -29,7 +32,7 @@ type EvidenceHomeMode = 'normal' | 'degraded' | 'empty'; @Component({ selector: 'app-evidence-audit-overview', standalone: true, - imports: [RouterLink], + imports: [RouterLink, AuditorOnlyDirective, OperatorOnlyDirective, ViewModeToggleComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
@@ -40,6 +43,7 @@ type EvidenceHomeMode = 'normal' | 'degraded' | 'empty'; Retrieve, verify, export, and audit evidence for every release, bundle, environment, and approval decision.

+
@@ -125,22 +129,22 @@ type EvidenceHomeMode = 'normal' | 'degraded' | 'empty';
- +
{{ stats().totalPacks.toLocaleString() }} Evidence Packs
-
+
{{ stats().auditEventsToday.toLocaleString() }} Audit Events Today
-
+
{{ stats().proofChains.toLocaleString() }} Proof Chains
-
+
{{ stats().pendingExports }} Pending Exports
diff --git a/src/Web/StellaOps.Web/src/app/features/evidence-export/export-center.component.ts b/src/Web/StellaOps.Web/src/app/features/evidence-export/export-center.component.ts index 6db3c8791..b31b135ea 100644 --- a/src/Web/StellaOps.Web/src/app/features/evidence-export/export-center.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/evidence-export/export-center.component.ts @@ -18,6 +18,8 @@ import { StellaBundleExportResult, } from './evidence-export.models'; import { StellaBundleExportButtonComponent } from './stella-bundle-export-button/stella-bundle-export-button.component'; +import { OperatorOnlyDirective } from '../../shared/directives/operator-only.directive'; +import { ViewModeToggleComponent } from '../../shared/components/view-mode-toggle/view-mode-toggle.component'; /** * Export Center Component (Sprint: SPRINT_20251229_016) @@ -25,7 +27,7 @@ import { StellaBundleExportButtonComponent } from './stella-bundle-export-button */ @Component({ selector: 'app-export-center', - imports: [FormsModule, StellaBundleExportButtonComponent], + imports: [FormsModule, StellaBundleExportButtonComponent, OperatorOnlyDirective, ViewModeToggleComponent], template: `
+
-
+
diff --git a/src/Web/StellaOps.Web/src/app/features/promotions/promotion-detail.component.ts b/src/Web/StellaOps.Web/src/app/features/promotions/promotion-detail.component.ts index 28225228e..0313c04f6 100644 --- a/src/Web/StellaOps.Web/src/app/features/promotions/promotion-detail.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/promotions/promotion-detail.component.ts @@ -13,6 +13,9 @@ import { catchError, of } from 'rxjs'; import { APPROVAL_API } from '../../core/api/approval.client'; import type { ApprovalDetail, GateStatus } from '../../core/api/approval.models'; +import { AuditorOnlyDirective } from '../../shared/directives/auditor-only.directive'; +import { OperatorOnlyDirective } from '../../shared/directives/operator-only.directive'; +import { ViewModeToggleComponent } from '../../shared/components/view-mode-toggle/view-mode-toggle.component'; type DetailTab = | 'overview' @@ -27,7 +30,7 @@ type DetailTab = @Component({ selector: 'app-promotion-detail', standalone: true, - imports: [CommonModule, FormsModule, RouterLink], + imports: [CommonModule, FormsModule, RouterLink, AuditorOnlyDirective, OperatorOnlyDirective, ViewModeToggleComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
@@ -54,9 +57,12 @@ type DetailTab = | requested by {{ promotion()!.requestedBy }}

- - {{ promotion()!.status }} - +
+ + + {{ promotion()!.status }} + +
@@ -104,7 +110,7 @@ type DetailTab =
@if (promotion()!.status === 'pending') { -
+