# 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