feat(policy): Start Epic 3900 - Exception Objects as Auditable Entities

Advisory Processing:
- Processed 7 unprocessed advisories and 12 moat documents
- Created advisory processing report with 3 new epic recommendations
- Identified Epic 3900 (Exception Objects) as highest priority

Sprint 3900.0001.0001 - 4/8 tasks completed:
- T1: ExceptionObject domain model with full governance fields
- T2: ExceptionEvent model for event-sourced audit trail
- T4: IExceptionRepository interface with CRUD and query methods
- T6: ExceptionEvaluator service with PURL pattern matching

New library: StellaOps.Policy.Exceptions
- Models: ExceptionObject, ExceptionScope, ExceptionEvent
- Enums: ExceptionStatus, ExceptionType, ExceptionReason
- Services: ExceptionEvaluator with scope matching and specificity
- Repository: IExceptionRepository with filter and history support

Remaining tasks: PostgreSQL schema, repository implementation, tests
This commit is contained in:
StellaOps Bot
2025-12-20 23:44:55 +02:00
parent ad193449a7
commit d55a353481
11 changed files with 1946 additions and 4 deletions

View File

@@ -0,0 +1,228 @@
# Advisory Processing Report — 2025-12-20
**Role**: Product Manager
**Date**: 2025-12-20
**Status**: ANALYZED
---
## Executive Summary
Reviewed **7 unprocessed advisories** and **12 moat documents** from `docs/product-advisories/unprocessed/`. After cross-referencing with existing sprints, archived advisories, and implemented code, identified **3 new epic-level initiatives** and **5 enhancement opportunities** for existing features.
---
## 1. Advisories Reviewed
| File | Date | Primary Topic | Status |
|------|------|---------------|--------|
| Reimagining ProofLinked UX in Security Workflows | 2025-12-16 | Narrative-First Triage UX | ALREADY PROCESSED |
| Reachability Drift Detection | 2025-12-17 | Call graph drift between versions | NEW - ACTIONABLE |
| Designing Explainable Triage and ProofLinked Evidence | 2025-12-18 | Evidence-linked approvals | OVERLAPS w/ 12/16 |
| Branch · UX patterns worth borrowing | 2025-12-20 | Competitor UX analysis | REFERENCE ONLY |
| Testing strategy | 2025-12-20 | E2E testing strategy | NEW - ACTIONABLE |
| Moat #1 (Security Delta) | 2025-12-19 | Delta Verdicts as governance | NEW - STRATEGIC |
| Moat - Exception management | 2025-12-20 | Auditable exceptions | NEW - ACTIONABLE |
| Moat - Signed Replayable Verdicts | 2025-12-20 | Verdict attestations | PARTIAL OVERLAP |
| Moat - Knowledge Snapshots | 2025-12-20 | Time-travel replay | NEW - ACTIONABLE |
| Moat - Risk Budgets | 2025-12-20 | Diff-aware release gates | PARTIAL OVERLAP |
---
## 2. Cross-Reference with Existing Work
### 2.1 Already Implemented (Do Not Duplicate)
| Topic | Existing Implementation | Location |
|-------|------------------------|----------|
| Proof Ledger | ProofLedgerViewComponent | Sprint 3500.0004.0002 T1 |
| Reachability Explain | ReachabilityExplainWidget | Sprint 3500.0004.0002 T3 |
| Score Comparison | ScoreComparisonComponent | Sprint 3500.0004.0002 T4 |
| Proof Replay | ProofReplayDashboard | Sprint 3500.0004.0002 T5 |
| Material Risk Changes | MaterialRiskChangeDetector | Scanner.SmartDiff.Detection |
| VEX Lattice Merge | Excititor module | src/Excititor |
| Unknowns Registry | UnknownsService | Sprint 3500.0002.0002 |
| Call Graph Extraction | DotNetCallGraphExtractor, JavaCallGraphExtractor | Sprint 3500.0003.x |
| Semantic Entrypoints | Sprint 0411 | EntryTrace module |
| Temporal/Mesh Analysis | Sprint 0412 | EntryTrace module |
| Binary Intelligence | Sprint 0414 | EntryTrace module |
| Risk Scoring | Sprint 0415 | EntryTrace module |
### 2.2 Gaps Identified (New Work Required)
| Gap | Advisory Source | Priority | Complexity |
|-----|----------------|----------|------------|
| **Reachability Drift Detection** | 17-Dec advisory | HIGH | HIGH |
| **Exception Objects (Auditable)** | Moat Exception mgmt | HIGH | MEDIUM |
| **Knowledge Snapshots + Time-Travel** | Moat Knowledge Snapshots | HIGH | HIGH |
| **Delta Verdict Attestations** | Moat #1 | MEDIUM | MEDIUM |
| **Offline E2E Test Suite** | Testing strategy | MEDIUM | MEDIUM |
| **Code Change Facts Table** | 17-Dec advisory | MEDIUM | LOW |
| **Path Viewer UI Enhancement** | 17-Dec advisory | LOW | LOW |
---
## 3. Recommended New Epics
### Epic 3800: Reachability Drift Detection
**Justification**: The 17-Dec advisory identifies that reachability can change between versions even when vulnerability count stays the same. This is a significant moat differentiator.
**What's Missing** (per advisory gap analysis):
- `scanner.code_changes` table for AST-level diff facts
- `scanner.call_graph_snapshots` for per-scan graph cache
- `DriftCauseExplainer` service to attribute causes to code changes
- Cross-scan function-level drift (state drift exists, function-level doesn't)
**Scope**:
- Sprint 3800.0001.0001: Schema + Code Changes Table
- Sprint 3800.0001.0002: Call Graph Snapshot Service
- Sprint 3800.0002.0001: Drift Cause Explainer
- Sprint 3800.0002.0002: UI Integration
**Estimated Duration**: 4 weeks
---
### Epic 3900: Exception Management as Auditable Objects
**Justification**: The moat advisory explicitly states "Exception Objects" should be first-class, governed decisions — not .ignore files or UI toggles. This is critical for enterprise customers.
**What's Missing**:
- `policy.exceptions` table with full governance fields
- Exception lifecycle (proposed → approved → active → expired → revoked)
- Scope constraints (artifact digest, purl, environment)
- Time-bounded expiry enforcement
- Approval workflow integration
- Signed exception attestations
**Scope**:
- Sprint 3900.0001.0001: Schema + Exception Object Model
- Sprint 3900.0001.0002: Exception API (CRUD + approval workflow)
- Sprint 3900.0002.0001: Policy Engine Integration
- Sprint 3900.0002.0002: UI + Audit Pack Export
**Estimated Duration**: 4 weeks
---
### Epic 4000: Knowledge Snapshots + Time-Travel Replay
**Justification**: Multiple advisories emphasize that replayability requires pinned knowledge state (vuln feeds, VEX, policies). Current replay works for scores but not for full "time-travel" to a past knowledge state.
**What's Missing**:
- Content-addressed knowledge snapshot bundles
- Snapshot manifest with feed digests + policy versions
- Time-travel replay API that loads historical snapshots
- Evidence that the same inputs produce the same verdict
**Scope**:
- Sprint 4000.0001.0001: Knowledge Snapshot Model + Storage
- Sprint 4000.0001.0002: Snapshot Creation Service
- Sprint 4000.0002.0001: Time-Travel Replay API
- Sprint 4000.0002.0002: Verification + Audit Integration
**Estimated Duration**: 4 weeks
---
## 4. Enhancement Opportunities (Existing Features)
### 4.1 Delta Verdict Attestations
**Current State**: Score proofs exist and are signed via DSSE. Material risk changes are detected.
**Enhancement**: Create a formal "Delta Verdict" attestation that wraps:
- Baseline snapshot digest
- Target snapshot digest
- Delta categories (SBOM/VEX/Reachability/Decision changes)
- Policy outcome with explanation
- Signed envelope
**Effort**: ~1 sprint (add to existing attestation infrastructure)
---
### 4.2 Offline E2E Test Suite
**Current State**: Integration tests exist (Sprint 3500.0004.0003). Air-gap tests are ad-hoc.
**Enhancement**: Formalize per the Testing Strategy advisory:
- Offline bundle spec (`bundle.json` with digests)
- No-egress CI jobs
- SBOM round-trip tests (Syft → cosign → Grype)
- Router backpressure chaos tests
**Effort**: ~1 sprint
---
### 4.3 VEX Conflict Studio UI
**Current State**: VEX merge happens in Excititor with lattice logic. No UI for conflict visualization.
**Enhancement**: Per UX advisory, add side-by-side VEX conflict view:
- Left: Vendor statement + provenance
- Right: Internal statement + provenance
- Middle: Merge result + rule that decided
- Evidence hooks checklist
**Effort**: ~1 sprint
---
## 5. Recommendations
### Immediate Actions (Next 2 Weeks)
1. **Create Sprint files for Epic 3800** (Reachability Drift) — highest impact moat
2. **Archive processed advisories** — move 16-Dec and 18-Dec to archive (already processed)
3. **Update moat.md** — sync key-features with new moat explanations
### Medium-Term (Next 4 Weeks)
4. **Create Sprint files for Epic 3900** (Exception Objects)
5. **Create Sprint files for Epic 4000** (Knowledge Snapshots)
6. **Add Delta Verdict attestation to existing proof infrastructure**
### Deferred (Roadmap)
7. Offline E2E test formalization
8. VEX Conflict Studio UI
9. Fleet-level blast radius visualization
---
## 6. Decision Required
**Question for Stakeholders**: Which epic should be prioritized first?
| Option | Epic | Business Value | Technical Risk |
|--------|------|----------------|----------------|
| A | 3800 Reachability Drift | HIGH (differentiator) | MEDIUM |
| B | 3900 Exception Objects | HIGH (enterprise) | LOW |
| C | 4000 Knowledge Snapshots | MEDIUM (audit) | HIGH |
**Recommendation**: Start with **Epic 3900 (Exception Objects)** due to lower risk and clear enterprise demand, then **Epic 3800 (Reachability Drift)** for moat differentiation.
---
## Appendix: Files to Archive
These advisories have been processed or are reference-only:
```
docs/product-advisories/unprocessed/16-Dec-2025 - Reimagining ProofLinked UX in Security Workflows.md
→ Already processed (Status: PROCESSED in file)
docs/product-advisories/unprocessed/18-Dec-2025 - Designing Explainable Triage and ProofLinked Evidence.md
→ Overlaps with 16-Dec, consolidate
docs/product-advisories/unprocessed/20-Dec-2025 - Branch · UX patterns worth borrowing from top scanners.md
→ Reference only, no actionable tasks
```
---
**Report Generated By**: StellaOps Agent (Product Manager Role)
**Next Step**: Await stakeholder decision on epic prioritization

View File

@@ -511,9 +511,9 @@ stella unknowns export --format csv --out unknowns.csv
| 3500.0003.0002 | DONE | 100% | | Java Reachability Implemented via SPRINT_3610_0001_0001 (Java Call Graph). JavaCallGraphExtractor with Spring Boot entrypoint detection complete. |
| 3500.0003.0003 | DONE | 100% | | Graph Attestations + Rekor RichGraphAttestationService complete. APIs (CallGraphEndpoints, ReachabilityEndpoints) complete. Rekor integration via Attestor module. Budget policy: docs/operations/rekor-policy.md |
| 3500.0004.0001 | DONE | 100% | | CLI verbs + offline bundles complete. 8/8 tasks done. ScoreReplayCommandGroup, ProofCommandGroup, ScanGraphCommandGroup, UnknownsCommandGroup. 183 CLI tests pass. |
| 3500.0004.0002 | TODO | 0% | | Wireframes complete |
| 3500.0004.0003 | TODO | 0% | | |
| 3500.0004.0004 | TODO | 0% | | |
| 3500.0004.0002 | DONE | 100% | | UI Components + Visualization 8/8 tasks done. ProofLedgerView, UnknownsQueue, ReachabilityExplain, ScoreComparison, ProofReplayDashboard, API services, accessibility utils. Completed 2025-12-20 |
| 3500.0004.0003 | DONE | 100% | | Integration Tests + Corpus 8/8 tasks done. 74 test methods, golden corpus (12 cases), CI gates, perf baselines |
| 3500.0004.0004 | DONE | 100% | | Documentation + Handoff 8/8 tasks done. 17 documents: runbooks (5), training (6), release notes, OpenAPI, handoff checklist |
---

View File

@@ -18,7 +18,7 @@
| **3500.0003.0002** | Reachability Java Integration | 2 weeks | DONE | Implemented via SPRINT_3610_0001_0001 (JavaCallGraphExtractor, Spring Boot) |
| **3500.0003.0003** | Graph Attestations + Rekor | 2 weeks | DONE | RichGraphAttestationService, Rekor via Attestor module, budget policy documented |
| **3500.0004.0001** | CLI Verbs + Offline Bundles | 2 weeks | DONE | `stella score`, `stella graph`, `stella unknowns`, offline kit, corpus — 8/8 tasks, 183 tests pass |
| **3500.0004.0002** | UI Components + Visualization | 2 weeks | IN PROGRESS | T6 DOING: API models done. T1-T5, T7-T8 TODO |
| **3500.0004.0002** | UI Components + Visualization | 2 weeks | DONE | All 8 components: Proof Ledger, Unknowns Queue, Reachability Explain, Score Comparison, Proof Replay, API Services, Accessibility, Tests |
| **3500.0004.0003** | Integration Tests + Corpus | 2 weeks | DONE | Golden corpus (12 cases), 6 test projects (74 test methods), CI gates, perf baselines |
| **3500.0004.0004** | Documentation + Handoff | 2 weeks | DONE | Runbooks (5), training (6 docs), release notes, OpenAPI, handoff checklist — 8/8 tasks |

View File

@@ -0,0 +1,302 @@
# Sprint 3900.0001.0001 · Exception Objects — Schema & Model
## Topic & Scope
- Implement auditable Exception Objects as first-class entities with full governance lifecycle.
- Create PostgreSQL schema for `policy.exceptions` table with attribution, scoping, and time-bounded expiry.
- Build C# domain model for exception management.
- **Working directory:** `src/Policy/__Libraries/StellaOps.Policy.Exceptions/` and `src/Policy/Migrations/`
## Dependencies & Concurrency
- **Upstream**: None (foundational sprint)
- **Downstream**: Sprint 3900.0001.0002 (Exception API) depends on this
- **Safe to parallelize with**: Unrelated epics
## Documentation Prerequisites
- `docs/product-advisories/unprocessed/moats/20-Dec-2025 - Moat Explanation - Exception management as auditable objects.md`
- `docs/modules/policy/architecture.md`
- `docs/db/SPECIFICATION.md`
---
## Tasks
### T1: Exception Object Domain Model
**Assignee**: Policy Team
**Story Points**: 5
**Status**: DONE
**Description**:
Create the core Exception Object domain model with all required fields per the moat advisory.
**Implementation Path**: `src/Policy/__Libraries/StellaOps.Policy.Exceptions/Models/`
**Acceptance Criteria**:
- [ ] `ExceptionObject` record with all required fields
- [ ] `ExceptionStatus` enum: Proposed, Approved, Active, Expired, Revoked
- [ ] `ExceptionType` enum: Vulnerability, Policy, Unknown, Component
- [ ] `ExceptionScope` record: artifact digest, purl pattern, environment constraints
- [ ] `ExceptionReason` enum: FalsePositive, AcceptedRisk, CompensatingControl, TestOnly, etc.
- [ ] Immutable history via event-sourced versioning
**Domain Model Spec**:
```csharp
public sealed record ExceptionObject
{
public required string ExceptionId { get; init; }
public required int Version { get; init; }
public required ExceptionStatus Status { get; init; }
public required ExceptionType Type { get; init; }
public required ExceptionScope Scope { get; init; }
public required string OwnerId { get; init; }
public required string RequesterId { get; init; }
public string? ApproverId { get; init; }
public required DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset? ApprovedAt { get; init; }
public required DateTimeOffset ExpiresAt { get; init; }
public required ExceptionReason ReasonCode { get; init; }
public required string Rationale { get; init; }
public ImmutableArray<string> EvidenceRefs { get; init; }
public ImmutableDictionary<string, string> Metadata { get; init; }
}
public sealed record ExceptionScope
{
public string? ArtifactDigest { get; init; } // sha256:...
public string? PurlPattern { get; init; } // pkg:npm/lodash@*
public string? VulnerabilityId { get; init; } // CVE-2024-XXXX
public string? PolicyRuleId { get; init; } // rule identifier
public ImmutableArray<string> Environments { get; init; } // prod, staging, dev
public string? TenantId { get; init; }
}
```
---
### T2: Exception Event Model
**Assignee**: Policy Team
**Story Points**: 3
**Status**: DONE
**Description**:
Create event-sourced history model for exception lifecycle tracking.
**Acceptance Criteria**:
- [ ] `ExceptionEvent` record for all state transitions
- [ ] `ExceptionEventType` enum: Created, Approved, Activated, Extended, Revoked, Expired
- [ ] Event includes actor, timestamp, and previous state
- [ ] Audit trail is immutable (append-only)
---
### T3: PostgreSQL Schema Migration
**Assignee**: Policy Team
**Story Points**: 5
**Status**: TODO
**Description**:
Create database migration for exception storage.
**Implementation Path**: `src/Policy/Migrations/`
**Acceptance Criteria**:
- [ ] `policy.exceptions` table with all fields
- [ ] `policy.exception_events` table for audit trail
- [ ] Indexes on: exception_id, status, expires_at, scope fields
- [ ] Foreign keys to tenant (if applicable)
- [ ] BRIN index on created_at for time-based queries
**Schema Spec**:
```sql
CREATE TABLE policy.exceptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
exception_id TEXT NOT NULL UNIQUE,
version INTEGER NOT NULL DEFAULT 1,
status TEXT NOT NULL CHECK (status IN ('proposed', 'approved', 'active', 'expired', 'revoked')),
type TEXT NOT NULL CHECK (type IN ('vulnerability', 'policy', 'unknown', 'component')),
-- Scope
artifact_digest TEXT,
purl_pattern TEXT,
vulnerability_id TEXT,
policy_rule_id TEXT,
environments TEXT[] DEFAULT '{}',
tenant_id UUID,
-- Attribution
owner_id TEXT NOT NULL,
requester_id TEXT NOT NULL,
approver_id TEXT,
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
approved_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ NOT NULL,
-- Reason
reason_code TEXT NOT NULL,
rationale TEXT NOT NULL,
evidence_refs JSONB DEFAULT '[]',
metadata JSONB DEFAULT '{}',
CONSTRAINT valid_scope CHECK (
artifact_digest IS NOT NULL OR
purl_pattern IS NOT NULL OR
vulnerability_id IS NOT NULL OR
policy_rule_id IS NOT NULL
)
);
CREATE TABLE policy.exception_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
exception_id TEXT NOT NULL REFERENCES policy.exceptions(exception_id),
event_type TEXT NOT NULL,
actor_id TEXT NOT NULL,
occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
previous_status TEXT,
new_status TEXT,
details JSONB DEFAULT '{}',
CONSTRAINT fk_exception FOREIGN KEY (exception_id)
REFERENCES policy.exceptions(exception_id) ON DELETE CASCADE
);
CREATE INDEX idx_exceptions_status ON policy.exceptions(status);
CREATE INDEX idx_exceptions_expires ON policy.exceptions(expires_at);
CREATE INDEX idx_exceptions_vuln ON policy.exceptions(vulnerability_id) WHERE vulnerability_id IS NOT NULL;
CREATE INDEX idx_exceptions_purl ON policy.exceptions(purl_pattern) WHERE purl_pattern IS NOT NULL;
CREATE INDEX idx_exception_events_exception ON policy.exception_events(exception_id);
CREATE INDEX idx_exception_events_time USING BRIN ON policy.exception_events(occurred_at);
```
---
### T4: Exception Repository Interface
**Assignee**: Policy Team
**Story Points**: 3
**Status**: DONE
**Description**:
Create repository interface for exception persistence.
**Acceptance Criteria**:
- [ ] `IExceptionRepository` interface
- [ ] Methods: Create, Update, GetById, GetByScope, GetActive, GetExpiring
- [ ] Support for optimistic concurrency via version
- [ ] Audit event recording on all mutations
---
### T5: PostgreSQL Repository Implementation
**Assignee**: Policy Team
**Story Points**: 5
**Status**: TODO
**Description**:
Implement PostgreSQL repository for exceptions.
**Implementation Path**: `src/Policy/__Libraries/StellaOps.Policy.Storage.Postgres/`
**Acceptance Criteria**:
- [ ] `PostgresExceptionRepository` implementation
- [ ] Uses Npgsql with Dapper or raw ADO.NET
- [ ] Transactional event recording
- [ ] Efficient scope matching queries
- [ ] Expiry check queries
---
### T6: Exception Evaluator Service
**Assignee**: Policy Team
**Story Points**: 5
**Status**: DONE
**Description**:
Create service that evaluates whether an exception applies to a given finding.
**Acceptance Criteria**:
- [ ] `IExceptionEvaluator` interface
- [ ] `ExceptionEvaluator` implementation
- [ ] Scope matching: digest exact match, purl pattern match, vuln ID match
- [ ] Status check: only Active exceptions apply
- [ ] Expiry check: auto-mark expired if past expires_at
- [ ] Environment matching
---
### T7: Unit Tests
**Assignee**: Policy Team
**Story Points**: 3
**Status**: TODO
**Description**:
Comprehensive unit tests for exception domain model and evaluator.
**Acceptance Criteria**:
- [ ] Model construction and validation tests
- [ ] Scope matching tests (positive and negative cases)
- [ ] Status transition tests
- [ ] Expiry boundary tests
- [ ] Event generation tests
---
### T8: Integration Tests
**Assignee**: Policy Team
**Story Points**: 3
**Status**: TODO
**Description**:
Integration tests for PostgreSQL repository.
**Acceptance Criteria**:
- [ ] Repository CRUD tests
- [ ] Concurrent update handling
- [ ] Event audit trail verification
- [ ] Scope query performance tests
---
## Delivery Tracker
| # | Task ID | Status | Dependency | Owners | Task Definition |
|---|---------|--------|------------|--------|-----------------|
| 1 | T1 | DONE | — | Policy Team | Exception Object Domain Model |
| 2 | T2 | DONE | T1 | Policy Team | Exception Event Model |
| 3 | T3 | TODO | T1, T2 | Policy Team | PostgreSQL Schema Migration |
| 4 | T4 | DONE | T1 | Policy Team | Exception Repository Interface |
| 5 | T5 | TODO | T3, T4 | Policy Team | PostgreSQL Repository Implementation |
| 6 | T6 | DONE | T1 | Policy Team | Exception Evaluator Service |
| 7 | T7 | TODO | T1-T6 | Policy Team | Unit Tests |
| 8 | T8 | TODO | T5 | Policy Team | Integration Tests |
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2025-12-20 | Sprint file created based on advisory processing report. | Agent |
| 2025-12-20 | T1, T2, T4, T6 completed: Domain models, event model, repository interface, evaluator service. | Agent |
---
## Decisions & Risks
| Item | Type | Owner | Notes |
|------|------|-------|-------|
| Event sourcing vs CRUD | Decision | Policy Team | Using event-sourced audit trail but CRUD for current state |
| Scope matching complexity | Risk | Policy Team | PURL pattern matching may need optimization for large exception sets |
| Expiry enforcement | Decision | Policy Team | Lazy expiry check on read + scheduled background job for proactive marking |
---
**Sprint Status**: IN PROGRESS (4/8 tasks done)

View File

@@ -0,0 +1,288 @@
# Sprint 3900.0001.0002 · Exception Objects — API & Workflow
## Topic & Scope
- Implement REST API for Exception Object lifecycle management.
- Create approval workflow with multi-party authorization support.
- Add OpenAPI specification and client generation.
- **Working directory:** `src/Policy/StellaOps.Policy.WebService/` and `src/Api/`
## Dependencies & Concurrency
- **Upstream**: Sprint 3900.0001.0001 (Schema & Model) — MUST BE DONE
- **Downstream**: Sprint 3900.0002.0001 (Policy Engine Integration)
- **Safe to parallelize with**: Unrelated epics
## Documentation Prerequisites
- Sprint 3900.0001.0001 completion
- `docs/api/` for API conventions
- `docs/modules/policy/architecture.md`
---
## Tasks
### T1: Exception API Controller
**Assignee**: Policy Team
**Story Points**: 5
**Status**: TODO
**Description**:
Create REST API controller for exception CRUD operations.
**Implementation Path**: `src/Policy/StellaOps.Policy.WebService/Controllers/ExceptionsController.cs`
**Acceptance Criteria**:
- [ ] `POST /api/v1/policy/exceptions` — Create exception (returns Proposed status)
- [ ] `GET /api/v1/policy/exceptions/{id}` — Get exception by ID
- [ ] `GET /api/v1/policy/exceptions` — List exceptions with filters
- [ ] `PUT /api/v1/policy/exceptions/{id}` — Update exception (rationale, metadata)
- [ ] `DELETE /api/v1/policy/exceptions/{id}` — Revoke exception
- [ ] `POST /api/v1/policy/exceptions/{id}/approve` — Approve exception
- [ ] `POST /api/v1/policy/exceptions/{id}/activate` — Activate approved exception
- [ ] `POST /api/v1/policy/exceptions/{id}/extend` — Extend expiry
- [ ] All endpoints require authentication
- [ ] All mutations record events
**API Spec**:
```yaml
paths:
/api/v1/policy/exceptions:
post:
summary: Create a new exception
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateExceptionRequest'
responses:
201:
description: Exception created
content:
application/json:
schema:
$ref: '#/components/schemas/ExceptionObject'
get:
summary: List exceptions
parameters:
- name: status
in: query
schema:
type: string
enum: [proposed, approved, active, expired, revoked]
- name: type
in: query
schema:
type: string
- name: vulnerabilityId
in: query
schema:
type: string
- name: environment
in: query
schema:
type: string
```
---
### T2: Exception Service Layer
**Assignee**: Policy Team
**Story Points**: 5
**Status**: TODO
**Description**:
Create service layer with business logic for exception lifecycle.
**Acceptance Criteria**:
- [ ] `IExceptionService` interface
- [ ] `ExceptionService` implementation
- [ ] Validation: scope must be specific enough
- [ ] Validation: expiry must be in future, max 1 year
- [ ] Validation: rationale required, min 50 characters
- [ ] Status transitions follow state machine
- [ ] Notifications on status changes (event bus)
---
### T3: Approval Workflow
**Assignee**: Policy Team
**Story Points**: 5
**Status**: TODO
**Description**:
Implement approval workflow with configurable requirements.
**Acceptance Criteria**:
- [ ] `ApprovalPolicy` configuration per environment
- [ ] Dev: auto-approve or single approver
- [ ] Staging: single approver required
- [ ] Prod: two approvers required (configurable)
- [ ] Approver cannot be requester
- [ ] Approval deadline with auto-reject
- [ ] Approval notification integration
**Approval Policy Model**:
```csharp
public sealed record ApprovalPolicy
{
public required string Environment { get; init; }
public required int RequiredApprovers { get; init; }
public required bool RequesterCanApprove { get; init; }
public required TimeSpan ApprovalDeadline { get; init; }
public ImmutableArray<string> AllowedApproverRoles { get; init; }
}
```
---
### T4: Exception Query Service
**Assignee**: Policy Team
**Story Points**: 3
**Status**: TODO
**Description**:
Create optimized query service for exception lookup.
**Acceptance Criteria**:
- [ ] `IExceptionQueryService` interface
- [ ] `GetApplicableExceptions(finding)` — returns matching active exceptions
- [ ] `GetExpiringExceptions(horizon)` — returns exceptions expiring within horizon
- [ ] `GetExceptionsByScope(scope)` — returns exceptions for specific scope
- [ ] Caching layer for hot paths
- [ ] Efficient PURL pattern matching
---
### T5: Exception DTO Models
**Assignee**: Policy Team
**Story Points**: 2
**Status**: TODO
**Description**:
Create DTOs for API requests/responses.
**Acceptance Criteria**:
- [ ] `CreateExceptionRequest` DTO
- [ ] `UpdateExceptionRequest` DTO
- [ ] `ApproveExceptionRequest` DTO
- [ ] `ExtendExceptionRequest` DTO
- [ ] `ExceptionResponse` DTO
- [ ] `ExceptionListResponse` DTO with pagination
- [ ] Validation attributes
---
### T6: OpenAPI Specification
**Assignee**: Policy Team
**Story Points**: 2
**Status**: TODO
**Description**:
Add exception endpoints to OpenAPI spec.
**Implementation Path**: `src/Api/StellaOps.Api.OpenApi/policy/exceptions.yaml`
**Acceptance Criteria**:
- [ ] All endpoints documented
- [ ] Request/response schemas defined
- [ ] Error responses documented
- [ ] Examples included
- [ ] Generated client compiles
---
### T7: Expiry Background Job
**Assignee**: Policy Team
**Story Points**: 3
**Status**: TODO
**Description**:
Create background job to mark expired exceptions.
**Acceptance Criteria**:
- [ ] Scheduled job runs every hour
- [ ] Finds all Active exceptions with expires_at < now
- [ ] Transitions to Expired status
- [ ] Records expiry event
- [ ] Sends expiry notifications
- [ ] Uses Scheduler.JobClient abstraction
---
### T8: Unit Tests
**Assignee**: Policy Team
**Story Points**: 3
**Status**: TODO
**Description**:
Unit tests for service layer and workflow.
**Acceptance Criteria**:
- [ ] Service method tests
- [ ] Approval workflow tests
- [ ] State transition tests
- [ ] Validation tests
- [ ] Query service tests
---
### T9: Integration Tests
**Assignee**: Policy Team
**Story Points**: 3
**Status**: TODO
**Description**:
API integration tests.
**Acceptance Criteria**:
- [ ] Full lifecycle API test
- [ ] Approval workflow integration test
- [ ] Concurrent modification handling
- [ ] Authorization tests
- [ ] Error handling tests
---
## Delivery Tracker
| # | Task ID | Status | Dependency | Owners | Task Definition |
|---|---------|--------|------------|--------|-----------------|
| 1 | T1 | TODO | Sprint 3900.0001.0001 | Policy Team | Exception API Controller |
| 2 | T2 | TODO | Sprint 3900.0001.0001 | Policy Team | Exception Service Layer |
| 3 | T3 | TODO | T2 | Policy Team | Approval Workflow |
| 4 | T4 | TODO | Sprint 3900.0001.0001 | Policy Team | Exception Query Service |
| 5 | T5 | TODO | | Policy Team | Exception DTO Models |
| 6 | T6 | TODO | T1, T5 | Policy Team | OpenAPI Specification |
| 7 | T7 | TODO | T2 | Policy Team | Expiry Background Job |
| 8 | T8 | TODO | T1-T7 | Policy Team | Unit Tests |
| 9 | T9 | TODO | T1-T7 | Policy Team | Integration Tests |
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2025-12-20 | Sprint file created. Depends on Sprint 3900.0001.0001. | Agent |
---
## Decisions & Risks
| Item | Type | Owner | Notes |
|------|------|-------|-------|
| Multi-approver workflow | Decision | Policy Team | Configurable per environment; start with simple approval |
| Caching strategy | Risk | Policy Team | May need Valkey for cross-instance consistency |
| Notification integration | Decision | Policy Team | Use existing Notify module event bus |
---
**Sprint Status**: TODO (0/9 tasks)