Initial commit: Add project files
ci / backend (push) Failing after 12s
ci / frontend (push) Failing after 19s
ci / static (push) Failing after 45s

This commit is contained in:
2026-08-02 05:15:36 +09:00
commit dcd1322d41
636 changed files with 122352 additions and 0 deletions
@@ -0,0 +1,10 @@
using KArtSell.Modules.ModelOperations.Domain;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class EvaluationReconciliationPlannerTests
{
[Fact] public void Missing_windows_are_planned_and_duplicates_quarantined() {
var plan=EvaluationReconciliationPlanner.Plan([new EvaluationWindowFact(1,true,true,false,false,false)]);
Assert.Equal(EvaluationReconciliationAction.QuarantineDuplicate,plan[1]);
Assert.Equal(EvaluationReconciliationAction.PlanMissing,plan[252]);
}
}
@@ -0,0 +1,21 @@
using KArtSell.Modules.ModelOperations.Domain;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class EvaluationWindowPlannerTests
{
[Fact] public void Uses_trading_sessions_not_calendar_days()
{
var planner = new EvaluationWindowPlanner();
var due = planner.Plan("KRX", new DateOnly(2026, 8, 3), new WeekdayCalendar());
Assert.Equal(new[] { 1, 5, 20, 63, 126, 252 }, due.Select(x => x.WindowTradingDays));
Assert.All(due, x => Assert.DoesNotContain(x.DueSession.DayOfWeek, new[] { DayOfWeek.Saturday, DayOfWeek.Sunday }));
}
private sealed class WeekdayCalendar : ITradingSessionCalendar
{
public DateOnly AddTradingSessions(string calendarId, DateOnly startSession, int sessionCount)
{
var current = startSession; var count = 0;
while (count < sessionCount) { current = current.AddDays(1); if (current.DayOfWeek is not (DayOfWeek.Saturday or DayOfWeek.Sunday)) count++; }
return current;
}
}
}
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../src/KArtSell.Modules.ModelOperations/KArtSell.Modules.ModelOperations.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" PrivateAssets="all" />
</ItemGroup>
</Project>
@@ -0,0 +1,41 @@
using KArtSell.Modules.ModelOperations.FeedbackLoop;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class ModelFeedbackCycleTests
{
[Fact]
public void Happy_path_stops_at_human_promotion_review_and_closes()
{
var cycle = new ModelFeedbackCycle(Guid.NewGuid(), "KR-EQUITY", "model-1", DateTimeOffset.UtcNow);
var now = DateTimeOffset.UtcNow;
cycle.MoveTo(ModelFeedbackCycleState.PredictionFrozen, "FREEZE_OK", "hash-1", "system", now);
cycle.MoveTo(ModelFeedbackCycleState.OutcomesMaturing, "WINDOWS_OPEN", "hash-2", "system", now);
cycle.MoveTo(ModelFeedbackCycleState.Evaluated, "SCORECARD_READY", "hash-3", "system", now);
cycle.MoveTo(ModelFeedbackCycleState.ImprovementProposed, "HYPOTHESIS_REVIEWED", "hash-4", "quant", now);
cycle.MoveTo(ModelFeedbackCycleState.ChallengerPlanned, "PLAN_APPROVED", "hash-5", "validator", now);
cycle.MoveTo(ModelFeedbackCycleState.IndependentlyValidated, "OOS_VALIDATED", "hash-6", "validator", now);
cycle.MoveTo(ModelFeedbackCycleState.PromotionReviewPending, "PACK_READY", "hash-7", "risk", now);
cycle.MoveTo(ModelFeedbackCycleState.Closed, "HUMAN_DECISION_RECORDED", "hash-8", "investment-committee", now);
Assert.Equal(ModelFeedbackCycleState.Closed, cycle.State);
Assert.Equal(9, cycle.Revision);
Assert.Equal(8, cycle.Transitions.Count);
}
[Fact]
public void Skipping_independent_validation_is_rejected()
{
var cycle = new ModelFeedbackCycle(Guid.NewGuid(), "US-EQUITY", "model-1", DateTimeOffset.UtcNow);
Assert.Throws<InvalidOperationException>(() => cycle.MoveTo(
ModelFeedbackCycleState.PromotionReviewPending, "SKIP", "hash", "system", DateTimeOffset.UtcNow));
}
[Fact]
public void Plan_contains_manual_only_activation_boundary()
{
var activation = Assert.Single(ModelFeedbackPlan.Stages.Where(x => x.StageCode == "ACTIVATE"));
Assert.Equal("AUTOMATION_FORBIDDEN", activation.AutomationBoundary);
Assert.Contains("NO_AUTOMATIC_MODEL_ACTIVATION", ModelFeedbackPlan.Boundary);
}
}
@@ -0,0 +1,20 @@
using KArtSell.Modules.ModelOperations.FeedbackLoop;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class ModelImprovementHypothesisTests
{
[Fact]
public void Decision_required_and_missing_counter_evidence_block_experiment()
{
var hypothesis = new ModelImprovementHypothesis(
Guid.NewGuid(), "KR-EQUITY", "model-1", "false exit increased", "review floor calibration",
"reject when OOS utility does not improve under double cost",
[new(EvidenceClassification.DecisionRequired, "DEC-012", "latest PIT source unresolved", "h1")],
[], "Quant Lead", DateTimeOffset.UtcNow.AddDays(30));
var errors = hypothesis.Validate(DateTimeOffset.UtcNow);
Assert.Contains("COUNTER_EVIDENCE_REQUIRED", errors);
Assert.Contains("DECISION_REQUIRED_BLOCKS_EXPERIMENT", errors);
}
}
@@ -0,0 +1,21 @@
using KArtSell.Modules.ModelOperations.Domain;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class ModelOperationExecutionBoundaryTests
{
[Fact]
public void Registry_definitions_are_unique_and_evidence_only()
{
Assert.Equal(ModelOperationRegistry.All.Count, ModelOperationRegistry.All.Select(x => x.OperationCode).Distinct().Count());
Assert.All(ModelOperationRegistry.All, ModelOperationExecutionBoundary.EnsureAllowed);
}
[Fact]
public void Registry_never_contains_order_or_auto_promotion_operations()
{
var forbidden = new[] { "ORDER", "BROKER", "AUTO_PROMOTE", "AUTO_ROLLBACK", "KIS_SUBMIT" };
Assert.All(ModelOperationRegistry.All, operation =>
Assert.DoesNotContain(forbidden, token => operation.Name.Contains(token, StringComparison.OrdinalIgnoreCase)));
}
}
@@ -0,0 +1,14 @@
using KArtSell.Modules.ModelOperations.Domain;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class ModelOperationExecutionTests
{
[Fact] public void Business_hold_can_resume_but_success_is_terminal()
{
var now = DateTimeOffset.UtcNow;
var execution = new ModelOperationExecution(Guid.NewGuid(), "J32:KR:1", now);
execution.MoveTo(ModelOperationExecutionState.BusinessHold, "PIT_NOT_READY", "h1", now);
execution.MoveTo(ModelOperationExecutionState.Running, "DATA_READY", "h2", now.AddMinutes(1));
execution.MoveTo(ModelOperationExecutionState.Succeeded, "OUTPUT_FROZEN", "h3", now.AddMinutes(2));
Assert.Throws<InvalidOperationException>(() => execution.MoveTo(ModelOperationExecutionState.Running, "REOPEN", "h4", now.AddMinutes(3)));
}
}
@@ -0,0 +1,13 @@
using KArtSell.Modules.ModelOperations.Domain;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class ModelOperationLeaseTests
{
[Fact] public void Stale_token_cannot_renew() {
var now=DateTimeOffset.UtcNow; var lease=new ModelOperationLease("J42","GLOBAL",new LeaseFencingToken(3),"worker-a",now.AddMinutes(5));
Assert.Throws<InvalidOperationException>(()=>lease.Renew(new LeaseFencingToken(2),"worker-a",now.AddMinutes(10)));
}
[Fact] public void Expired_lease_transfer_increments_token() {
var now=DateTimeOffset.UtcNow; var lease=new ModelOperationLease("J42","GLOBAL",new LeaseFencingToken(3),"worker-a",now.AddMinutes(-1));
Assert.Equal(4,lease.Transfer(now,"worker-b",now.AddMinutes(5)).Value);
}
}
@@ -0,0 +1,28 @@
using KArtSell.Modules.ModelOperations.Domain;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class ModelOperationRegistryTests
{
[Fact]
public void Operation_codes_are_unique_and_no_auto_promotion_mode_exists()
{
var codes = ModelOperationRegistry.All.Select(x => x.OperationCode).ToArray();
Assert.Equal(codes.Length, codes.Distinct(StringComparer.OrdinalIgnoreCase).Count());
Assert.All(ModelOperationRegistry.All, operation =>
Assert.Contains(operation.AutomationMode, new[]
{
AutomationMode.EvaluationOnly,
AutomationMode.ProposalOnly,
AutomationMode.DrillOnly
}));
}
[Fact]
public void Improvement_and_promotion_packet_jobs_are_proposal_only()
{
Assert.Equal(AutomationMode.ProposalOnly, ModelOperationRegistry.GetRequired("J21").AutomationMode);
Assert.Equal(AutomationMode.ProposalOnly, ModelOperationRegistry.GetRequired("J22").AutomationMode);
}
}
@@ -0,0 +1,57 @@
using KArtSell.Modules.ModelOperations.Domain;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class PromotionGateEvaluatorTests
{
[Fact]
public void Passes_evidence_gate_but_still_requires_human_approval()
{
var snapshot = new ModelEvaluationSnapshot(
"GLOBAL",
"model-1",
504,
3,
0.08m,
0.01m,
0.70m,
0.10m,
0.97m,
true,
0,
true,
DateTimeOffset.UtcNow);
var result = new PromotionGateEvaluator().Evaluate(snapshot, PromotionGateThresholds.ResearchBaseline);
Assert.Equal(GateDecision.Pass, result.Decision);
Assert.Empty(result.BlockingReasons);
Assert.True(result.RequiresIndependentValidation);
Assert.True(result.RequiresHumanApproval);
Assert.Equal("EVIDENCE_ONLY_NO_AUTO_PROMOTION", result.Boundary);
}
[Fact]
public void Holds_when_any_operational_integrity_error_exists()
{
var snapshot = new ModelEvaluationSnapshot(
"GLOBAL",
"model-1",
504,
3,
0.08m,
0.01m,
0.70m,
0.10m,
0.97m,
true,
1,
true,
DateTimeOffset.UtcNow);
var result = new PromotionGateEvaluator().Evaluate(snapshot, PromotionGateThresholds.ResearchBaseline);
Assert.Equal(GateDecision.Hold, result.Decision);
Assert.Contains(result.BlockingReasons, reason => reason.Contains("Operational integrity", StringComparison.Ordinal));
}
}
@@ -0,0 +1,19 @@
using KArtSell.Modules.ModelOperations.Domain;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class ScheduleOccurrencePlannerTests
{
private readonly ScheduleOccurrencePlanner planner = new();
[Fact] public void Daily_anchor_does_not_drift_to_dispatch_time()
{
var scheduled = new DateTimeOffset(2026, 8, 1, 1, 0, 0, TimeSpan.Zero);
var next = planner.GetNextDueAt(scheduled, "DAILY", "LATEST_ONLY", 1, scheduled.AddHours(10));
Assert.Equal(scheduled.AddDays(1), next);
}
[Fact] public void Missed_occurrences_are_skipped_without_dispatch_storm()
{
var scheduled = new DateTimeOffset(2026, 7, 1, 1, 0, 0, TimeSpan.Zero);
var next = planner.GetNextDueAt(scheduled, "DAILY", "LATEST_ONLY", 1, new DateTimeOffset(2026, 8, 1, 4, 0, 0, TimeSpan.Zero));
Assert.True(next > new DateTimeOffset(2026, 8, 1, 4, 0, 0, TimeSpan.Zero));
Assert.Equal(1, next.Hour);
}
}