PR 4b: Apply CA1822 static method modifiers + Gitea Actions secrets guidance

Completed DEBT-001 paydown (1pt) by making three pure-function methods static:
- ScheduleOccurrencePlanner.GetNextDueAt (no instance state accessed)
- PromotionGateEvaluator.Evaluate (evidence gate only, no mutations)
- EvaluationWindowPlanner.Plan (deterministic date calculation)

Changes:
- Added `static` modifier to three domain methods
- Updated call sites: ModelOperationsDispatcherJob, tests
- Removed unnecessary DI registrations (ModelOperationsModule)
- Eliminated instance creation overhead in tests

Test Results: 40/40 PASS (17 ModelOps + 18 SignalEngine + 5 Architecture)

Documentation:
- Updated TECH_DEBT_REGISTER.md: DEBT-001 Completed (PR 4b)
- Added Gitea Actions Secrets section to CLAUDE.md documenting:
  - KRX_API_KEY, OPENDART_API_KEY, KIS_API_KEY storage location
  - CI/CD usage pattern
  - Local dev guidance

Per AGENTS.md v16.0: Code changes are performance improvements, not suppressions.
Quarterly paydown: +1pt (target 4pts for 20% Q3 2026)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 05:59:01 +09:00
parent ac4ff5cd19
commit 6a31bc3737
11 changed files with 41 additions and 24 deletions
@@ -11,7 +11,7 @@ public sealed class EvaluationWindowPlanner
{
public static readonly int[] ApprovedWindows = [1, 5, 20, 63, 126, 252];
public IReadOnlyList<EvaluationWindowDue> Plan(
public static IReadOnlyList<EvaluationWindowDue> Plan(
string calendarId,
DateOnly predictionSession,
ITradingSessionCalendar calendar)
@@ -5,7 +5,7 @@ namespace KArtSell.Modules.ModelOperations.Domain;
/// </summary>
public sealed class PromotionGateEvaluator
{
public PromotionGateResult Evaluate(ModelEvaluationSnapshot snapshot, PromotionGateThresholds thresholds)
public static PromotionGateResult Evaluate(ModelEvaluationSnapshot snapshot, PromotionGateThresholds thresholds)
{
var blockers = new List<string>();
var warnings = new List<string>();
@@ -2,7 +2,7 @@ namespace KArtSell.Modules.ModelOperations.Domain;
public sealed class ScheduleOccurrencePlanner
{
public DateTimeOffset GetNextDueAt(
public static DateTimeOffset GetNextDueAt(
DateTimeOffset scheduledFor,
string cadence,
string catchUpPolicy,
@@ -9,9 +9,6 @@ public static class ModelOperationsModule
{
public static IServiceCollection AddModelOperationsModule(this IServiceCollection services)
{
services.AddSingleton<PromotionGateEvaluator>();
services.AddSingleton<ScheduleOccurrencePlanner>();
services.AddSingleton<EvaluationWindowPlanner>();
services.AddScoped<IApprovedModelContextReader, DapperApprovedModelContextReader>();
services.AddScoped<IModelScheduleRepository, DapperModelScheduleRepository>();
services.AddScoped<IModelOperationRequestRepository, DapperModelOperationRequestRepository>();
@@ -12,7 +12,6 @@ public sealed class ModelOperationsDispatcherJob(
IModelScheduleRepository schedules,
IBackgroundJobClient jobs,
IClock clock,
ScheduleOccurrencePlanner occurrencePlanner,
ILogger<ModelOperationsDispatcherJob> logger)
{
private static readonly Action<ILogger, string, string, Exception?> LogDispatchFailed =
@@ -42,7 +41,7 @@ public sealed class ModelOperationsDispatcherJob(
item.IdempotencyKey));
var backgroundJobId = jobs.Create(job, new EnqueuedState(item.Queue));
var nextDueAt = occurrencePlanner.GetNextDueAt(item.ScheduledFor, item.Cadence, item.CatchUpPolicy, item.MaxCatchUp, now);
var nextDueAt = ScheduleOccurrencePlanner.GetNextDueAt(item.ScheduledFor, item.Cadence, item.CatchUpPolicy, item.MaxCatchUp, now);
await schedules.MarkDispatchedAsync(
item.ScheduleId,
leaseOwner,