fix(scanner): Fix WebService test infrastructure failure

- Update PostgresIdempotencyKeyRepository to use ScannerDataSource instead
  of NpgsqlDataSource directly (aligns with other Postgres repositories)
- Move IIdempotencyKeyRepository registration from IdempotencyMiddlewareExtensions
  to ServiceCollectionExtensions.RegisterScannerStorageServices
- Use Dapper instead of raw NpgsqlCommand for consistency
- Fixes: System.InvalidOperationException: Unable to resolve service for type
  'Npgsql.NpgsqlDataSource' when running WebService tests

Sprint planning:
- Create SPRINT_3500_0004_0001 CLI Verbs & Offline Bundles
- Create SPRINT_3500_0004_0002 UI Components & Visualization
- Create SPRINT_3500_0004_0003 Integration Tests & Corpus
- Create SPRINT_3500_0004_0004 Documentation & Handoff

Sprint: SPRINT_3500_0002_0003
This commit is contained in:
StellaOps Bot
2025-12-20 18:40:34 +02:00
parent 3698ebf4a8
commit 3c6e14fca5
8 changed files with 1111 additions and 71 deletions

View File

@@ -0,0 +1,239 @@
# SPRINT_3500_0004_0001: CLI Verbs + Offline Bundles
**Epic**: Deeper Moat Beyond Reachability
**Sprint**: 1 of 4 (CLI & UI phase)
**Duration**: 2 weeks
**Working Directory**: `src/Cli/StellaOps.Cli/`
**Owner**: CLI Team
---
## Sprint Goal
Implement CLI verbs for score proofs, reachability, and unknowns management:
1. `stella score replay --scan <id>` — Replay a score computation
2. `stella scan graph --lang dotnet|java --sln <path>` — Extract call graph
3. `stella unknowns list --band HOT` — List unknowns by band
4. Complete `stella proof verify --bundle <path>` implementation
5. Offline bundle extensions for reachability
**Success Criteria**:
- [ ] All CLI verbs implemented and functional
- [ ] JSON and text output formats supported
- [ ] Offline mode works without backend connectivity
- [ ] Unit tests achieve ≥85% coverage
- [ ] CLI help text is comprehensive
---
## Dependencies & Concurrency
- **Upstream**: SPRINT_3500_0002_0003 (Proof Replay API) — DONE
- **Upstream**: SPRINT_3500_0003_0003 (Graph Attestations) — DONE
- **Parallel with**: SPRINT_3500_0004_0002 (UI Components)
---
## Documentation Prerequisites
- `docs/09_API_CLI_REFERENCE.md` — CLI documentation
- `docs/24_OFFLINE_KIT.md` — Offline bundle format
- `src/Cli/StellaOps.Cli/AGENTS.md` — CLI working agreements
---
## Existing Infrastructure
The CLI already has:
- `stella proof verify` — Stub implementation in `ProofCommandGroup.cs`
- `stella reachability explain` — Full implementation in `CommandFactory.cs`
- `stella graph verify` — Graph DSSE verification
- Backend client infrastructure in `BackendOperationsClient.cs`
**Gaps to fill**:
1. `stella score replay` — New command
2. `stella scan graph` — New command
3. `stella unknowns list` — New command
4. Complete `proof verify` implementation
---
## Tasks
### T1: Score Replay Command
**Assignee**: CLI Engineer
**Story Points**: 3
**Status**: TODO
**Description**:
Add `stella score replay --scan <id>` command to replay score computation.
**Acceptance Criteria**:
- [ ] Accepts scan ID as argument
- [ ] Calls `/api/v1/scanner/scans/{id}/score/replay` endpoint
- [ ] Returns proof bundle with root hash
- [ ] Supports `--output json|text` format
- [ ] Shows verification status
**Implementation**:
- Add to `CommandFactory.cs` under score command group
- Use `BackendOperationsClient` for API calls
---
### T2: Scan Graph Command
**Assignee**: CLI Engineer
**Story Points**: 5
**Status**: TODO
**Description**:
Add `stella scan graph` command to extract call graphs locally.
**Acceptance Criteria**:
- [ ] `--lang dotnet|java|node|python|go` language selection
- [ ] `--sln <path>` or `--target <path>` for source path
- [ ] `--output <file>` for call graph output
- [ ] `--upload` flag to submit to backend
- [ ] Deterministic output (stable ordering)
**Implementation**:
- Invoke language-specific extractors from `StellaOps.Scanner.CallGraph`
- Support offline extraction without backend
---
### T3: Unknowns List Command
**Assignee**: CLI Engineer
**Story Points**: 3
**Status**: TODO
**Description**:
Add `stella unknowns list` command to list unknowns by band.
**Acceptance Criteria**:
- [ ] `--band HOT|WARM|COLD` filter
- [ ] `--limit N` pagination
- [ ] `--format json|table` output
- [ ] Shows CVE, package, band, age
**Implementation**:
- Call `/api/v1/policy/unknowns` endpoint
- Add `UnknownsCommandGroup.cs`
---
### T4: Complete Proof Verify
**Assignee**: CLI Engineer
**Story Points**: 5
**Status**: TODO
**Description**:
Complete the `stella proof verify --bundle <path>` implementation.
**Acceptance Criteria**:
- [ ] Verifies DSSE envelope signature
- [ ] Validates Merkle proof
- [ ] Checks Rekor inclusion (unless `--offline`)
- [ ] Returns detailed verification result
- [ ] Supports `--output json|text`
**Implementation**:
- Wire up `IVerificationPipeline` from Attestor module
- Add Rekor client for inclusion proof
---
### T5: Offline Bundle Extensions
**Assignee**: CLI Engineer
**Story Points**: 3
**Status**: TODO
**Description**:
Extend offline bundle format for reachability data.
**Acceptance Criteria**:
- [ ] `/offline/reachability/` directory structure
- [ ] `/offline/corpus/` for ground-truth data
- [ ] Bundle includes call graphs and proofs
- [ ] Verification works offline
**Implementation**:
- Extend `OfflineKitImportService`
- Add reachability bundle builder
---
### T6: Unit Tests
**Assignee**: CLI Engineer
**Story Points**: 3
**Status**: TODO
**Description**:
Comprehensive unit tests for new CLI commands.
**Acceptance Criteria**:
- [ ] Score replay command tests
- [ ] Scan graph command tests
- [ ] Unknowns list command tests
- [ ] Proof verify tests
- [ ] ≥85% code coverage
---
### T7: Documentation Updates
**Assignee**: CLI Engineer
**Story Points**: 2
**Status**: TODO
**Description**:
Update CLI documentation with new commands.
**Acceptance Criteria**:
- [ ] `docs/09_API_CLI_REFERENCE.md` updated
- [ ] Help text for all new commands
- [ ] Examples in documentation
- [ ] Offline usage documented
---
## Delivery Tracker
| # | Task ID | Status | Dependency | Owners | Task Definition |
|---|---------|--------|------------|--------|-----------------|
| 1 | T1 | TODO | — | CLI Team | Score Replay Command |
| 2 | T2 | TODO | — | CLI Team | Scan Graph Command |
| 3 | T3 | TODO | — | CLI Team | Unknowns List Command |
| 4 | T4 | TODO | — | CLI Team | Complete Proof Verify |
| 5 | T5 | TODO | T1, T4 | CLI Team | Offline Bundle Extensions |
| 6 | T6 | TODO | T1-T4 | CLI Team | Unit Tests |
| 7 | T7 | TODO | T1-T5 | CLI Team | Documentation Updates |
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2025-12-20 | Sprint file created. Analyzed existing CLI commands; identified gaps. Ready to implement. | Agent |
---
## Decisions & Risks
| Item | Type | Owner | Notes |
|------|------|-------|-------|
| Use existing BackendOperationsClient | Decision | CLI Team | Consistent API access pattern |
| Offline-first for scan graph | Decision | CLI Team | Local extraction before upload |
| JSON as default for piping | Decision | CLI Team | Machine-readable output |
---
**Sprint Status**: IN PROGRESS (0/7 tasks done)

View File

@@ -0,0 +1,200 @@
# Sprint 3500.0004.0001 · CLI Verbs + Offline Bundles
## Topic & Scope
- Implement CLI commands for score replay, proof verification, call graph analysis, reachability explanation, and unknowns management.
- Extend offline kit with reachability graphs and test corpus bundles.
- **Working directory:** `src/Cli/StellaOps.Cli.Plugins.Scanner/` and `offline-kit/`.
## Dependencies & Concurrency
- Upstream: Sprint 3500.0002.0003 (Proof Replay + API) — DONE
- Upstream: Sprint 3500.0003.0003 (Graph Attestations + Rekor) — DONE
- Safe to parallelize with: Sprint 3500.0004.0002 (UI Components)
## Documentation Prerequisites
- `docs/modules/cli/architecture.md`
- `docs/10_CONCELIER_CLI_QUICKSTART.md`
- `docs/api/scanner-score-proofs-api.md`
- `src/Cli/AGENTS.md`
---
## Tasks
### T1: Score Replay Command
**Assignee**: CLI Team
**Story Points**: 3
**Status**: TODO
**Description**:
Implement `stella score replay --scan <id>` command to replay score computation.
**Acceptance Criteria**:
- [ ] `stella score replay --scan <scan-id>` triggers score replay
- [ ] `--output <format>` supports `json`, `table`, `yaml`
- [ ] `--verbose` shows detailed computation steps
- [ ] Returns exit code 0 on success, non-zero on failure
- [ ] Handles offline mode gracefully
---
### T2: Proof Verification Command
**Assignee**: CLI Team
**Story Points**: 3
**Status**: TODO
**Description**:
Implement `stella proof verify --bundle <path>` command to verify proof bundles.
**Acceptance Criteria**:
- [ ] `stella proof verify --bundle <path>` verifies a proof bundle file
- [ ] `--scan <id>` fetches bundle from API then verifies
- [ ] Displays Merkle tree verification result
- [ ] Shows DSSE signature validation status
- [ ] Optionally checks Rekor transparency log
---
### T3: Call Graph Command
**Assignee**: CLI Team
**Story Points**: 5
**Status**: TODO
**Description**:
Implement `stella scan graph --lang <dotnet|java> --path <sln|jar>` for call graph extraction.
**Acceptance Criteria**:
- [ ] `stella scan graph --lang dotnet --path <sln>` extracts .NET call graph
- [ ] `stella scan graph --lang java --path <jar>` extracts Java call graph
- [ ] `--output <path>` saves CallGraph.v1.json
- [ ] `--entrypoints` lists discovered entrypoints
- [ ] Progress indicator for large codebases
---
### T4: Reachability Explain Command
**Assignee**: CLI Team
**Story Points**: 5
**Status**: TODO
**Description**:
Implement `stella reachability explain --scan <id> --cve <cve>` for CVE reachability explanation.
**Acceptance Criteria**:
- [ ] Shows path from entrypoint to vulnerable function
- [ ] Displays confidence score and factors
- [ ] `--format graph` renders ASCII call chain
- [ ] `--verbose` shows all intermediate nodes
- [ ] Returns actionable remediation suggestions
---
### T5: Unknowns List Command
**Assignee**: CLI Team
**Story Points**: 2
**Status**: TODO
**Description**:
Implement `stella unknowns list --band <HOT|WARM|COLD>` for unknowns management.
**Acceptance Criteria**:
- [ ] Lists unknowns filtered by band
- [ ] `--scan <id>` filters to specific scan
- [ ] `--sort <field>` supports sorting by age, rank, count
- [ ] `--limit <n>` limits output
- [ ] Shows band transitions
---
### T6: Offline Reachability Bundle
**Assignee**: CLI Team
**Story Points**: 3
**Status**: TODO
**Description**:
Extend offline kit to include reachability graph bundles.
**Acceptance Criteria**:
- [ ] `/offline/reachability/` directory structure defined
- [ ] Call graphs exportable to offline format
- [ ] Entrypoint mappings included in bundle
- [ ] Reachability computation works fully offline
- [ ] Bundle size optimization (deduplicated nodes)
---
### T7: Offline Corpus Bundle
**Assignee**: CLI Team
**Story Points**: 3
**Status**: TODO
**Description**:
Create test corpus bundles for offline verification.
**Acceptance Criteria**:
- [ ] `/offline/corpus/` contains golden test cases
- [ ] Corpus covers all scoring scenarios
- [ ] SBOM + manifest + proof bundles for each case
- [ ] `stella test corpus --offline` validates corpus
- [ ] Corpus versioned with kit
---
### T8: Unit Tests
**Assignee**: CLI Team
**Story Points**: 3
**Status**: TODO
**Description**:
Comprehensive unit tests for all CLI commands.
**Acceptance Criteria**:
- [ ] ≥85% code coverage for new commands
- [ ] Mock API responses for all endpoints
- [ ] Offline mode tests
- [ ] Error handling tests
- [ ] Exit code verification
---
## Delivery Tracker
| # | Task ID | Status | Dependency | Owners | Task Definition |
|---|---------|--------|------------|--------|-----------------|
| 1 | T1 | TODO | — | CLI Team | Score Replay Command |
| 2 | T2 | TODO | — | CLI Team | Proof Verification Command |
| 3 | T3 | TODO | — | CLI Team | Call Graph Command |
| 4 | T4 | TODO | T3 | CLI Team | Reachability Explain Command |
| 5 | T5 | TODO | — | CLI Team | Unknowns List Command |
| 6 | T6 | TODO | T3, T4 | CLI Team | Offline Reachability Bundle |
| 7 | T7 | TODO | T1, T2 | CLI Team | Offline Corpus Bundle |
| 8 | T8 | TODO | T1-T7 | CLI Team | Unit Tests |
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2025-12-20 | Sprint file created. Ready for implementation. | Agent |
---
## Decisions & Risks
| Item | Type | Owner | Notes |
|------|------|-------|-------|
| CLI framework | Decision | CLI Team | Use existing System.CommandLine infrastructure |
| Offline bundle format | Decision | CLI Team | JSON with optional compression |
| Corpus versioning | Risk | CLI Team | Must stay in sync with scoring algorithm versions |
---
**Sprint Status**: TODO (0/8 tasks done)

View File

@@ -0,0 +1,202 @@
# Sprint 3500.0004.0002 · UI Components + Visualization
## Topic & Scope
- Implement Angular UI components for proof ledger visualization, unknowns queue management, and reachability explanation widgets.
- **Working directory:** `src/Web/StellaOps.Web/src/app/` (Angular v17).
## Dependencies & Concurrency
- Upstream: Sprint 3500.0002.0003 (Proof Replay + API) — DONE
- Upstream: Sprint 3500.0003.0003 (Graph Attestations + Rekor) — DONE
- Safe to parallelize with: Sprint 3500.0004.0001 (CLI Verbs)
## Documentation Prerequisites
- `docs/modules/ui/architecture.md`
- `docs/15_UI_GUIDE.md`
- `docs/accessibility.md`
- `src/Web/AGENTS.md`
---
## Tasks
### T1: Proof Ledger View Component
**Assignee**: UI Team
**Story Points**: 5
**Status**: TODO
**Description**:
Create ProofLedgerViewComponent to display scan proof history with Merkle tree visualization.
**Acceptance Criteria**:
- [ ] Displays scan manifest with all input hashes
- [ ] Shows Merkle tree structure (expandable)
- [ ] DSSE signature validation indicator
- [ ] Rekor transparency log link (if available)
- [ ] Download proof bundle button
- [ ] Responsive design (mobile-friendly)
---
### T2: Unknowns Queue Component
**Assignee**: UI Team
**Story Points**: 5
**Status**: TODO
**Description**:
Create UnknownsQueueComponent to manage unknown packages with band-based prioritization.
**Acceptance Criteria**:
- [ ] Tabbed view: HOT / WARM / COLD bands
- [ ] Sort by rank, age, occurrence count
- [ ] Escalate/Resolve action buttons
- [ ] Batch selection and bulk actions
- [ ] Filter by scan, image, package type
- [ ] Real-time updates via SignalR
---
### T3: Reachability Explain Widget
**Assignee**: UI Team
**Story Points**: 8
**Status**: TODO
**Description**:
Create ReachabilityExplainWidget to visualize CVE reachability paths.
**Acceptance Criteria**:
- [ ] Interactive call graph visualization (D3.js or similar)
- [ ] Path highlighting from entrypoint to vulnerable function
- [ ] Confidence score display with factor breakdown
- [ ] Zoom/pan controls
- [ ] Node details on hover/click
- [ ] Export to PNG/SVG
---
### T4: Score Comparison View
**Assignee**: UI Team
**Story Points**: 3
**Status**: TODO
**Description**:
Create ScoreComparisonViewComponent to diff scores between scan versions.
**Acceptance Criteria**:
- [ ] Side-by-side score comparison
- [ ] Highlight score changes (delta)
- [ ] Show which findings changed
- [ ] VEX status impact visualization
- [ ] Time-series chart option
---
### T5: Proof Replay Dashboard
**Assignee**: UI Team
**Story Points**: 5
**Status**: TODO
**Description**:
Create ProofReplayDashboardComponent for score replay operations.
**Acceptance Criteria**:
- [ ] Trigger replay from UI
- [ ] Progress indicator during replay
- [ ] Show original vs replayed score comparison
- [ ] Display any drift/discrepancies
- [ ] Export replay report
---
### T6: API Integration Service
**Assignee**: UI Team
**Story Points**: 3
**Status**: TODO
**Description**:
Create Angular services to integrate with new Scanner API endpoints.
**Acceptance Criteria**:
- [ ] ManifestService for `/scans/{id}/manifest`
- [ ] ProofBundleService for `/scans/{id}/proofs`
- [ ] UnknownsService for `/unknowns/*`
- [ ] ReachabilityService for reachability endpoints
- [ ] Error handling and retry logic
---
### T7: Accessibility Compliance
**Assignee**: UI Team
**Story Points**: 3
**Status**: TODO
**Description**:
Ensure all new components meet WCAG 2.1 AA accessibility standards.
**Acceptance Criteria**:
- [ ] Keyboard navigation for all interactive elements
- [ ] Screen reader compatibility (ARIA labels)
- [ ] Color contrast compliance
- [ ] Focus management
- [ ] Accessibility audit passing
---
### T8: Component Tests
**Assignee**: UI Team
**Story Points**: 3
**Status**: TODO
**Description**:
Comprehensive tests for all UI components using Angular testing utilities.
**Acceptance Criteria**:
- [ ] Unit tests for all components
- [ ] Integration tests with mock API
- [ ] Snapshot tests for visual regression
- [ ] E2E tests with Playwright
- [ ] ≥80% code coverage
---
## Delivery Tracker
| # | Task ID | Status | Dependency | Owners | Task Definition |
|---|---------|--------|------------|--------|-----------------|
| 1 | T1 | TODO | — | UI Team | Proof Ledger View Component |
| 2 | T2 | TODO | — | UI Team | Unknowns Queue Component |
| 3 | T3 | TODO | — | UI Team | Reachability Explain Widget |
| 4 | T4 | TODO | T1 | UI Team | Score Comparison View |
| 5 | T5 | TODO | T1, T6 | UI Team | Proof Replay Dashboard |
| 6 | T6 | TODO | — | UI Team | API Integration Service |
| 7 | T7 | TODO | T1-T5 | UI Team | Accessibility Compliance |
| 8 | T8 | TODO | T1-T7 | UI Team | Component Tests |
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2025-12-20 | Sprint file created. UX wireframes available (per master sprint tracker). | Agent |
---
## Decisions & Risks
| Item | Type | Owner | Notes |
|------|------|-------|-------|
| Graph library | Decision | UI Team | Evaluate D3.js vs Cytoscape.js for call graph |
| Real-time updates | Decision | UI Team | SignalR for unknowns queue notifications |
| Large graph rendering | Risk | UI Team | May need virtualization for 10k+ node graphs |
---
**Sprint Status**: TODO (0/8 tasks done)

View File

@@ -0,0 +1,205 @@
# Sprint 3500.0004.0003 · Integration Tests + Corpus
## Topic & Scope
- Create comprehensive integration tests covering full proof-chain and reachability workflows.
- Build golden test corpus with known-good SBOM/manifest/proof bundles.
- Add CI gates to prevent regressions.
- **Working directory:** `tests/` and `bench/`.
## Dependencies & Concurrency
- Upstream: Sprint 3500.0004.0001 (CLI Verbs) — for corpus validation commands
- Upstream: Sprint 3500.0004.0002 (UI Components) — for E2E coverage
- Requires: All Epic A and Epic B sprints DONE
## Documentation Prerequisites
- `docs/19_TEST_SUITE_OVERVIEW.md`
- `docs/modules/ci/architecture.md`
- `bench/README.md`
- `tests/AGENTS.md`
---
## Tasks
### T1: Proof Chain Integration Tests
**Assignee**: QA Team
**Story Points**: 5
**Status**: TODO
**Description**:
End-to-end tests for the complete proof chain: scan → manifest → score → proof bundle → verify.
**Acceptance Criteria**:
- [ ] Test scan submission creates manifest
- [ ] Test score computation produces deterministic results
- [ ] Test proof bundle generation and signing
- [ ] Test proof verification succeeds for valid bundles
- [ ] Test verification fails for tampered bundles
- [ ] Test replay produces identical scores
---
### T2: Reachability Integration Tests
**Assignee**: QA Team
**Story Points**: 5
**Status**: TODO
**Description**:
End-to-end tests for call graph extraction and reachability analysis.
**Acceptance Criteria**:
- [ ] Test .NET call graph extraction
- [ ] Test Java call graph extraction
- [ ] Test entrypoint discovery
- [ ] Test reachability computation
- [ ] Test reachability explanation output
- [ ] Test graph attestation signing
---
### T3: Unknowns Workflow Tests
**Assignee**: QA Team
**Story Points**: 3
**Status**: TODO
**Description**:
Integration tests for unknowns lifecycle: detection → ranking → escalation → resolution.
**Acceptance Criteria**:
- [ ] Test unknown detection during scan
- [ ] Test ranking determinism
- [ ] Test band assignment
- [ ] Test escalation triggers rescan
- [ ] Test resolution updates status
- [ ] Test band transitions
---
### T4: Golden Test Corpus
**Assignee**: QA Team
**Story Points**: 8
**Status**: TODO
**Description**:
Create golden test corpus with known-good artifacts for all scoring scenarios.
**Acceptance Criteria**:
- [ ] Corpus covers all CVE severity levels
- [ ] Corpus includes VEX overrides
- [ ] Corpus has reachability scenarios
- [ ] Corpus versioned with scoring algorithm
- [ ] Each case has: SBOM, manifest, proof bundle, expected score
- [ ] Corpus documented with scenario descriptions
---
### T5: Determinism Validation Suite
**Assignee**: QA Team
**Story Points**: 5
**Status**: TODO
**Description**:
Tests to validate scoring determinism across runs, platforms, and time.
**Acceptance Criteria**:
- [ ] Same input produces identical score hash
- [ ] Cross-platform determinism (Windows/Linux/macOS)
- [ ] Timestamp independence (frozen time tests)
- [ ] Parallel execution determinism
- [ ] Replay after code changes produces same result
---
### T6: CI Gate Configuration
**Assignee**: DevOps Team
**Story Points**: 3
**Status**: TODO
**Description**:
Configure CI to run integration tests and gate on failures.
**Acceptance Criteria**:
- [ ] Integration tests run on PR
- [ ] Corpus validation on release branch
- [ ] Determinism tests on nightly
- [ ] Test coverage reported to dashboard
- [ ] Flaky test quarantine process
---
### T7: Performance Baseline Tests
**Assignee**: QA Team
**Story Points**: 3
**Status**: TODO
**Description**:
Establish performance baselines for key operations.
**Acceptance Criteria**:
- [ ] Score computation time baseline
- [ ] Proof bundle generation baseline
- [ ] Call graph extraction baseline
- [ ] Reachability computation baseline
- [ ] Regression alerts on >20% degradation
---
### T8: Air-Gap Integration Tests
**Assignee**: QA Team
**Story Points**: 3
**Status**: TODO
**Description**:
Tests to verify full functionality in air-gapped environments.
**Acceptance Criteria**:
- [ ] Offline kit installation test
- [ ] Offline scan test
- [ ] Offline score replay test
- [ ] Offline proof verification test
- [ ] No network calls during offline operation
---
## Delivery Tracker
| # | Task ID | Status | Dependency | Owners | Task Definition |
|---|---------|--------|------------|--------|-----------------|
| 1 | T1 | TODO | — | QA Team | Proof Chain Integration Tests |
| 2 | T2 | TODO | — | QA Team | Reachability Integration Tests |
| 3 | T3 | TODO | — | QA Team | Unknowns Workflow Tests |
| 4 | T4 | TODO | T1, T2, T3 | QA Team | Golden Test Corpus |
| 5 | T5 | TODO | T1 | QA Team | Determinism Validation Suite |
| 6 | T6 | TODO | T1-T5 | DevOps Team | CI Gate Configuration |
| 7 | T7 | TODO | T1, T2 | QA Team | Performance Baseline Tests |
| 8 | T8 | TODO | T4 | QA Team | Air-Gap Integration Tests |
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2025-12-20 | Sprint file created. | Agent |
---
## Decisions & Risks
| Item | Type | Owner | Notes |
|------|------|-------|-------|
| Corpus storage | Decision | QA Team | Store in `bench/corpus/` with LFS for large files |
| Flaky test policy | Decision | DevOps Team | Quarantine after 2 consecutive failures |
| Performance thresholds | Risk | QA Team | Need production baselines before setting thresholds |
---
**Sprint Status**: TODO (0/8 tasks done)

View File

@@ -0,0 +1,204 @@
# Sprint 3500.0004.0004 · Documentation + Handoff
## Topic & Scope
- Complete all documentation for Score Proofs and Reachability features.
- Create runbooks, training materials, and API documentation.
- Prepare handoff materials for operations and support teams.
- **Working directory:** `docs/`.
## Dependencies & Concurrency
- Upstream: All 3500.0002.x, 3500.0003.x, 3500.0004.x sprints
- Final sprint in Epic 3500; cannot start until implementation is stable
## Documentation Prerequisites
- All existing `docs/` content
- API specifications from implementation sprints
- Test results and coverage reports
---
## Tasks
### T1: API Reference Documentation
**Assignee**: Docs Team
**Story Points**: 5
**Status**: TODO
**Description**:
Complete API reference documentation for all new endpoints.
**Acceptance Criteria**:
- [ ] Score Proofs API documented
- [ ] Reachability API documented
- [ ] Unknowns API documented
- [ ] Request/response examples for each endpoint
- [ ] Error codes and handling documented
- [ ] Rate limiting documented
---
### T2: Operations Runbooks
**Assignee**: Docs Team
**Story Points**: 5
**Status**: TODO
**Description**:
Create operational runbooks for Score Proofs and Reachability features.
**Acceptance Criteria**:
- [ ] Score replay runbook
- [ ] Proof verification runbook
- [ ] Reachability troubleshooting runbook
- [ ] Unknowns queue management runbook
- [ ] Air-gap operations runbook
- [ ] Escalation procedures
---
### T3: Architecture Documentation
**Assignee**: Docs Team
**Story Points**: 3
**Status**: TODO
**Description**:
Update architecture documentation with new components.
**Acceptance Criteria**:
- [ ] Update HIGH_LEVEL_ARCHITECTURE.md
- [ ] Document proof chain architecture
- [ ] Document reachability graph architecture
- [ ] Data flow diagrams
- [ ] Component interaction diagrams
---
### T4: CLI Reference Guide
**Assignee**: Docs Team
**Story Points**: 3
**Status**: TODO
**Description**:
Complete CLI reference documentation for new commands.
**Acceptance Criteria**:
- [ ] `stella score` command reference
- [ ] `stella proof` command reference
- [ ] `stella scan graph` command reference
- [ ] `stella reachability` command reference
- [ ] `stella unknowns` command reference
- [ ] Examples and common use cases
---
### T5: Training Materials
**Assignee**: Docs Team
**Story Points**: 5
**Status**: TODO
**Description**:
Create training materials for internal and external users.
**Acceptance Criteria**:
- [ ] Score Proofs concept guide
- [ ] Reachability analysis guide
- [ ] Unknowns management guide
- [ ] Video tutorials (scripts at minimum)
- [ ] FAQ document
- [ ] Troubleshooting guide
---
### T6: Release Notes
**Assignee**: Docs Team
**Story Points**: 2
**Status**: TODO
**Description**:
Prepare release notes for Score Proofs and Reachability features.
**Acceptance Criteria**:
- [ ] Feature highlights
- [ ] Breaking changes (if any)
- [ ] Migration guide (if needed)
- [ ] Known limitations
- [ ] Upgrade instructions
---
### T7: OpenAPI Specification Update
**Assignee**: Docs Team
**Story Points**: 3
**Status**: TODO
**Description**:
Finalize OpenAPI specification updates for all new endpoints.
**Acceptance Criteria**:
- [ ] All endpoints documented in OpenAPI
- [ ] Schema definitions complete
- [ ] Examples included
- [ ] Generated documentation validates
- [ ] SDK generation tested
---
### T8: Handoff Checklist
**Assignee**: Project Management
**Story Points**: 2
**Status**: TODO
**Description**:
Complete handoff to operations and support teams.
**Acceptance Criteria**:
- [ ] Knowledge transfer sessions completed
- [ ] Support team trained
- [ ] Escalation paths documented
- [ ] Monitoring dashboards configured
- [ ] Alerting rules defined
- [ ] Sign-off from stakeholders
---
## Delivery Tracker
| # | Task ID | Status | Dependency | Owners | Task Definition |
|---|---------|--------|------------|--------|-----------------|
| 1 | T1 | TODO | — | Docs Team | API Reference Documentation |
| 2 | T2 | TODO | — | Docs Team | Operations Runbooks |
| 3 | T3 | TODO | — | Docs Team | Architecture Documentation |
| 4 | T4 | TODO | — | Docs Team | CLI Reference Guide |
| 5 | T5 | TODO | T1-T4 | Docs Team | Training Materials |
| 6 | T6 | TODO | T1-T5 | Docs Team | Release Notes |
| 7 | T7 | TODO | T1 | Docs Team | OpenAPI Specification Update |
| 8 | T8 | TODO | T1-T7 | Project Mgmt | Handoff Checklist |
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2025-12-20 | Sprint file created. | Agent |
---
## Decisions & Risks
| Item | Type | Owner | Notes |
|------|------|-------|-------|
| Documentation format | Decision | Docs Team | Markdown in docs/, with generated API docs |
| Training delivery | Decision | Docs Team | Written guides + recorded sessions |
| Handoff timing | Risk | Project Mgmt | Must complete before release |
---
**Sprint Status**: TODO (0/8 tasks done)

View File

@@ -8,8 +8,6 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Scanner.Storage.Postgres;
using StellaOps.Scanner.Storage.Repositories;
using StellaOps.Scanner.WebService.Options;
namespace StellaOps.Scanner.WebService.Middleware;
@@ -21,6 +19,7 @@ public static class IdempotencyMiddlewareExtensions
{
/// <summary>
/// Adds idempotency services to the service collection.
/// Note: IIdempotencyKeyRepository is registered by AddScannerStorage.
/// </summary>
public static IServiceCollection AddIdempotency(
this IServiceCollection services,
@@ -32,8 +31,6 @@ public static class IdempotencyMiddlewareExtensions
services.Configure<IdempotencyOptions>(
configuration.GetSection(IdempotencyOptions.SectionName));
services.AddScoped<IIdempotencyKeyRepository, PostgresIdempotencyKeyRepository>();
return services;
}

View File

@@ -83,6 +83,9 @@ public static class ServiceCollectionExtensions
services.AddScoped<ICodeChangeRepository, PostgresCodeChangeRepository>();
services.AddScoped<IReachabilityDriftResultRepository, PostgresReachabilityDriftResultRepository>();
// Idempotency key storage (Sprint: SPRINT_3500_0002_0003)
services.AddScoped<IIdempotencyKeyRepository, PostgresIdempotencyKeyRepository>();
// EPSS ingestion services
services.AddSingleton<EpssCsvStreamParser>();
services.AddScoped<IEpssRepository, PostgresEpssRepository>();

View File

@@ -5,6 +5,7 @@
// Description: PostgreSQL implementation of idempotency key repository
// -----------------------------------------------------------------------------
using Dapper;
using Microsoft.Extensions.Logging;
using Npgsql;
using StellaOps.Scanner.Storage.Entities;
@@ -17,11 +18,12 @@ namespace StellaOps.Scanner.Storage.Postgres;
/// </summary>
public sealed class PostgresIdempotencyKeyRepository : IIdempotencyKeyRepository
{
private readonly NpgsqlDataSource _dataSource;
private readonly ScannerDataSource _dataSource;
private readonly ILogger<PostgresIdempotencyKeyRepository> _logger;
private string SchemaName => _dataSource.SchemaName ?? ScannerDataSource.DefaultSchema;
public PostgresIdempotencyKeyRepository(
NpgsqlDataSource dataSource,
ScannerDataSource dataSource,
ILogger<PostgresIdempotencyKeyRepository> logger)
{
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
@@ -35,41 +37,28 @@ public sealed class PostgresIdempotencyKeyRepository : IIdempotencyKeyRepository
string endpointPath,
CancellationToken cancellationToken = default)
{
const string sql = """
SELECT key_id, tenant_id, content_digest, endpoint_path,
response_status, response_body, response_headers,
created_at, expires_at
FROM scanner.idempotency_keys
WHERE tenant_id = @tenantId
AND content_digest = @contentDigest
AND endpoint_path = @endpointPath
var sql = $"""
SELECT
key_id AS KeyId,
tenant_id AS TenantId,
content_digest AS ContentDigest,
endpoint_path AS EndpointPath,
response_status AS ResponseStatus,
response_body AS ResponseBody,
response_headers AS ResponseHeaders,
created_at AS CreatedAt,
expires_at AS ExpiresAt
FROM {SchemaName}.idempotency_keys
WHERE tenant_id = @TenantId
AND content_digest = @ContentDigest
AND endpoint_path = @EndpointPath
AND expires_at > now()
""";
await using var conn = await _dataSource.OpenConnectionAsync(cancellationToken).ConfigureAwait(false);
await using var cmd = new NpgsqlCommand(sql, conn);
cmd.Parameters.AddWithValue("tenantId", tenantId);
cmd.Parameters.AddWithValue("contentDigest", contentDigest);
cmd.Parameters.AddWithValue("endpointPath", endpointPath);
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
if (!await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
return null;
}
return new IdempotencyKeyRow
{
KeyId = reader.GetGuid(0),
TenantId = reader.GetString(1),
ContentDigest = reader.GetString(2),
EndpointPath = reader.GetString(3),
ResponseStatus = reader.GetInt32(4),
ResponseBody = reader.IsDBNull(5) ? null : reader.GetString(5),
ResponseHeaders = reader.IsDBNull(6) ? null : reader.GetString(6),
CreatedAt = reader.GetDateTime(7),
ExpiresAt = reader.GetDateTime(8)
};
await using var conn = await _dataSource.OpenSystemConnectionAsync(cancellationToken).ConfigureAwait(false);
return await conn.QuerySingleOrDefaultAsync<IdempotencyKeyRow>(
new CommandDefinition(sql, new { TenantId = tenantId, ContentDigest = contentDigest, EndpointPath = endpointPath }, cancellationToken: cancellationToken))
.ConfigureAwait(false);
}
/// <inheritdoc />
@@ -77,15 +66,20 @@ public sealed class PostgresIdempotencyKeyRepository : IIdempotencyKeyRepository
IdempotencyKeyRow key,
CancellationToken cancellationToken = default)
{
const string sql = """
INSERT INTO scanner.idempotency_keys
if (key.KeyId == Guid.Empty)
{
key.KeyId = Guid.NewGuid();
}
var sql = $"""
INSERT INTO {SchemaName}.idempotency_keys
(key_id, tenant_id, content_digest, endpoint_path,
response_status, response_body, response_headers,
created_at, expires_at)
VALUES
(@keyId, @tenantId, @contentDigest, @endpointPath,
@responseStatus, @responseBody::jsonb, @responseHeaders::jsonb,
@createdAt, @expiresAt)
(@KeyId, @TenantId, @ContentDigest, @EndpointPath,
@ResponseStatus, @ResponseBody::jsonb, @ResponseHeaders::jsonb,
@CreatedAt, @ExpiresAt)
ON CONFLICT (tenant_id, content_digest, endpoint_path) DO UPDATE
SET response_status = EXCLUDED.response_status,
response_body = EXCLUDED.response_body,
@@ -95,26 +89,23 @@ public sealed class PostgresIdempotencyKeyRepository : IIdempotencyKeyRepository
RETURNING key_id
""";
await using var conn = await _dataSource.OpenConnectionAsync(cancellationToken).ConfigureAwait(false);
await using var cmd = new NpgsqlCommand(sql, conn);
if (key.KeyId == Guid.Empty)
await using var conn = await _dataSource.OpenSystemConnectionAsync(cancellationToken).ConfigureAwait(false);
var keyId = await conn.ExecuteScalarAsync<Guid>(
new CommandDefinition(sql, new
{
key.KeyId = Guid.NewGuid();
}
key.KeyId,
key.TenantId,
key.ContentDigest,
key.EndpointPath,
key.ResponseStatus,
key.ResponseBody,
key.ResponseHeaders,
key.CreatedAt,
key.ExpiresAt
}, cancellationToken: cancellationToken))
.ConfigureAwait(false);
cmd.Parameters.AddWithValue("keyId", key.KeyId);
cmd.Parameters.AddWithValue("tenantId", key.TenantId);
cmd.Parameters.AddWithValue("contentDigest", key.ContentDigest);
cmd.Parameters.AddWithValue("endpointPath", key.EndpointPath);
cmd.Parameters.AddWithValue("responseStatus", key.ResponseStatus);
cmd.Parameters.AddWithValue("responseBody", (object?)key.ResponseBody ?? DBNull.Value);
cmd.Parameters.AddWithValue("responseHeaders", (object?)key.ResponseHeaders ?? DBNull.Value);
cmd.Parameters.AddWithValue("createdAt", key.CreatedAt);
cmd.Parameters.AddWithValue("expiresAt", key.ExpiresAt);
var keyId = await cmd.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
key.KeyId = (Guid)keyId!;
key.KeyId = keyId;
_logger.LogDebug(
"Saved idempotency key {KeyId} for tenant {TenantId}, digest {ContentDigest}",
@@ -126,19 +117,18 @@ public sealed class PostgresIdempotencyKeyRepository : IIdempotencyKeyRepository
/// <inheritdoc />
public async Task<int> DeleteExpiredAsync(CancellationToken cancellationToken = default)
{
const string sql = "SELECT scanner.cleanup_expired_idempotency_keys()";
var sql = $"SELECT {SchemaName}.cleanup_expired_idempotency_keys()";
await using var conn = await _dataSource.OpenConnectionAsync(cancellationToken).ConfigureAwait(false);
await using var cmd = new NpgsqlCommand(sql, conn);
await using var conn = await _dataSource.OpenSystemConnectionAsync(cancellationToken).ConfigureAwait(false);
var result = await conn.ExecuteScalarAsync<int>(
new CommandDefinition(sql, cancellationToken: cancellationToken))
.ConfigureAwait(false);
var result = await cmd.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
var deletedCount = Convert.ToInt32(result);
if (deletedCount > 0)
if (result > 0)
{
_logger.LogInformation("Cleaned up {Count} expired idempotency keys", deletedCount);
_logger.LogInformation("Cleaned up {Count} expired idempotency keys", result);
}
return deletedCount;
return result;
}
}