Smart-Diff UI Architecture
This document retains the December 2025 Smart-Diff design. Its old component, route and API examples are historical design proposals; they are not a current backend or operator contract.
Source boundary verified 2026-10-07
At commit ed225f7338382c180774c2ebf582353e34472ed6, the compare feature is under src/Web/StellaOps.Web/src/app/features/compare/, with route declarations in src/Web/StellaOps.Web/src/app/routes/security-risk.routes.ts. services/compare.service.ts declares /api/compare as its base and getItemEvidence(itemId) requests /api/compare/evidence/${itemId}. services/delta-compute.service.ts uses the same caller base for session delta reads. These are verified client request targets, not proof of backend response behavior or current live acceptance.
The Evidence host composes its own /api/evidence/v1/* namespace. Its consolidated route inventory does not contain the design’s delta-item, witness-path or VEX-merge read family. Moving those proposed paths under the Evidence prefix would invent a contract. Verify a compare caller against its actual owning endpoint before using it as an implementation reference.
Re-verify the feature clients and src/Evidence/__Tests/StellaOps.Evidence.WebService.Tests/Fixtures/consolidated-route-inventory.tsv. The Console architecture is the current module entry point.
Historical December 2025 design withdrawn 2026-10-07
The original diagrams, code examples, component tree, API table and test strategy below are retained for design history. They have no current implementation or runtime acceptance authority. The source boundary above takes precedence.
Currency note (2026-09-26, DOC-2). Design reference from 2025-12, moved here from
docs/modules/web/. The compare view is implemented undersrc/Web/StellaOps.Web/src/app/features/compare/and routed fromsrc/app/routes/security-risk.routes.ts. Component names, paths and endpoints below were not line-verified;src/wins where they differ.
Version: 1.1 Status: Historical design; implementation details withdrawn 2026-10-07 Last Updated: 2025-12-26 Sprint Reference: SPRINT_20251226_012_FE_smart_diff_compare
Overview
The Smart-Diff UI provides a dedicated comparison experience for analyzing material risk changes between container image versions. It implements a “diff-first” approach to vulnerability triage, enabling users to focus on what changed rather than reviewing entire vulnerability lists.
Design Principles
1. Diff-First Triage
The primary question in any release is: “What changed that affects risk?” The UI defaults to showing delta information rather than full vulnerability lists.
2. Proof-Carrying Evidence
Every verdict and comparison includes cryptographic evidence. Users can verify determinism, trace decisions to policy rules, and replay computations.
3. Baseline Transparency
Comparisons require explicit baselines with auditor-friendly rationale. The system never uses “magic” to select baselines without explanation.
4. Role-Based Defaults
Different personas (Developer, Security, Audit) see different default views while retaining access to all information.
Component Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ SMART-DIFF UI ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ COMPARE VIEW CONTAINER │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ Baseline │ │ Trust │ │ Export │ │ │
│ │ │ Selector │ │ Indicators │ │ Actions │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ │
│ │ │ │ │ │ │
│ │ ┌────────────────────────────────────────────────────────────────┐ │ │
│ │ │ DELTA SUMMARY STRIP │ │ │
│ │ │ [+N added] [-N removed] [~N changed] [Policy: v1.2] [Feed: 2h]│ │ │
│ │ └────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────────┐ │ │
│ │ │ THREE-PANE LAYOUT │ │ │
│ │ │ ┌──────────┐ ┌────────────────┐ ┌────────────────────────┐ │ │ │
│ │ │ │Categories│ │ Items │ │ Proof Panel │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ ● SBOM │ │ CVE-2024-1234 │ │ ┌────────────────────┐ │ │ │ │
│ │ │ │ ● Reach │ │ lodash@4.17.20 │ │ │ Witness Path │ │ │ │ │
│ │ │ │ ● VEX │ │ +reachable │ │ │ main() → parse() │ │ │ │ │
│ │ │ │ ● Policy │ │ Priority: 0.85 │ │ │ → vuln_func() │ │ │ │ │
│ │ │ │ ● Unknwn │ │ │ │ └────────────────────┘ │ │ │ │
│ │ │ │ │ │ CVE-2024-5678 │ │ ┌────────────────────┐ │ │ │ │
│ │ │ │ │ │ requests@2.28 │ │ │ VEX Merge │ │ │ │ │
│ │ │ │ │ │ +KEV │ │ │ vendor: affected │ │ │ │ │
│ │ │ │ │ │ Priority: 0.95 │ │ │ distro: not_aff │ │ │ │ │
│ │ │ │ │ │ │ │ │ → Result: affected │ │ │ │ │
│ │ │ │ │ │ │ │ └────────────────────┘ │ │ │ │
│ │ │ └──────────┘ └────────────────┘ └────────────────────────┘ │ │ │
│ │ └───────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────────┐ │ │
│ │ │ ACTIONABLES PANEL │ │ │
│ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ What to do next: │ │ │ │
│ │ │ │ 1. [CRITICAL] Upgrade lodash → 4.17.21 │ │ │ │
│ │ │ │ 2. [HIGH] Add VEX statement for urllib3 (not affected) │ │ │ │
│ │ │ │ 3. [MEDIUM] Resolve unknown: missing SBOM for module A │ │ │ │
│ │ │ └─────────────────────────────────────────────────────────┘ │ │ │
│ │ └────────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Component Hierarchy
CompareViewComponent
├── BaselineSelectorComponent
│ └── BaselineRationaleComponent
├── TrustIndicatorsComponent
│ ├── DeterminismHashDisplay
│ ├── PolicyVersionDisplay
│ ├── FeedSnapshotDisplay
│ ├── SignatureStatusDisplay
│ └── PolicyDriftIndicator
├── DeltaSummaryStripComponent
├── ThreePaneLayoutComponent
│ ├── CategoriesPaneComponent
│ ├── ItemsPaneComponent
│ └── ProofPaneComponent
│ ├── WitnessPathComponent
│ ├── VexMergeExplanationComponent
│ └── EnvelopeHashesComponent
├── ActionablesPanelComponent
└── ExportActionsComponent
State Management
Signals-Based State
The compare view uses Angular signals for reactive state management:
// Core state
currentTarget = signal<CompareTarget | null>(null);
baselineTarget = signal<CompareTarget | null>(null);
delta = signal<DeltaVerdictResponse | null>(null);
// UI state
selectedCategory = signal<string | null>(null);
selectedItem = signal<DeltaItem | null>(null);
viewMode = signal<'side-by-side' | 'unified'>('side-by-side');
userRole = signal<'developer' | 'security' | 'audit'>('developer');
// Computed state
filteredItems = computed(() => {
const cat = this.selectedCategory();
const items = this.delta()?.Items ?? [];
return cat ? items.filter(i => i.category === cat) : items;
});
deltaSummary = computed(() => this.delta()?.Summary);
trustIndicators = computed(() => this.delta()?.TrustIndicators);
Data Flow
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Route │───►│ Component │───►│ Service │
│ Params │ │ Init │ │ Calls │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Signals │◄───│ Backend │
│ Update │ │ Response │
└─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ Computed │
│ Values │
└─────────────┘
│
▼
┌─────────────┐
│ Template │
│ Render │
└─────────────┘
API Integration
Backend Endpoints
| Endpoint | Purpose |
|---|---|
GET /api/v1/baselines/recommendations/{digest} | Get recommended baselines |
GET /api/v1/baselines/rationale/{base}/{head} | Get baseline selection rationale |
POST /api/v1/delta/compute | Compute delta (idempotent) |
GET /api/v1/delta/{deltaId} | Get delta results |
GET /api/v1/delta/{deltaId}/trust-indicators | Get trust indicators |
GET /api/v1/actionables/delta/{deltaId} | Get actionable recommendations |
GET /api/v1/evidence/delta/{deltaId}/items/{itemId} | Get item evidence |
GET /api/v1/evidence/delta/{deltaId}/witness-paths | Get witness paths |
GET /api/v1/evidence/delta/{deltaId}/vex-merge/{vulnId} | Get VEX merge explanation |
Service Layer
@Injectable({ providedIn: 'root' })
export class CompareService {
constructor(private http: HttpClient) {}
getRecommendedBaselines(digest: string): Observable<BaselineRecommendationsResponse> {
return this.http.get<BaselineRecommendationsResponse>(
`/api/v1/baselines/recommendations/${digest}`
);
}
computeDelta(request: DeltaComputeRequest): Observable<DeltaVerdictResponse> {
return this.http.post<DeltaVerdictResponse>('/api/v1/delta/compute', request);
}
getActionables(deltaId: string): Observable<ActionablesResponse> {
return this.http.get<ActionablesResponse>(`/api/v1/actionables/delta/${deltaId}`);
}
getItemEvidence(deltaId: string, itemId: string): Observable<DeltaItemEvidenceResponse> {
return this.http.get<DeltaItemEvidenceResponse>(
`/api/v1/evidence/delta/${deltaId}/items/${itemId}`
);
}
}
Routing
// app.routes.ts additions
{
path: 'releases/:releaseId',
children: [
{ path: '', redirectTo: 'detail', pathMatch: 'full' },
{ path: 'detail', component: ReleaseFlowComponent },
{
path: 'compare',
component: CompareViewComponent,
data: { requireBaseline: false }
},
{
path: 'compare/:baselineId',
component: CompareViewComponent,
data: { requireBaseline: true }
}
]
},
{
path: 'compare',
children: [
{
path: ':currentDigest',
component: CompareViewComponent
},
{
path: ':currentDigest/:baselineDigest',
component: CompareViewComponent
}
]
}
Role-Based Views
Default Tab by Role
| Role | Default Tab | Visible Features |
|---|---|---|
| Developer | Actionables | Actionables, Witness Paths, Upgrade Suggestions |
| Security | Claims | VEX Merge, Policy Reasoning, Claim Sources, Actionables |
| Audit | Attestations | Signatures, Replay, Evidence Pack, Envelope Hashes |
Implementation
const ROLE_DEFAULTS: Record<UserRole, RoleConfig> = {
developer: {
defaultTab: 'actionables',
showFeatures: ['actionables', 'witness-paths', 'upgrade-suggestions'],
expandedPanels: ['actionables']
},
security: {
defaultTab: 'claims',
showFeatures: ['vex-merge', 'policy-reasoning', 'claim-sources', 'actionables'],
expandedPanels: ['vex-merge', 'policy']
},
audit: {
defaultTab: 'attestations',
showFeatures: ['signatures', 'replay', 'evidence-pack', 'envelope-hashes'],
expandedPanels: ['trust-indicators', 'signatures']
}
};
Trust Indicators
Determinism Verification
The UI displays and enables verification of:
- Determinism Hash - SHA-256 of normalized delta output
- Policy Version/Hash - Active policy at scan time
- Feed Snapshot - Vulnerability feed timestamp and hash
- Signature Status - DSSE envelope verification result
Degraded Mode
When signature verification fails, the UI:
- Displays a prominent warning banner
- Disables “Approve” actions
- Shows detailed verification failure reason
- Provides replay command for local verification
Accessibility
Keyboard Navigation
Tab/Shift+Tab: Navigate between panesArrow Up/Down: Navigate items within paneEnter: Select item / expand detailEscape: Close expanded detailC: Copy replay command (when focused on trust indicators)
Screen Reader Support
- ARIA labels on all interactive elements
- Live regions for delta summary updates
- Semantic heading structure
Performance Considerations
Lazy Loading
- Evidence panel loads on-demand when item selected
- Witness paths collapse by default (expand on click)
- VEX merge details in expansion panel
Caching
- Delta computations cached by (base_hash, head_hash, policy_hash)
- Baseline recommendations cached per session
- Trust indicators cached with delta
Virtual Scrolling
For large deltas (> 100 items), the items pane uses virtual scrolling:
<cdk-virtual-scroll-viewport itemSize="56" class="items-viewport">
<mat-list-item *cdkVirtualFor="let item of filteredItems()">
<!-- item content -->
</mat-list-item>
</cdk-virtual-scroll-viewport>
Testing Strategy
Unit Tests
- Component behavior (selection, filtering, expansion)
- Computed signal derivations
- Role-based view switching
Integration Tests
- API service calls and response handling
- Navigation and routing
- State persistence across route changes
E2E Tests
- Full comparison workflow
- Baseline selection and rationale display
- Export functionality
- Role-based default verification
Related Documentation
- Unified Triage Specification - Consolidated triage experience requirements
- Triage Component Catalog - Angular component documentation
- Sprint: Smart-Diff Compare View - Historical implementation sprint
- Sprint: Triage Canvas - Unified triage canvas sprint
- Sprint: Risk Dashboard - Risk budget visualization sprint
