# Blocker Unblock Implementation Plan > **Created:** 2025-12-04 > **Purpose:** Step-by-step implementation plan to unblock remaining ~14 tasks > **Estimated Effort:** 16-22 developer-days ## Executive Summary After creating 11 specification contracts that unblocked ~61 tasks, we have **14 remaining blocked tasks** that require actual implementation work (not just specs). This plan outlines the implementation roadmap. --- ## Remaining Blockers Analysis | Blocker | Tasks Blocked | Type | Complexity | |---------|--------------|------|------------| | WEB-POLICY-20-004 (Rate Limiting) | 6 | Code Implementation | SIMPLE | | Shared Signals Library | 5+ | New Library | MODERATE | | Postgres Repositories | 5 | Code Implementation | MODERATE | | Test Infrastructure | N/A | Infrastructure | MODERATE | | PGMI0101 Staffing | 3 | Human Decision | N/A | --- ## Implementation Phases ### Phase 1: Policy Engine Rate Limiting (WEB-POLICY-20-004) **Duration:** 1-2 days **Unblocks:** 6 tasks (WEB-POLICY-20-004 chain) **Dependencies:** None #### 1.1 Create Rate Limit Options **File:** `src/Policy/StellaOps.Policy.Engine/Options/PolicyEngineRateLimitOptions.cs` ```csharp namespace StellaOps.Policy.Engine.Options; public sealed class PolicyEngineRateLimitOptions { public const string SectionName = "RateLimiting"; /// Default permits per window for simulation endpoints public int SimulationPermitLimit { get; set; } = 100; /// Window duration in seconds public int WindowSeconds { get; set; } = 60; /// Queue limit for pending requests public int QueueLimit { get; set; } = 10; /// Enable tenant-aware partitioning public bool TenantPartitioning { get; set; } = true; } ``` #### 1.2 Register Rate Limiter in Program.cs Add to `src/Policy/StellaOps.Policy.Engine/Program.cs`: ```csharp // Rate limiting configuration var rateLimitOptions = builder.Configuration .GetSection(PolicyEngineRateLimitOptions.SectionName) .Get() ?? new(); builder.Services.AddRateLimiter(options => { options.RejectionStatusCode = StatusCodes.Status429TooManyRequests; options.AddTokenBucketLimiter("policy-simulation", limiterOptions => { limiterOptions.TokenLimit = rateLimitOptions.SimulationPermitLimit; limiterOptions.ReplenishmentPeriod = TimeSpan.FromSeconds(rateLimitOptions.WindowSeconds); limiterOptions.TokensPerPeriod = rateLimitOptions.SimulationPermitLimit; limiterOptions.QueueLimit = rateLimitOptions.QueueLimit; limiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; }); options.OnRejected = async (context, cancellationToken) => { PolicyEngineTelemetry.RateLimitExceededCounter.Add(1); context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests; await context.HttpContext.Response.WriteAsJsonAsync(new { error = "ERR_POL_007", message = "Rate limit exceeded. Please retry after the reset window.", retryAfterSeconds = rateLimitOptions.WindowSeconds }, cancellationToken); }; }); ``` #### 1.3 Apply to Simulation Endpoints Modify `src/Policy/StellaOps.Policy.Engine/Endpoints/RiskSimulationEndpoints.cs`: ```csharp group.MapPost("/simulate", SimulateRisk) .RequireRateLimiting("policy-simulation") // ADD THIS .WithName("SimulateRisk"); ``` #### 1.4 Add Telemetry Counter Add to `src/Policy/StellaOps.Policy.Engine/Telemetry/PolicyEngineTelemetry.cs`: ```csharp public static readonly Counter RateLimitExceededCounter = Meter.CreateCounter( "policy_rate_limit_exceeded_total", unit: "requests", description: "Total requests rejected due to rate limiting"); ``` #### 1.5 Configuration Sample Add to `etc/policy-engine.yaml.sample`: ```yaml RateLimiting: SimulationPermitLimit: 100 WindowSeconds: 60 QueueLimit: 10 TenantPartitioning: true ``` --- ### Phase 2: Shared Signals Contracts Library **Duration:** 3-4 days **Unblocks:** 5+ modules (Concelier, Scanner, Policy, Signals, Authority) **Dependencies:** None #### 2.1 Create Project Structure ``` src/__Libraries/StellaOps.Signals.Contracts/ ├── StellaOps.Signals.Contracts.csproj ├── AGENTS.md ├── Models/ │ ├── SignalEnvelope.cs │ ├── SignalType.cs │ ├── ReachabilitySignal.cs │ ├── EntropySignal.cs │ ├── ExploitabilitySignal.cs │ ├── TrustSignal.cs │ └── UnknownSymbolSignal.cs ├── Abstractions/ │ ├── ISignalEmitter.cs │ ├── ISignalConsumer.cs │ └── ISignalContext.cs └── Extensions/ └── ServiceCollectionExtensions.cs ``` #### 2.2 Core Models **SignalEnvelope.cs:** ```csharp namespace StellaOps.Signals.Contracts; public sealed record SignalEnvelope( string SignalKey, SignalType SignalType, object Value, DateTimeOffset ComputedAt, string SourceService, string? TenantId = null, string? CorrelationId = null, string? ProvenanceDigest = null); ``` **SignalType.cs:** ```csharp namespace StellaOps.Signals.Contracts; public enum SignalType { Reachability, Entropy, Exploitability, Trust, UnknownSymbol, Custom } ``` #### 2.3 Signal Models Each signal type gets a dedicated record: - `ReachabilitySignal` - package reachability from callgraph - `EntropySignal` - code complexity/risk metrics - `ExploitabilitySignal` - KEV status, exploit availability - `TrustSignal` - reputation, chain of custody scores - `UnknownSymbolSignal` - unresolved dependencies #### 2.4 Abstractions ```csharp public interface ISignalEmitter { ValueTask EmitAsync(SignalEnvelope signal, CancellationToken ct = default); ValueTask EmitBatchAsync(IEnumerable signals, CancellationToken ct = default); } public interface ISignalConsumer { IAsyncEnumerable ConsumeAsync( SignalType? filterType = null, CancellationToken ct = default); } ``` --- ### Phase 3: Postgres Repositories **Duration:** 4-5 days **Unblocks:** Persistence for new features **Dependencies:** SQL migrations #### 3.1 Repository Interfaces Create in `src/Policy/__Libraries/StellaOps.Policy.Storage.Postgres/Repositories/`: | Interface | Methods | |-----------|---------| | `ISnapshotRepository` | Create, GetById, List, Delete | | `IViolationEventRepository` | Append, GetById, List (immutable) | | `IWorkerResultRepository` | Create, GetById, List, Update | | `IConflictRepository` | Create, GetById, List, Resolve | | `ILedgerExportRepository` | Create, GetById, List, GetByDigest | #### 3.2 SQL Migrations Create migrations for tables: ```sql -- policy.snapshots CREATE TABLE policy.snapshots ( id UUID PRIMARY KEY, tenant_id TEXT NOT NULL, policy_id UUID NOT NULL, version INTEGER NOT NULL, content_digest TEXT NOT NULL, metadata JSONB, created_by TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- policy.violation_events (append-only) CREATE TABLE policy.violation_events ( id UUID PRIMARY KEY, tenant_id TEXT NOT NULL, policy_id UUID NOT NULL, rule_id TEXT NOT NULL, severity TEXT NOT NULL, subject_purl TEXT, details JSONB, occurred_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Similar for conflicts, worker_results, ledger_exports ``` #### 3.3 Implementation Pattern Follow `RiskProfileRepository.cs` pattern: ```csharp public sealed class SnapshotRepository : RepositoryBase, ISnapshotRepository { public SnapshotRepository(PolicyDataSource dataSource, ILogger logger) : base(dataSource, logger) { } public async Task CreateAsync(SnapshotEntity entity, CancellationToken ct) { const string sql = """ INSERT INTO policy.snapshots (id, tenant_id, policy_id, version, content_digest, metadata, created_by) VALUES (@Id, @TenantId, @PolicyId, @Version, @ContentDigest, @Metadata::jsonb, @CreatedBy) RETURNING * """; return await ExecuteScalarAsync(sql, entity, ct); } // ... other CRUD methods } ``` --- ### Phase 4: Test Infrastructure **Duration:** 2-3 days **Unblocks:** Validation before merge **Dependencies:** Phase 3 #### 4.1 Postgres Test Fixture ```csharp public sealed class PostgresFixture : IAsyncLifetime { private TestcontainersContainer? _container; public string ConnectionString { get; private set; } = string.Empty; public async Task InitializeAsync() { _container = new TestcontainersBuilder() .WithImage("postgres:16-alpine") .WithEnvironment("POSTGRES_PASSWORD", "test") .WithPortBinding(5432, true) .Build(); await _container.StartAsync(); ConnectionString = $"Host=localhost;Port={_container.GetMappedPublicPort(5432)};..."; // Run migrations await MigrationRunner.RunAsync(ConnectionString); } public async Task DisposeAsync() => await _container?.DisposeAsync(); } ``` #### 4.2 Test Classes - `RateLimitingTests.cs` - quota exhaustion, recovery, tenant partitioning - `SnapshotRepositoryTests.cs` - CRUD operations - `ViolationEventRepositoryTests.cs` - append-only semantics - `ConflictRepositoryTests.cs` - resolution workflow - `SignalEnvelopeTests.cs` - serialization, validation --- ### Phase 5: New Endpoints **Duration:** 2-3 days **Unblocks:** API surface completion **Dependencies:** Phase 3 #### 5.1 Endpoint Groups | Path | Operations | Auth | |------|------------|------| | `/api/policy/snapshots` | GET, POST, DELETE | `policy:read`, `policy:author` | | `/api/policy/violations` | GET | `policy:read` | | `/api/policy/conflicts` | GET, POST (resolve) | `policy:read`, `policy:review` | | `/api/policy/exports` | GET, POST | `policy:read`, `policy:archive` | --- ## Execution Order ``` Day 1-2: Phase 1 (Rate Limiting) └── WEB-POLICY-20-004 ✓ UNBLOCKED Day 3-5: Phase 2 (Signals Library) └── Concelier, Scanner, Policy, Signals, Authority ✓ ENABLED Day 6-9: Phase 3 (Repositories) └── Persistence layer ✓ COMPLETE Day 10-12: Phase 4 (Tests) └── Validation ✓ READY Day 13-15: Phase 5 (Endpoints) └── API surface ✓ COMPLETE ``` --- ## Files to Create/Modify Summary ### New Files (22 files) ``` src/Policy/StellaOps.Policy.Engine/Options/ └── PolicyEngineRateLimitOptions.cs src/__Libraries/StellaOps.Signals.Contracts/ ├── StellaOps.Signals.Contracts.csproj ├── AGENTS.md ├── Models/SignalEnvelope.cs ├── Models/SignalType.cs ├── Models/ReachabilitySignal.cs ├── Models/EntropySignal.cs ├── Models/ExploitabilitySignal.cs ├── Models/TrustSignal.cs ├── Models/UnknownSymbolSignal.cs ├── Abstractions/ISignalEmitter.cs ├── Abstractions/ISignalConsumer.cs └── Extensions/ServiceCollectionExtensions.cs src/Policy/__Libraries/StellaOps.Policy.Storage.Postgres/Repositories/ ├── ISnapshotRepository.cs ├── SnapshotRepository.cs ├── IViolationEventRepository.cs ├── ViolationEventRepository.cs ├── IConflictRepository.cs ├── ConflictRepository.cs ├── ILedgerExportRepository.cs └── LedgerExportRepository.cs ``` ### Files to Modify (5 files) ``` src/Policy/StellaOps.Policy.Engine/Program.cs src/Policy/StellaOps.Policy.Engine/Telemetry/PolicyEngineTelemetry.cs src/Policy/StellaOps.Policy.Engine/Endpoints/RiskSimulationEndpoints.cs src/Policy/StellaOps.Policy.Engine/Endpoints/PathScopeSimulationEndpoint.cs etc/policy-engine.yaml.sample ``` --- ## Success Criteria - [ ] Rate limiting returns 429 when quota exceeded - [ ] Signals library compiles and referenced by 5+ modules - [ ] All 5 repositories pass CRUD tests - [ ] Endpoints return proper responses with auth - [ ] Telemetry metrics visible in dashboards - [ ] No regression in existing tests --- ## Risk Mitigation | Risk | Mitigation | |------|------------| | Breaking existing endpoints | Feature flag rate limiting | | Signal library circular deps | Careful namespace isolation | | Migration failures | Test migrations in isolated DB first | | Test flakiness | Use deterministic test data | --- ## Next Steps 1. **Start Phase 1** - Implement rate limiting (simplest, immediate impact) 2. **Parallel Phase 2** - Create Signals.Contracts scaffolding 3. **Review** - Get feedback before Phase 3