feat(ai): centralize durable usage
CI and Deploy / test (pull_request) Successful in 5m19s
CI and Deploy / deploy (pull_request) Has been skipped

Add a content-free usage ledger with legacy backfill. Reserve Workspace and durable Strategy/CV work before execution so deleted history or duplicate admission cannot reset limits.
This commit is contained in:
cesnimda
2026-08-15 20:03:06 +02:00
parent dbff0f8d49
commit 134aac7bcf
28 changed files with 3539 additions and 83 deletions
@@ -60,6 +60,12 @@ public sealed class StrategySnapshotOperationTests
Assert.Equal("ollama", operation.Provider);
Assert.Equal("qwen-test", operation.Model);
Assert.Equal(1, fixture.GenerationCalls);
var usage = Assert.Single(await db.AiUsageRecords.ToListAsync());
Assert.Equal("strategy.snapshot", usage.TaskType);
Assert.True(usage.InputCharacterCount > 0);
Assert.Equal(Fixture.ValidResponse.Length, usage.OutputCharacterCount);
Assert.Equal((usage.InputCharacterCount + usage.OutputCharacterCount + 3) / 4, usage.EstimatedTokenCount);
Assert.True(usage.EstimatedTokenCount < AiUsageMeter.ReservationFor(StrategySnapshotService.TaskType).EstimatedTokens);
}
[Fact]
@@ -103,7 +109,7 @@ public sealed class StrategySnapshotOperationTests
private sealed class Fixture : IAsyncDisposable
{
private const string ValidResponse = """{"strategicSummary":"Lead with delivery evidence.","cvBulletIdeas":["Built APIs"],"proofPointsToLeadWith":["Led delivery"],"coverLetterAngles":["Platform relevance"]}""";
internal const string ValidResponse = """{"strategicSummary":"Lead with delivery evidence.","cvBulletIdeas":["Built APIs"],"proofPointsToLeadWith":["Led delivery"],"coverLetterAngles":["Platform relevance"]}""";
private readonly SqliteConnection _connection;
private readonly Mock<ISummarizerService> _summarizer;
public ServiceProvider Provider { get; }
@@ -146,6 +152,7 @@ public sealed class StrategySnapshotOperationTests
services.AddSingleton<AiPrivacyPolicy>();
services.AddSingleton<AiOperationExecutionScope>();
services.AddScoped<UserOperationStore>();
services.AddScoped<AiUsageMeter>();
services.AddScoped<AiOperationAdmission>();
services.AddScoped<StrategySnapshotService>();
services.AddSingleton<IAiOperationHandler, StrategySnapshotOperationHandler>();