Run #12
Failed Guard: — Preflight: — Build: — Test: —
Persisted agent run. Review patches, guard/preflight results, and take actions (apply/build/test/autopilot).
Live pipeline
Real-time step updates (SignalR).
Live: Connecting…
Waiting for updates…
Autopilot is disabled because there is no PatchSet on this run yet.
Planner: gpt-5-mini
Implementer: gpt-5-mini
Reviewer: gpt-5
Guard: gpt-5-mini
Autopilot Timeline
Each card represents one PatchSet attempt with Guard/Preflight/Apply/Build/Test outcomes.
Task
Agent run objective
TASK: Implement NBA Injury storage schema (snapshots, enterprise-grade) using EF migrations.
Requirements
• Create Domain entities only (no EF attributes) for injuries:
o NbaInjurySnapshotBatch (identity PK; SourceName, PulledAtUtc, optional SeasonId, optional Notes, BatchGuid or BatchKey string)
o NbaPlayerInjurySnapshot (identity PK; FK to batch; AsOfUtc (or use batch PulledAtUtc); TeamId int; nullable PlayerId long?; PlayerName string; Status enum; Description string?; ReturnDateUtc DateTime?; IsMapped bool; RawJson string?; CreatedUtc)
o Enum NbaInjuryStatus (Unknown, Available, Probable, Questionable, Doubtful, Out)
• Update AppDbContext:
o Add DbSets
o Configure tables, keys, FK batch→snapshots (Restrict)
o String max lengths (PlayerName 80, SourceName 40, Status as int, Description 300, RawJson max)
o Indexes:
Batch: (SourceName, PulledAtUtc)
Snapshot: (TeamId, AsOfUtc) and (PlayerId, AsOfUtc) (PlayerId nullable -> still index)
Snapshot unique: (BatchId, TeamId, PlayerName) (or (BatchId, TeamId, PlayerId) if mapped) — choose safe uniqueness
• Add migration + update DB
Constraints
• Domain doesn’t depend on anything.
• DbContext not used from Pages.
• UTC everywhere.
Deliverable
• PatchSet containing entity files + DbContext mapping + migration.
Reviewer must validate
• Snapshot model supports multiple pulls/day
• Indexes are sane and scalable
• Migration builds
Created
2026-02-19 18:23
Completed
2026-02-19 18:25
Repo
—
Models
Planner: gpt-5-mini · Implementer: gpt-5-mini · Reviewer: gpt-5 · Guard: gpt-5-mini
Governance bundle
—
Files bundle
—
Steps
3
total
Planner / Implementer / Reviewer / Guard / Apply / Build / Test
Patch sets
0
generated
Latest: —
Executions
0
build/test
Captured logs are shown below.
Pipeline steps
Each step is persisted with messages and duration.
| Step | Status | Started | Completed | ms | Model | Msgs |
|---|---|---|---|---|---|---|
| Planner | Succeeded | 2026-02-19 18:23:18 | 2026-02-19 18:23:47 | 28911 | gpt-5-mini | 2 |
|
Step messages
User
planner_schema
UTC: 2026-02-20 02:23:18
TASK: TASK: Implement NBA Injury storage schema (snapshots, enterprise-grade) using EF migrations. Requirements • Create Domain entities only (no EF attributes) for injuries: o NbaInjurySnapshotBatch (identity PK; SourceName, PulledAtUtc, optional SeasonId, optional Notes, BatchGuid or BatchKey string) o NbaPlayerInjurySnapshot (identity PK; FK to batch; AsOfUtc (or use batch PulledAtUtc); TeamId int; nullable PlayerId long?; PlayerName string; Status enum; Description string?; ReturnDateUtc DateTime?; IsMapped bool; RawJson string?; CreatedUtc) o Enum NbaInjuryStatus (Unknown, Available, Probable, Questionable, Doubtful, Out) • Update AppDbContext: o Add DbSets o Configure tables, keys, FK batch→snapshots (Restrict) o String max lengths (PlayerName 80, SourceName 40, Status as int, Description 300, RawJson max) o Indexes: Batch: (SourceName, PulledAtUtc) Snapshot: (TeamId, AsOfUtc) and (PlayerId, AsOfUtc) (PlayerId nullable -> still index) Snapshot unique: (BatchId, TeamId, PlayerName) (or (BatchId, TeamId, PlayerId) if mapped) — choose safe uniqueness • Add migration + update DB Constraints • Domain doesn’t depend on anything. • DbContext not used from Pages. • UTC everywhere. Deliverable • PatchSet containing entity files + DbContext mapping + migration. Reviewer must validate • Snapshot model supports multiple pulls/day • Indexes are sane and scalable • Migration builds SOLUTION TREE: BettingOdds +---App | +---Agents | | +---Autopilot | | | | AgentAutopilotRunner.cs | | | | AutopilotOptions.cs | | +---Git | | | | GitRunner.cs | | +---Guard | | | | GuardEvaluator.cs | | | | GuardPolicyOptions.cs | | +---Live | | | | AaiRunHub.cs | | | | BackgroundTaskQueue.cs | | | | IAaiLiveNotifier.cs | | | | QueuedHostedService.cs | | | | SignalRAaiLiveNotifier.cs | | +---Models | | | | AgentPatch.cs | | | | AgentPolicyGuard.cs | | | | AgentRunContext.cs | | | | AgentRunResult.cs | | | | IAgentPolicyGuard.cs | | +---Patching | | | | PatchNormalization.cs | | | | PatchPreflightValidator.cs | | | | PreflightPolicyOptions.cs | | | | UnifiedDiffBuilder.cs | | +---RunLaunch | | | | AaiRunLauncher.cs | | +---Steps | | | +---Models | | | | | ImplementerInput.cs | | | | | PlannerInput.cs | | | | | ReviewerInput.cs | | | | AgentJson.cs | | | | IAgentStep.cs | | | | ImplementerAgent.cs | | | | PlannerAgent.cs | | | | ReviewerAgent.cs | | +---Tests | | | | TestAuthorAgent.cs | | | AaiRunWriter.cs | | | AgentOrchestrator.cs | | | AgentPolicyOptions.cs | | | CommandPolicy.cs | | | DiffApplier.cs | | | DotnetRunner.cs | | | OpenAiOptions.cs | | | OpenAiResponsesClient.cs | | | RepoFileService.cs | +---Dtos | | | NbaTeamAdvancedRow.cs | +---Pricing | | | FairLinesEngine.cs | | | FairLinesMath.cs | | | FairLinesOptions.cs | | | FairLinesResult.cs | | | IFairLinesEngine.cs | | | IStartingLineupResolver.cs | | | NbaProjectionService.cs | | | ProjectionResult.cs | | | StartingLineupResolver.cs | +---Queries | | +---Agents | | | | AaiRunsQuery.cs | | +---Games | | | | GameRow.cs | | | | GamesQuery.cs | | | | IGamesQuery.cs | | +---Matchup | | | | IMatchupQuery.cs | | | | MatchupQuery.cs | | | | MatchupVm.cs | | +---Player | | | | IPlayerQuery.cs | | | | PlayerQuery.cs | | +---Teams | | | | ISyncTeamStatsQuery.cs | | | | ITeamQuery.cs | | | | ITeamsQuery.cs | | | | SyncTeamStatsQuery.cs | | | | TeamQuery.cs | | | | TeamsQuery.cs | +---Sync | | +---GameSync | | | | GameSyncOrchestrator.cs | | | | IGameSyncOrchestrator.cs | | +---Modules | | | +---Impl | | | | | PlayerSnapshotBuilder.cs | | | | | PlayerSync.cs | | | | | ScheduleSync.cs | | | | | TeamStatsSync.cs | | | | IPlayerSnapshotBuilder.cs | | | | IPlayerSync.cs | | | | IScheduleSync.cs | | | | ITeamStatsSync.cs | | | ISeasonResolver.cs | | | ISyncCenter.cs | | | NbaSeedService.cs | | | SeasonResolver.cs | | | SyncCenter.cs | | | SyncOptions.cs | | | SyncResult.cs | | | SyncTeamStatsUseCase.cs | +---Time | | | CostaRicaAppClock.cs | | | IAppClock.cs +---Data | | AppDbContext.cs | | AppDbContextFactory.cs +---docs | | AGENT_EF_PATTERN.md | | AGENT_RULES.md | | AGENTS.md | | ARCHITECTURE.md | | CODING_RULES.md | | CODING_STANDARDS.md | | PR_TEMPLATE.md | | PRICING_MODEL_CONTRACT.md | | ROADMAP.md | | TASKS.md +---Domain | +---Entities | | +---Agents | | | +---Enums | | | | | AaiApplyResult.cs | | | | | AaiDotnetExecutionType.cs | | | | | AaiGuardSeverity.cs | | | | | AaiMessageRole.cs | | | | | AaiRunStatus.cs | | | | | AaiStepStatus.cs | | | | | AaiStepType.cs | | | | AaiAgentMessage.cs | | | | AaiAgentPatch.cs | | | | AaiAgentPatchSet.cs | | | | AaiAgentRun.cs | | | | AaiAgentRunStep.cs | | | | AaiDotnetExecution.cs | | | | AaiGuardReport.cs | | | | AaiGuardViolation.cs | | | | AaiPatchApplyAttempt.cs | | | | AaiPatchPreflightReport.cs | | | | AaiPatchPreflightViolation.cs | | | | AaiRepoFileSnapshot.cs | | | NbaGame.cs | | | NbaPlayer.cs | | | NbaPlayerGameStat.cs | | | NbaPlayerRelevanceSnapshot.cs | | | NbaPlayerRollingStatsSnapshot.cs | | | NbaPlayerTeam.cs | | | NbaSeason.cs | | | NbaTeam.cs | | | NbaTeamStatsSnapshot.cs | +---Enum | +---ValueObjects +---Infrastructure | +---NbaStats | | | NbaScheduleClient.cs | | | NbaStatsClient.cs | | | NbaStatsService.cs | +---Time +---Migrations | | 20260212223141_Init.cs | | 20260212223141_Init.Designer.cs | | 20260213025511_NbaSchemaV1.cs | | 20260213025511_NbaSchemaV1.Designer.cs | | 20260218153318_Reconcile_NbaTeams_LogoUrl.cs | | 20260218153318_Reconcile_NbaTeams_LogoUrl.Designer.cs | | 20260218153704_AddAaiAgenticSchema.cs | | 20260218153704_AddAaiAgenticSchema.Designer.cs | | 20260218154538_Repair_AaiAgenticSchema.cs | | 20260218154538_Repair_AaiAgenticSchema.Designer.cs | | 20260218154908_Repair_AaiAgenticSchema2.cs | | 20260218154908_Repair_AaiAgenticSchema2.Designer.cs | | 20260218224121_AddAaiPatchPreflight.cs | | 20260218224121_AddAaiPatchPreflight.Designer.cs | | 20260218231615_Aai_ApplyAttempt_CommitFields.cs | | 20260218231615_Aai_ApplyAttempt_CommitFields.Designer.cs | | 20260219003652_Aai_Latest_Updates.cs | | 20260219003652_Aai_Latest_Updates.Designer.cs | | AppDbContextModelSnapshot.cs +---Pages | +---Agents | | +---Runs | | | +---Partials | | | | | _AutopilotTimeline.cshtml | | | | | _ExecutionsCard.cshtml | | | | | _GuardCard.cshtml | | | | | _LivePipeline.cshtml | | | | | _LivePipelineScripts.cshtml | | | | | _OverviewAndStats.cshtml | | | | | _PatchSetsCard.cshtml | | | | | _RepoSnapshotsCard.cshtml | | | | | _ReviewerOutput.cshtml | | | | | _RunActions.cshtml | | | | | _RunHeader.cshtml | | | | | _StatusBanner.cshtml | | | | | _StepsTable.cshtml | | | | Details.cshtml | | | | Details.cshtml.cs | | | Index.cshtml | | | Index.cshtml.cs | +---Nba | | | Games.cshtml | | | Games.cshtml.cs | | | Matchup.cshtml | | | Matchup.cshtml.cs | | | Player.cshtml | | | Player.cshtml.cs | | | Sync.cshtml | | | Sync.cshtml.cs | | | SyncTeamStats.cshtml | | | SyncTeamStats.cshtml.cs | | | Team.cshtml | | | Team.cshtml.cs | | | Teams.cshtml | | | Teams.cshtml.cs | +---Shared | | | _Layout.cshtml | | | _Layout.cshtml.css | | | _ValidationScriptsPartial.cshtml | | _ViewImports.cshtml | | _ViewStart.cshtml | | Error.cshtml | | Error.cshtml.cs | | Index.cshtml | | Index.cshtml.cs | | Privacy.cshtml | | Privacy.cshtml.cs +---Properties | | AssemblyInfo.cs | | launchSettings.json +---wwwroot | +---css | | | poc.css | | | site.css | +---img | | +---nba | | | +---76ers | | | | | logo.png | | | +---allstar | | | | | logo.png | | | +---bucks | | | | | logo.png | | | +---bulls | | | | | logo.png | | | +---cavs | | | | | logo.png | | | +---celtics | | | | | logo.png | | | +---clippers | | | | | logo.png | | | +---grizzlies | | | | | logo.png | | | +---gs | | | | | logo.png | | | +---hawks | | | | | logo.png | | | +---heat | | | | | logo.png | | | +---hornets | | | | | logo.png | | | +---jazz | | | | | logo.png | | | +---kings | | | | | logo.png | | | +---knicks | | | | | logo.png | | | +---lakers | | | | | logo.png | | | +---magic | | | | | logo.png | | | +---mavs | | | | | logo.png | | | +---nets | | | | | logo.png | | | +---nuguets | | | | | logo.png | | | +---pacers | | | | | logo.png | | | +---pelicans | | | | | logo.png | | | +---pistons | | | | | logo.png | | | +---raptors | | | | | logo.png | | | +---rockets | | | | | logo.png | | | +---spurs | | | | | logo.png | | | +---suns | | | | | logo.png | | | +---thunder | | | | | logo.png | | | +---trailblazers | | | | | logo.png | | | +---wizards | | | | | logo.png | | | +---wolves | | | | | logo.png | +---js | | | site.js | +---lib | | +---bootstrap | | | +---dist | | | | +---css | | | | | | bootstrap-grid.css | | | | | | bootstrap-grid.css.map | | | | | | bootstrap-grid.min.css | | | | | | bootstrap-grid.min.css.map | | | | | | bootstrap-grid.rtl.css | | | | | | bootstrap-grid.rtl.css.map | | | | | | bootstrap-grid.rtl.min.css | | | | | | bootstrap-grid.rtl.min.css.map | | | | | | bootstrap-reboot.css | | | | | | bootstrap-reboot.css.map | | | | | | bootstrap-reboot.min.css | | | | | | bootstrap-reboot.min.css.map | | | | | | bootstrap-reboot.rtl.css | | | | | | bootstrap-reboot.rtl.css.map | | | | | | bootstrap-reboot.rtl.min.css | | | | | | bootstrap-reboot.rtl.min.css.map | | | | | | bootstrap-utilities.css | | | | | | bootstrap-utilities.css.map | | | | | | bootstrap-utilities.min.css | | | | | | bootstrap-utilities.min.css.map | | | | | | bootstrap-utilities.rtl.css | | | | | | bootstrap-utilities.rtl.css.map | | | | | | bootstrap-utilities.rtl.min.css | | | | | | bootstrap-utilities.rtl.min.css.map | | | | | | bootstrap.css | | | | | | bootstrap.css.map | | | | | | bootstrap.min.css | | | | | | bootstrap.min.css.map | | | | | | bootstrap.rtl.css | | | | | | bootstrap.rtl.css.map | | | | | | bootstrap.rtl.min.css | | | | | | bootstrap.rtl.min.css.map | | | | +---js | | | | | | bootstrap.bundle.js | | | | | | bootstrap.bundle.js.map | | | | | | bootstrap.bundle.min.js | | | | | | bootstrap.bundle.min.js.map | | | | | | bootstrap.esm.js | | | | | | bootstrap.esm.js.map | | | | | | bootstrap.esm.min.js | | | | | | bootstrap.esm.min.js.map | | | | | | bootstrap.js | | | | | | bootstrap.js.map | | | | | | bootstrap.min.js | | | | | | bootstrap.min.js.map | | | | LICENSE | | +---jquery | | | +---dist | | | | | jquery.js | | | | | jquery.min.js | | | | | jquery.min.map | | | | | jquery.slim.js | | | | | jquery.slim.min.js | | | | | jquery.slim.min.map | | | | LICENSE.txt | | +---jquery-validation | | | +---dist | | | | | additional-methods.js | | | | | additional-methods.min.js | | | | | jquery.validate.js | | | | | jquery.validate.min.js | | | | LICENSE.md | | +---jquery-validation-unobtrusive | | | +---dist | | | | | jquery.validate.unobtrusive.js | | | | | jquery.validate.unobtrusive.min.js | | | | LICENSE.txt | | +---microsoft | | | +---signalr | | | | +---dist | | | | | +---browser | | | | | | | signalr.min.js | | favicon.ico | appsettings.Development.json | appsettings.json | BettingOdds.csproj | libman.json | Program.cs RULES:
Assistant
planner_schema
UTC: 2026-02-20 02:23:47
{"summary":"Add NBA injury snapshot domain model, DbContext mapping, and EF migration. Create domain-only entities (no EF attributes): NbaInjurySnapshotBatch, NbaPlayerInjurySnapshot and enum NbaInjuryStatus. Update AppDbContext with DbSets and fluent mapping (table names, keys, FK with Restrict, column lengths/types, indexes). Generate and apply an EF migration. Validate build and behavior (multiple pulls/day support, index coverage).","plan":["Create enum file Domain/Enum/NbaInjuryStatus.cs containing: Unknown, Available, Probable, Questionable, Doubtful, Out. Keep as plain enum (no EF attributes).","Create domain entity Domain/Entities/NbaInjurySnapshotBatch.cs (no EF attributes). Properties: int Id (identity), string SourceName (max 40), DateTime PulledAtUtc, int? SeasonId, string? Notes, string BatchKey (or BatchGuid) (max 100?). Keep simple POCO.","Create domain entity Domain/Entities/NbaPlayerInjurySnapshot.cs (no EF attributes). Properties: int Id (identity), int BatchId (FK), DateTime AsOfUtc (nullable? use non-null to support indexing; or use Batch PulledAtUtc if omitted), int TeamId, long? PlayerId, string PlayerName, NbaInjuryStatus Status, string? Description, DateTime? ReturnDateUtc, bool IsMapped, string? RawJson, DateTime CreatedUtc. Keep POCO and no EF attributes.","Update Data/AppDbContext.cs:"," - Add DbSet\u003CNbaInjurySnapshotBatch\u003E NbaInjurySnapshotBatches { get; set; }"," - Add DbSet\u003CNbaPlayerInjurySnapshot\u003E NbaPlayerInjurySnapshots { get; set; }"," - In OnModelCreating(ModelBuilder): add fluent configuration for both entities (use modelBuilder.Entity\u003C...\u003E):"," * Map to tables NbaInjurySnapshotBatches and NbaPlayerInjurySnapshots."," * Configure PKs as identity columns (Id)."," * Configure Batch-\u003ESnapshot relationship: NbaInjurySnapshotBatch has many NbaPlayerInjurySnapshot with foreign key BatchId and OnDelete(DeleteBehavior.Restrict)."," * Set string column max lengths: PlayerName 80, SourceName 40, Description 300. For RawJson use nvarchar(max) via HasColumnType(\u0022nvarchar(max)\u0022). (If using other DB provider adapt types accordingly)."," * Store Status as int (use HasConversion\u003Cint\u003E() or HasColumnType(\u0022int\u0022)). Make Status required with default Unknown if desired."," * Ensure datetime properties are treated as UTC by convention; add comments in mapping to indicate UTC-only usage (no runtime enforcement required)."," * Create indexes:"," - On batch: index on (SourceName, PulledAtUtc)."," - On snapshot: index on (TeamId, AsOfUtc)."," - On snapshot: index on (PlayerId, AsOfUtc) (PlayerId nullable \u2014 index still created)."," - On snapshot: unique index to avoid duplicates per batch: choose safe uniqueness (use BatchId \u002B TeamId \u002B PlayerName) because PlayerId may be null/unmapped. Create a unique index on (BatchId, TeamId, PlayerName). Add a comment in mapping explaining the design choice and that PlayerId could optionally be included when IsMapped=true but nullable PlayerId prevents a safe unique constraint on (BatchId, TeamId, PlayerId)."," * Set CreatedUtc default value or require application to populate CreatedUtc. (Prefer application populates it; do not create DB-level default to keep domain simple.)"," * Ensure all property names and types align with domain classes."," - Add using statements for the new domain types in AppDbContext.","Generate EF migration:"," - From repository root run (ensure correct tools and project selection): dotnet ef migrations add AddNbaInjurySnapshots --context AppDbContext -o Migrations"," - Inspect the generated migration to ensure proper types, identity, FK with Restrict, indexes, string lengths, and nvarchar(max) for RawJson.","Apply migration to database (development environment): dotnet ef database update --context AppDbContext","Validation and review steps:"," - Build the solution (dotnet build) to ensure migration and code compile."," - Verify the migration creates both tables and that the FK has ON DELETE RESTRICT semantics."," - Verify indexes exist (SourceName\u002BPulledAtUtc on batches; TeamId\u002BAsOfUtc and PlayerId\u002BAsOfUtc on snapshots; unique BatchId\u002BTeamId\u002BPlayerName index)."," - Validate model supports multiple pulls/day: PulledAtUtc on batch is a full datetime (not date-only), snapshots include AsOfUtc so multiple batches/snapshots per day are supported and indexed for queries by time."," - Confirm domain layer has no EF dependencies (no EF Core attributes/usings added to domain files)."," - Confirm AppDbContext is not referenced by Pages directly (keep existing architecture), and all UTC semantics are documented and used (properties named ...Utc).","Prepare PatchSet:"," - Commit new files: Domain/Enum/NbaInjuryStatus.cs, Domain/Entities/NbaInjurySnapshotBatch.cs, Domain/Entities/NbaPlayerInjurySnapshot.cs."," - Commit modified file: Data/AppDbContext.cs (DbSets \u002B OnModelCreating additions)."," - Commit generated migration files under Migrations (migration .cs and .Designer.cs), and AppDbContextModelSnapshot.cs updated by EF if applicable."," - Push the PatchSet for reviewer.","Notes and considerations for reviewer:"," - Unique constraint uses PlayerName to remain safe when PlayerId is null/unmapped. If later the integration ensures PlayerId is always set when IsMapped=true, consider adding a filtered unique index (where PlayerId is not null) to enforce uniqueness on (BatchId, TeamId, PlayerId) for mapped rows but that\u0027s DB-provider dependent and requires additional migration work."," - RawJson is stored as nvarchar(max) to allow full payload persistence; if JSON column type is preferred for a specific DB (e.g., PostgreSQL jsonb), adjust HasColumnType accordingly during implementation."," - All datetime fields are named ...Utc to make UTC usage explicit; application code should set UTC times (DateTime.UtcNow) when creating batch/snapshot rows."]}
|
||||||
| Implementer | Running | 2026-02-19 18:23:47 | — | — | gpt-5-mini | 1 |
|
Step messages
User
implementer_schema
UTC: 2026-02-20 02:23:47
ATTEMPT 1/3
TASK:
TASK: Implement NBA Injury storage schema (snapshots, enterprise-grade) using EF migrations.
Requirements
• Create Domain entities only (no EF attributes) for injuries:
o NbaInjurySnapshotBatch (identity PK; SourceName, PulledAtUtc, optional SeasonId, optional Notes, BatchGuid or BatchKey string)
o NbaPlayerInjurySnapshot (identity PK; FK to batch; AsOfUtc (or use batch PulledAtUtc); TeamId int; nullable PlayerId long?; PlayerName string; Status enum; Description string?; ReturnDateUtc DateTime?; IsMapped bool; RawJson string?; CreatedUtc)
o Enum NbaInjuryStatus (Unknown, Available, Probable, Questionable, Doubtful, Out)
• Update AppDbContext:
o Add DbSets
o Configure tables, keys, FK batch→snapshots (Restrict)
o String max lengths (PlayerName 80, SourceName 40, Status as int, Description 300, RawJson max)
o Indexes:
Batch: (SourceName, PulledAtUtc)
Snapshot: (TeamId, AsOfUtc) and (PlayerId, AsOfUtc) (PlayerId nullable -> still index)
Snapshot unique: (BatchId, TeamId, PlayerName) (or (BatchId, TeamId, PlayerId) if mapped) — choose safe uniqueness
• Add migration + update DB
Constraints
• Domain doesn’t depend on anything.
• DbContext not used from Pages.
• UTC everywhere.
Deliverable
• PatchSet containing entity files + DbContext mapping + migration.
Reviewer must validate
• Snapshot model supports multiple pulls/day
• Indexes are sane and scalable
• Migration builds
PLAN:
- Create enum file Domain/Enum/NbaInjuryStatus.cs containing: Unknown, Available, Probable, Questionable, Doubtful, Out. Keep as plain enum (no EF attributes).
- Create domain entity Domain/Entities/NbaInjurySnapshotBatch.cs (no EF attributes). Properties: int Id (identity), string SourceName (max 40), DateTime PulledAtUtc, int? SeasonId, string? Notes, string BatchKey (or BatchGuid) (max 100?). Keep simple POCO.
- Create domain entity Domain/Entities/NbaPlayerInjurySnapshot.cs (no EF attributes). Properties: int Id (identity), int BatchId (FK), DateTime AsOfUtc (nullable? use non-null to support indexing; or use Batch PulledAtUtc if omitted), int TeamId, long? PlayerId, string PlayerName, NbaInjuryStatus Status, string? Description, DateTime? ReturnDateUtc, bool IsMapped, string? RawJson, DateTime CreatedUtc. Keep POCO and no EF attributes.
- Update Data/AppDbContext.cs:
- - Add DbSet<NbaInjurySnapshotBatch> NbaInjurySnapshotBatches { get; set; }
- - Add DbSet<NbaPlayerInjurySnapshot> NbaPlayerInjurySnapshots { get; set; }
- - In OnModelCreating(ModelBuilder): add fluent configuration for both entities (use modelBuilder.Entity<...>):
- * Map to tables NbaInjurySnapshotBatches and NbaPlayerInjurySnapshots.
- * Configure PKs as identity columns (Id).
- * Configure Batch->Snapshot relationship: NbaInjurySnapshotBatch has many NbaPlayerInjurySnapshot with foreign key BatchId and OnDelete(DeleteBehavior.Restrict).
- * Set string column max lengths: PlayerName 80, SourceName 40, Description 300. For RawJson use nvarchar(max) via HasColumnType("nvarchar(max)"). (If using other DB provider adapt types accordingly).
- * Store Status as int (use HasConversion<int>() or HasColumnType("int")). Make Status required with default Unknown if desired.
- * Ensure datetime properties are treated as UTC by convention; add comments in mapping to indicate UTC-only usage (no runtime enforcement required).
- * Create indexes:
- - On batch: index on (SourceName, PulledAtUtc).
- - On snapshot: index on (TeamId, AsOfUtc).
- - On snapshot: index on (PlayerId, AsOfUtc) (PlayerId nullable — index still created).
- - On snapshot: unique index to avoid duplicates per batch: choose safe uniqueness (use BatchId + TeamId + PlayerName) because PlayerId may be null/unmapped. Create a unique index on (BatchId, TeamId, PlayerName). Add a comment in mapping explaining the design choice and that PlayerId could optionally be included when IsMapped=true but nullable PlayerId prevents a safe unique constraint on (BatchId, TeamId, PlayerId).
- * Set CreatedUtc default value or require application to populate CreatedUtc. (Prefer application populates it; do not create DB-level default to keep domain simple.)
- * Ensure all property names and types align with domain classes.
- - Add using statements for the new domain types in AppDbContext.
- Generate EF migration:
- - From repository root run (ensure correct tools and project selection): dotnet ef migrations add AddNbaInjurySnapshots --context AppDbContext -o Migrations
- - Inspect the generated migration to ensure proper types, identity, FK with Restrict, indexes, string lengths, and nvarchar(max) for RawJson.
- Apply migration to database (development environment): dotnet ef database update --context AppDbContext
- Validation and review steps:
- - Build the solution (dotnet build) to ensure migration and code compile.
- - Verify the migration creates both tables and that the FK has ON DELETE RESTRICT semantics.
- - Verify indexes exist (SourceName+PulledAtUtc on batches; TeamId+AsOfUtc and PlayerId+AsOfUtc on snapshots; unique BatchId+TeamId+PlayerName index).
- - Validate model supports multiple pulls/day: PulledAtUtc on batch is a full datetime (not date-only), snapshots include AsOfUtc so multiple batches/snapshots per day are supported and indexed for queries by time.
- - Confirm domain layer has no EF dependencies (no EF Core attributes/usings added to domain files).
- - Confirm AppDbContext is not referenced by Pages directly (keep existing architecture), and all UTC semantics are documented and used (properties named ...Utc).
- Prepare PatchSet:
- - Commit new files: Domain/Enum/NbaInjuryStatus.cs, Domain/Entities/NbaInjurySnapshotBatch.cs, Domain/Entities/NbaPlayerInjurySnapshot.cs.
- - Commit modified file: Data/AppDbContext.cs (DbSets + OnModelCreating additions).
- - Commit generated migration files under Migrations (migration .cs and .Designer.cs), and AppDbContextModelSnapshot.cs updated by EF if applicable.
- - Push the PatchSet for reviewer.
- Notes and considerations for reviewer:
- - Unique constraint uses PlayerName to remain safe when PlayerId is null/unmapped. If later the integration ensures PlayerId is always set when IsMapped=true, consider adding a filtered unique index (where PlayerId is not null) to enforce uniqueness on (BatchId, TeamId, PlayerId) for mapped rows but that's DB-provider dependent and requires additional migration work.
- - RawJson is stored as nvarchar(max) to allow full payload persistence; if JSON column type is preferred for a specific DB (e.g., PostgreSQL jsonb), adjust HasColumnType accordingly during implementation.
- - All datetime fields are named ...Utc to make UTC usage explicit; application code should set UTC times (DateTime.UtcNow) when creating batch/snapshot rows.
SOLUTION TREE:
BettingOdds
+---App
| +---Agents
| | +---Autopilot
| | | | AgentAutopilotRunner.cs
| | | | AutopilotOptions.cs
| | +---Git
| | | | GitRunner.cs
| | +---Guard
| | | | GuardEvaluator.cs
| | | | GuardPolicyOptions.cs
| | +---Live
| | | | AaiRunHub.cs
| | | | BackgroundTaskQueue.cs
| | | | IAaiLiveNotifier.cs
| | | | QueuedHostedService.cs
| | | | SignalRAaiLiveNotifier.cs
| | +---Models
| | | | AgentPatch.cs
| | | | AgentPolicyGuard.cs
| | | | AgentRunContext.cs
| | | | AgentRunResult.cs
| | | | IAgentPolicyGuard.cs
| | +---Patching
| | | | PatchNormalization.cs
| | | | PatchPreflightValidator.cs
| | | | PreflightPolicyOptions.cs
| | | | UnifiedDiffBuilder.cs
| | +---RunLaunch
| | | | AaiRunLauncher.cs
| | +---Steps
| | | +---Models
| | | | | ImplementerInput.cs
| | | | | PlannerInput.cs
| | | | | ReviewerInput.cs
| | | | AgentJson.cs
| | | | IAgentStep.cs
| | | | ImplementerAgent.cs
| | | | PlannerAgent.cs
| | | | ReviewerAgent.cs
| | +---Tests
| | | | TestAuthorAgent.cs
| | | AaiRunWriter.cs
| | | AgentOrchestrator.cs
| | | AgentPolicyOptions.cs
| | | CommandPolicy.cs
| | | DiffApplier.cs
| | | DotnetRunner.cs
| | | OpenAiOptions.cs
| | | OpenAiResponsesClient.cs
| | | RepoFileService.cs
| +---Dtos
| | | NbaTeamAdvancedRow.cs
| +---Pricing
| | | FairLinesEngine.cs
| | | FairLinesMath.cs
| | | FairLinesOptions.cs
| | | FairLinesResult.cs
| | | IFairLinesEngine.cs
| | | IStartingLineupResolver.cs
| | | NbaProjectionService.cs
| | | ProjectionResult.cs
| | | StartingLineupResolver.cs
| +---Queries
| | +---Agents
| | | | AaiRunsQuery.cs
| | +---Games
| | | | GameRow.cs
| | | | GamesQuery.cs
| | | | IGamesQuery.cs
| | +---Matchup
| | | | IMatchupQuery.cs
| | | | MatchupQuery.cs
| | | | MatchupVm.cs
| | +---Player
| | | | IPlayerQuery.cs
| | | | PlayerQuery.cs
| | +---Teams
| | | | ISyncTeamStatsQuery.cs
| | | | ITeamQuery.cs
| | | | ITeamsQuery.cs
| | | | SyncTeamStatsQuery.cs
| | | | TeamQuery.cs
| | | | TeamsQuery.cs
| +---Sync
| | +---GameSync
| | | | GameSyncOrchestrator.cs
| | | | IGameSyncOrchestrator.cs
| | +---Modules
| | | +---Impl
| | | | | PlayerSnapshotBuilder.cs
| | | | | PlayerSync.cs
| | | | | ScheduleSync.cs
| | | | | TeamStatsSync.cs
| | | | IPlayerSnapshotBuilder.cs
| | | | IPlayerSync.cs
| | | | IScheduleSync.cs
| | | | ITeamStatsSync.cs
| | | ISeasonResolver.cs
| | | ISyncCenter.cs
| | | NbaSeedService.cs
| | | SeasonResolver.cs
| | | SyncCenter.cs
| | | SyncOptions.cs
| | | SyncResult.cs
| | | SyncTeamStatsUseCase.cs
| +---Time
| | | CostaRicaAppClock.cs
| | | IAppClock.cs
+---Data
| | AppDbContext.cs
| | AppDbContextFactory.cs
+---docs
| | AGENT_EF_PATTERN.md
| | AGENT_RULES.md
| | AGENTS.md
| | ARCHITECTURE.md
| | CODING_RULES.md
| | CODING_STANDARDS.md
| | PR_TEMPLATE.md
| | PRICING_MODEL_CONTRACT.md
| | ROADMAP.md
| | TASKS.md
+---Domain
| +---Entities
| | +---Agents
| | | +---Enums
| | | | | AaiApplyResult.cs
| | | | | AaiDotnetExecutionType.cs
| | | | | AaiGuardSeverity.cs
| | | | | AaiMessageRole.cs
| | | | | AaiRunStatus.cs
| | | | | AaiStepStatus.cs
| | | | | AaiStepType.cs
| | | | AaiAgentMessage.cs
| | | | AaiAgentPatch.cs
| | | | AaiAgentPatchSet.cs
| | | | AaiAgentRun.cs
| | | | AaiAgentRunStep.cs
| | | | AaiDotnetExecution.cs
| | | | AaiGuardReport.cs
| | | | AaiGuardViolation.cs
| | | | AaiPatchApplyAttempt.cs
| | | | AaiPatchPreflightReport.cs
| | | | AaiPatchPreflightViolation.cs
| | | | AaiRepoFileSnapshot.cs
| | | NbaGame.cs
| | | NbaPlayer.cs
| | | NbaPlayerGameStat.cs
| | | NbaPlayerRelevanceSnapshot.cs
| | | NbaPlayerRollingStatsSnapshot.cs
| | | NbaPlayerTeam.cs
| | | NbaSeason.cs
| | | NbaTeam.cs
| | | NbaTeamStatsSnapshot.cs
| +---Enum
| +---ValueObjects
+---Infrastructure
| +---NbaStats
| | | NbaScheduleClient.cs
| | | NbaStatsClient.cs
| | | NbaStatsService.cs
| +---Time
+---Migrations
| | 20260212223141_Init.cs
| | 20260212223141_Init.Designer.cs
| | 20260213025511_NbaSchemaV1.cs
| | 20260213025511_NbaSchemaV1.Designer.cs
| | 20260218153318_Reconcile_NbaTeams_LogoUrl.cs
| | 20260218153318_Reconcile_NbaTeams_LogoUrl.Designer.cs
| | 20260218153704_AddAaiAgenticSchema.cs
| | 20260218153704_AddAaiAgenticSchema.Designer.cs
| | 20260218154538_Repair_AaiAgenticSchema.cs
| | 20260218154538_Repair_AaiAgenticSchema.Designer.cs
| | 20260218154908_Repair_AaiAgenticSchema2.cs
| | 20260218154908_Repair_AaiAgenticSchema2.Designer.cs
| | 20260218224121_AddAaiPatchPreflight.cs
| | 20260218224121_AddAaiPatchPreflight.Designer.cs
| | 20260218231615_Aai_ApplyAttempt_CommitFields.cs
| | 20260218231615_Aai_ApplyAttempt_CommitFields.Designer.cs
| | 20260219003652_Aai_Latest_Updates.cs
| | 20260219003652_Aai_Latest_Updates.Designer.cs
| | AppDbContextModelSnapshot.cs
+---Pages
| +---Agents
| | +---Runs
| | | +---Partials
| | | | | _AutopilotTimeline.cshtml
| | | | | _ExecutionsCard.cshtml
| | | | | _GuardCard.cshtml
| | | | | _LivePipeline.cshtml
| | | | | _LivePipelineScripts.cshtml
| | | | | _OverviewAndStats.cshtml
| | | | | _PatchSetsCard.cshtml
| | | | | _RepoSnapshotsCard.cshtml
| | | | | _ReviewerOutput.cshtml
| | | | | _RunActions.cshtml
| | | | | _RunHeader.cshtml
| | | | | _StatusBanner.cshtml
| | | | | _StepsTable.cshtml
| | | | Details.cshtml
| | | | Details.cshtml.cs
| | | Index.cshtml
| | | Index.cshtml.cs
| +---Nba
| | | Games.cshtml
| | | Games.cshtml.cs
| | | Matchup.cshtml
| | | Matchup.cshtml.cs
| | | Player.cshtml
| | | Player.cshtml.cs
| | | Sync.cshtml
| | | Sync.cshtml.cs
| | | SyncTeamStats.cshtml
| | | SyncTeamStats.cshtml.cs
| | | Team.cshtml
| | | Team.cshtml.cs
| | | Teams.cshtml
| | | Teams.cshtml.cs
| +---Shared
| | | _Layout.cshtml
| | | _Layout.cshtml.css
| | | _ValidationScriptsPartial.cshtml
| | _ViewImports.cshtml
| | _ViewStart.cshtml
| | Error.cshtml
| | Error.cshtml.cs
| | Index.cshtml
| | Index.cshtml.cs
| | Privacy.cshtml
| | Privacy.cshtml.cs
+---Properties
| | AssemblyInfo.cs
| | launchSettings.json
+---wwwroot
| +---css
| | | poc.css
| | | site.css
| +---img
| | +---nba
| | | +---76ers
| | | | | logo.png
| | | +---allstar
| | | | | logo.png
| | | +---bucks
| | | | | logo.png
| | | +---bulls
| | | | | logo.png
| | | +---cavs
| | | | | logo.png
| | | +---celtics
| | | | | logo.png
| | | +---clippers
| | | | | logo.png
| | | +---grizzlies
| | | | | logo.png
| | | +---gs
| | | | | logo.png
| | | +---hawks
| | | | | logo.png
| | | +---heat
| | | | | logo.png
| | | +---hornets
| | | | | logo.png
| | | +---jazz
| | | | | logo.png
| | | +---kings
| | | | | logo.png
| | | +---knicks
| | | | | logo.png
| | | +---lakers
| | | | | logo.png
| | | +---magic
| | | | | logo.png
| | | +---mavs
| | | | | logo.png
| | | +---nets
| | | | | logo.png
| | | +---nuguets
| | | | | logo.png
| | | +---pacers
| | | | | logo.png
| | | +---pelicans
| | | | | logo.png
| | | +---pistons
| | | | | logo.png
| | | +---raptors
| | | | | logo.png
| | | +---rockets
| | | | | logo.png
| | | +---spurs
| | | | | logo.png
| | | +---suns
| | | | | logo.png
| | | +---thunder
| | | | | logo.png
| | | +---trailblazers
| | | | | logo.png
| | | +---wizards
| | | | | logo.png
| | | +---wolves
| | | | | logo.png
| +---js
| | | site.js
| +---lib
| | +---bootstrap
| | | +---dist
| | | | +---css
| | | | | | bootstrap-grid.css
| | | | | | bootstrap-grid.css.map
| | | | | | bootstrap-grid.min.css
| | | | | | bootstrap-grid.min.css.map
| | | | | | bootstrap-grid.rtl.css
| | | | | | bootstrap-grid.rtl.css.map
| | | | | | bootstrap-grid.rtl.min.css
| | | | | | bootstrap-grid.rtl.min.css.map
| | | | | | bootstrap-reboot.css
| | | | | | bootstrap-reboot.css.map
| | | | | | bootstrap-reboot.min.css
| | | | | | bootstrap-reboot.min.css.map
| | | | | | bootstrap-reboot.rtl.css
| | | | | | bootstrap-reboot.rtl.css.map
| | | | | | bootstrap-reboot.rtl.min.css
| | | | | | bootstrap-reboot.rtl.min.css.map
| | | | | | bootstrap-utilities.css
| | | | | | bootstrap-utilities.css.map
| | | | | | bootstrap-utilities.min.css
| | | | | | bootstrap-utilities.min.css.map
| | | | | | bootstrap-utilities.rtl.css
| | | | | | bootstrap-utilities.rtl.css.map
| | | | | | bootstrap-utilities.rtl.min.css
| | | | | | bootstrap-utilities.rtl.min.css.map
| | | | | | bootstrap.css
| | | | | | bootstrap.css.map
| | | | | | bootstrap.min.css
| | | | | | bootstrap.min.css.map
| | | | | | bootstrap.rtl.css
| | | | | | bootstrap.rtl.css.map
| | | | | | bootstrap.rtl.min.css
| | | | | | bootstrap.rtl.min.css.map
| | | | +---js
| | | | | | bootstrap.bundle.js
| | | | | | bootstrap.bundle.js.map
| | | | | | bootstrap.bundle.min.js
| | | | | | bootstrap.bundle.min.js.map
| | | | | | bootstrap.esm.js
| | | | | | bootstrap.esm.js.map
| | | | | | bootstrap.esm.min.js
| | | | | | bootstrap.esm.min.js.map
| | | | | | bootstrap.js
| | | | | | bootstrap.js.map
| | | | | | bootstrap.min.js
| | | | | | bootstrap.min.js.map
| | | | LICENSE
| | +---jquery
| | | +---dist
| | | | | jquery.js
| | | | | jquery.min.js
| | | | | jquery.min.map
| | | | | jquery.slim.js
| | | | | jquery.slim.min.js
| | | | | jquery.slim.min.map
| | | | LICENSE.txt
| | +---jquery-validation
| | | +---dist
| | | | | additional-methods.js
| | | | | additional-methods.min.js
| | | | | jquery.validate.js
| | | | | jquery.validate.min.js
| | | | LICENSE.md
| | +---jquery-validation-unobtrusive
| | | +---dist
| | | | | jquery.validate.unobtrusive.js
| | | | | jquery.validate.unobtrusive.min.js
| | | | LICENSE.txt
| | +---microsoft
| | | +---signalr
| | | | +---dist
| | | | | +---browser
| | | | | | | signalr.min.js
| | favicon.ico
| appsettings.Development.json
| appsettings.json
| BettingOdds.csproj
| libman.json
| Program.cs
RULES:
FILES:
# FILE: Migrations/20260212223141_Init.cs
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BettingOdds.Migrations
{
/// <inheritdoc />
public partial class Init : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "NbaTeamStatSnapshots",
columns: table => new
{
NbaTeamStatSnapshotId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Season = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
SeasonType = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
LastNGames = table.Column<int>(type: "int", nullable: false),
TeamId = table.Column<int>(type: "int", nullable: false),
TeamName = table.Column<string>(type: "nvarchar(80)", maxLength: 80, nullable: false),
OffRtg = table.Column<decimal>(type: "decimal(7,3)", nullable: false),
DefRtg = table.Column<decimal>(type: "decimal(7,3)", nullable: false),
Pace = table.Column<decimal>(type: "decimal(7,3)", nullable: false),
NetRtg = table.Column<decimal>(type: "decimal(7,3)", nullable: false),
PulledAtUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaTeamStatSnapshots", x => x.NbaTeamStatSnapshotId);
});
migrationBuilder.CreateIndex(
name: "IX_NbaTeamStatSnapshots_Season_SeasonType_LastNGames_TeamId_PulledAtUtc",
table: "NbaTeamStatSnapshots",
columns: new[] { "Season", "SeasonType", "LastNGames", "TeamId", "PulledAtUtc" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "NbaTeamStatSnapshots");
}
}
}
# FILE: Migrations/20260212223141_Init.Designer.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260212223141_Init")]
partial class Init
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Models.NbaTeamStatSnapshot", b =>
{
b.Property<int>("NbaTeamStatSnapshotId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("NbaTeamStatSnapshotId"));
b.Property<decimal>("DefRtg")
.HasColumnType("decimal(7,3)");
b.Property<int>("LastNGames")
.HasColumnType("int");
b.Property<decimal>("NetRtg")
.HasColumnType("decimal(7,3)");
b.Property<decimal>("OffRtg")
.HasColumnType("decimal(7,3)");
b.Property<decimal>("Pace")
.HasColumnType("decimal(7,3)");
b.Property<DateTime>("PulledAtUtc")
.HasColumnType("datetime2");
b.Property<string>("Season")
.IsRequired()
.HasMaxLength(16)
.HasColumnType("nvarchar(16)");
b.Property<string>("SeasonType")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.Property<int>("TeamId")
.HasColumnType("int");
b.Property<string>("TeamName")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("nvarchar(80)");
b.HasKey("NbaTeamStatSnapshotId");
b.HasIndex("Season", "SeasonType", "LastNGames", "TeamId", "PulledAtUtc");
b.ToTable("NbaTeamStatSnapshots");
});
#pragma warning restore 612, 618
}
}
}
# FILE: Migrations/20260213025511_NbaSchemaV1.cs
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BettingOdds.Migrations
{
/// <inheritdoc />
public partial class NbaSchemaV1 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "NbaTeamStatSnapshots");
migrationBuilder.EnsureSchema(
name: "dbo");
migrationBuilder.CreateTable(
name: "NbaPlayers",
schema: "dbo",
columns: table => new
{
PlayerId = table.Column<int>(type: "int", nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(80)", maxLength: 80, nullable: false),
FirstName = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: false),
LastName = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaPlayers", x => x.PlayerId);
});
migrationBuilder.CreateTable(
name: "NbaSeasons",
schema: "dbo",
columns: table => new
{
SeasonId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
SeasonCode = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
SeasonType = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaSeasons", x => x.SeasonId);
});
migrationBuilder.CreateTable(
name: "NbaTeams",
schema: "dbo",
columns: table => new
{
TeamId = table.Column<int>(type: "int", nullable: false),
Abbr = table.Column<string>(type: "nvarchar(6)", maxLength: 6, nullable: false),
City = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: false),
Name = table.Column<string>(type: "nvarchar(60)", maxLength: 60, nullable: false),
FullName = table.Column<string>(type: "nvarchar(80)", maxLength: 80, nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaTeams", x => x.TeamId);
});
migrationBuilder.CreateTable(
name: "NbaGames",
schema: "dbo",
columns: table => new
{
GameId = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
SeasonId = table.Column<int>(type: "int", nullable: false),
GameDateUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
Status = table.Column<string>(type: "nvarchar(30)", maxLength: 30, nullable: false),
HomeTeamId = table.Column<int>(type: "int", nullable: false),
AwayTeamId = table.Column<int>(type: "int", nullable: false),
HomeScore = table.Column<int>(type: "int", nullable: true),
AwayScore = table.Column<int>(type: "int", nullable: true),
Arena = table.Column<string>(type: "nvarchar(120)", maxLength: 120, nullable: true),
LastSyncedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaGames", x => x.GameId);
table.ForeignKey(
name: "FK_NbaGames_NbaSeasons_SeasonId",
column: x => x.SeasonId,
principalSchema: "dbo",
principalTable: "NbaSeasons",
principalColumn: "SeasonId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_NbaGames_NbaTeams_AwayTeamId",
column: x => x.AwayTeamId,
principalSchema: "dbo",
principalTable: "NbaTeams",
principalColumn: "TeamId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_NbaGames_NbaTeams_HomeTeamId",
column: x => x.HomeTeamId,
principalSchema: "dbo",
principalTable: "NbaTeams",
principalColumn: "TeamId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "NbaPlayerRelevanceSnapshots",
schema: "dbo",
columns: table => new
{
PlayerRelevanceSnapshotId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
SeasonId = table.Column<int>(type: "int", nullable: false),
TeamId = table.Column<int>(type: "int", nullable: false),
PlayerId = table.Column<int>(type: "int", nullable: false),
AsOfUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
RelevanceScore = table.Column<decimal>(type: "decimal(6,2)", nullable: false),
RelevanceTier = table.Column<byte>(type: "tinyint", nullable: false),
MinutesSharePct = table.Column<decimal>(type: "decimal(6,2)", nullable: true),
UsageProxy = table.Column<decimal>(type: "decimal(6,2)", nullable: true),
AvailabilityFactor = table.Column<decimal>(type: "decimal(6,4)", nullable: true),
RecentMinutesAvg = table.Column<decimal>(type: "decimal(7,2)", nullable: true),
BatchId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaPlayerRelevanceSnapshots", x => x.PlayerRelevanceSnapshotId);
table.ForeignKey(
name: "FK_NbaPlayerRelevanceSnapshots_NbaPlayers_PlayerId",
column: x => x.PlayerId,
principalSchema: "dbo",
principalTable: "NbaPlayers",
principalColumn: "PlayerId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_NbaPlayerRelevanceSnapshots_NbaSeasons_SeasonId",
column: x => x.SeasonId,
principalSchema: "dbo",
principalTable: "NbaSeasons",
principalColumn: "SeasonId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_NbaPlayerRelevanceSnapshots_NbaTeams_TeamId",
column: x => x.TeamId,
principalSchema: "dbo",
principalTable: "NbaTeams",
principalColumn: "TeamId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "NbaPlayerRollingStatsSnapshots",
schema: "dbo",
columns: table => new
{
SnapshotId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
BatchId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
SeasonId = table.Column<int>(type: "int", nullable: false),
TeamId = table.Column<int>(type: "int", nullable: false),
PlayerId = table.Column<int>(type: "int", nullable: false),
LastNGames = table.Column<int>(type: "int", nullable: false),
AsOfUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
MinutesAvg = table.Column<decimal>(type: "decimal(7,2)", nullable: false),
PointsAvg = table.Column<decimal>(type: "decimal(7,2)", nullable: false),
ReboundsAvg = table.Column<decimal>(type: "decimal(7,2)", nullable: false),
AssistsAvg = table.Column<decimal>(type: "decimal(7,2)", nullable: false),
TurnoversAvg = table.Column<decimal>(type: "decimal(7,2)", nullable: false),
TS = table.Column<decimal>(type: "decimal(6,4)", nullable: false),
ThreePpct = table.Column<decimal>(type: "decimal(6,4)", nullable: false),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaPlayerRollingStatsSnapshots", x => x.SnapshotId);
table.ForeignKey(
name: "FK_NbaPlayerRollingStatsSnapshots_NbaPlayers_PlayerId",
column: x => x.PlayerId,
principalSchema: "dbo",
principalTable: "NbaPlayers",
principalColumn: "PlayerId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_NbaPlayerRollingStatsSnapshots_NbaSeasons_SeasonId",
column: x => x.SeasonId,
principalSchema: "dbo",
principalTable: "NbaSeasons",
principalColumn: "SeasonId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_NbaPlayerRollingStatsSnapshots_NbaTeams_TeamId",
column: x => x.TeamId,
principalSchema: "dbo",
principalTable: "NbaTeams",
principalColumn: "TeamId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "NbaPlayerTeams",
schema: "dbo",
columns: table => new
{
PlayerTeamId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
PlayerId = table.Column<int>(type: "int", nullable: false),
TeamId = table.Column<int>(type: "int", nullable: false),
SeasonId = table.Column<int>(type: "int", nullable: false),
RosterStatus = table.Column<string>(type: "nvarchar(max)", nullable: true),
JerseyNumber = table.Column<string>(type: "nvarchar(max)", nullable: true),
DepthOrder = table.Column<int>(type: "int", nullable: true),
StartDateUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
EndDateUtc = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaPlayerTeams", x => x.PlayerTeamId);
table.ForeignKey(
name: "FK_NbaPlayerTeams_NbaPlayers_PlayerId",
column: x => x.PlayerId,
principalSchema: "dbo",
principalTable: "NbaPlayers",
principalColumn: "PlayerId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_NbaPlayerTeams_NbaSeasons_SeasonId",
column: x => x.SeasonId,
principalSchema: "dbo",
principalTable: "NbaSeasons",
principalColumn: "SeasonId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_NbaPlayerTeams_NbaTeams_TeamId",
column: x => x.TeamId,
principalSchema: "dbo",
principalTable: "NbaTeams",
principalColumn: "TeamId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "NbaTeamStatsSnapshots",
schema: "dbo",
columns: table => new
{
SnapshotId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
BatchId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
PulledAtUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
SeasonId = table.Column<int>(type: "int", nullable: false),
TeamId = table.Column<int>(type: "int", nullable: false),
LastNGames = table.Column<int>(type: "int", nullable: false),
OffRtg = table.Column<decimal>(type: "decimal(7,3)", nullable: false),
DefRtg = table.Column<decimal>(type: "decimal(7,3)", nullable: false),
Pace = table.Column<decimal>(type: "decimal(7,3)", nullable: false),
NetRtg = table.Column<decimal>(type: "decimal(7,3)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaTeamStatsSnapshots", x => x.SnapshotId);
table.ForeignKey(
name: "FK_NbaTeamStatsSnapshots_NbaSeasons_SeasonId",
column: x => x.SeasonId,
principalSchema: "dbo",
principalTable: "NbaSeasons",
principalColumn: "SeasonId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_NbaTeamStatsSnapshots_NbaTeams_TeamId",
column: x => x.TeamId,
principalSchema: "dbo",
principalTable: "NbaTeams",
principalColumn: "TeamId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "NbaPlayerGameStats",
schema: "dbo",
columns: table => new
{
PlayerGameStatId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
GameId = table.Column<string>(type: "nvarchar(20)", nullable: false),
PlayerId = table.Column<int>(type: "int", nullable: false),
TeamId = table.Column<int>(type: "int", nullable: false),
DidStart = table.Column<bool>(type: "bit", nullable: false),
Minutes = table.Column<decimal>(type: "decimal(5,2)", nullable: false),
Points = table.Column<int>(type: "int", nullable: false),
Rebounds = table.Column<int>(type: "int", nullable: false),
Assists = table.Column<int>(type: "int", nullable: false),
Steals = table.Column<int>(type: "int", nullable: false),
Blocks = table.Column<int>(type: "int", nullable: false),
Turnovers = table.Column<int>(type: "int", nullable: false),
Fouls = table.Column<int>(type: "int", nullable: false),
FGM = table.Column<int>(type: "int", nullable: false),
FGA = table.Column<int>(type: "int", nullable: false),
TPM = table.Column<int>(type: "int", nullable: false),
TPA = table.Column<int>(type: "int", nullable: false),
FTM = table.Column<int>(type: "int", nullable: false),
FTA = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NbaPlayerGameStats", x => x.PlayerGameStatId);
table.ForeignKey(
name: "FK_NbaPlayerGameStats_NbaGames_GameId",
column: x => x.GameId,
principalSchema: "dbo",
principalTable: "NbaGames",
principalColumn: "GameId",
onDelete: Referentia
/* ... truncated for agent context ... */
# FILE: Migrations/20260213025511_NbaSchemaV1.Designer.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260213025511_NbaSchemaV1")]
partial class NbaSchemaV1
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("dbo")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Models.NbaGame", b =>
{
b.Property<string>("GameId")
.HasMaxLength(20)
.HasColumnType("nvarchar(20)");
b.Property<string>("Arena")
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<int?>("AwayScore")
.HasColumnType("int");
b.Property<int>("AwayTeamId")
.HasColumnType("int");
b.Property<DateTime>("GameDateUtc")
.HasColumnType("datetime2");
b.Property<int?>("HomeScore")
.HasColumnType("int");
b.Property<int>("HomeTeamId")
.HasColumnType("int");
b.Property<DateTime>("LastSyncedUtc")
.HasColumnType("datetime2");
b.Property<int>("SeasonId")
.HasColumnType("int");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.HasKey("GameId");
b.HasIndex("GameDateUtc");
b.HasIndex("AwayTeamId", "GameDateUtc");
b.HasIndex("HomeTeamId", "GameDateUtc");
b.HasIndex("SeasonId", "GameDateUtc");
b.ToTable("NbaGames", "dbo");
});
modelBuilder.Entity("BettingOdds.Models.NbaPlayer", b =>
{
b.Property<int>("PlayerId")
.HasColumnType("int");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("nvarchar(80)");
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)");
b.HasKey("PlayerId");
b.HasIndex("DisplayName");
b.ToTable("NbaPlayers", "dbo");
});
modelBuilder.Entity("BettingOdds.Models.NbaPlayerGameStat", b =>
{
b.Property<long>("PlayerGameStatId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("PlayerGameStatId"));
b.Property<int>("Assists")
.HasColumnType("int");
b.Property<int>("Blocks")
.HasColumnType("int");
b.Property<bool>("DidStart")
.HasColumnType("bit");
b.Property<int>("FGA")
.HasColumnType("int");
b.Property<int>("FGM")
.HasColumnType("int");
b.Property<int>("FTA")
.HasColumnType("int");
b.Property<int>("FTM")
.HasColumnType("int");
b.Property<int>("Fouls")
.HasColumnType("int");
b.Property<string>("GameId")
.IsRequired()
.HasColumnType("nvarchar(20)");
b.Property<decimal>("Minutes")
.HasColumnType("decimal(5,2)");
b.Property<int>("PlayerId")
.HasColumnType("int");
b.Property<int>("Points")
.HasColumnType("int");
b.Property<int>("Rebounds")
.HasColumnType("int");
b.Property<int>("Steals")
.HasColumnType("int");
b.Property<int>("TPA")
.HasColumnType("int");
b.Property<int>("TPM")
.HasColumnType("int");
b.Property<int>("TeamId")
.HasColumnType("int");
b.Property<int>("Turnovers")
.HasColumnType("int");
b.HasKey("PlayerGameStatId");
b.HasIndex("PlayerId");
b.HasIndex("TeamId");
b.HasIndex("GameId", "PlayerId")
.IsUnique();
b.ToTable("NbaPlayerGameStats", "dbo");
});
modelBuilder.Entity("BettingOdds.Models.NbaPlayerRelevanceSnapshot", b =>
{
b.Property<long>("PlayerRelevanceSnapshotId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("PlayerRelevanceSnapshotId"));
b.Property<DateTime>("AsOfUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("AvailabilityFactor")
.HasColumnType("decimal(6,4)");
b.Property<Guid>("BatchId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("MinutesSharePct")
.HasColumnType("decimal(6,2)");
b.Property<int>("PlayerId")
.HasColumnType("int");
b.Property<decimal?>("RecentMinutesAvg")
.HasColumnType("decimal(7,2)");
b.Property<decimal>("RelevanceScore")
.HasColumnType("decimal(6,2)");
b.Property<byte>("RelevanceTier")
.HasColumnType("tinyint");
b.Property<int>("SeasonId")
.HasColumnType("int");
b.Property<int>("TeamId")
.HasColumnType("int");
b.Property<decimal?>("UsageProxy")
.HasColumnType("decimal(6,2)");
b.HasKey("PlayerRelevanceSnapshotId");
b.HasIndex("AsOfUtc");
b.HasIndex("PlayerId");
b.HasIndex("TeamId");
b.HasIndex("SeasonId", "TeamId", "AsOfUtc");
b.HasIndex("SeasonId", "TeamId", "PlayerId", "AsOfUtc")
.IsUnique();
b.ToTable("NbaPlayerRelevanceSnapshots", "dbo");
});
modelBuilder.Entity("BettingOdds.Models.NbaPlayerRollingStatsSnapshot", b =>
{
b.Property<long>("SnapshotId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("SnapshotId"));
b.Property<DateTime>("AsOfUtc")
.HasColumnType("datetime2");
b.Property<decimal>("AssistsAvg")
.HasColumnType("decimal(7,2)");
b.Property<Guid>("BatchId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int>("LastNGames")
.HasColumnType("int");
b.Property<decimal>("MinutesAvg")
.HasColumnType("decimal(7,2)");
b.Property<int>("PlayerId")
.HasColumnType("int");
b.Property<decimal>("PointsAvg")
.HasColumnType("decimal(7,2)");
b.Property<decimal>("ReboundsAvg")
.HasColumnType("decimal(7,2)");
b.Property<int>("SeasonId")
.HasColumnType("int");
b.Property<decimal>("TS")
.HasColumnType("decimal(6,4)");
b.Property<int>("TeamId")
.HasColumnType("int");
b.Property<decimal>("ThreePpct")
.HasColumnType("decimal(6,4)");
b.Property<decimal>("TurnoversAvg")
.HasColumnType("decimal(7,2)");
b.HasKey("SnapshotId");
b.HasIndex("AsOfUtc");
b.HasIndex("PlayerId");
b.HasIndex("TeamId");
b.HasIndex("SeasonId", "PlayerId", "AsOfUtc");
b.HasIndex("SeasonId", "TeamId", "AsOfUtc");
b.HasIndex("SeasonId", "TeamId", "PlayerId", "LastNGames", "AsOfUtc")
.IsUnique();
b.ToTable("NbaPlayerRollingStatsSnapshots", "dbo");
});
modelBuilder.Entity("BettingOdds.Models.NbaPlayerTeam", b =>
{
b.Property<long>("PlayerTeamId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("PlayerTeamId"));
b.Property<int?>("DepthOrder")
.HasColumnType("int");
b.Property<DateTime?>("EndDateUtc")
.HasColumnType("datetime2");
b.Property<string>("JerseyNumber")
.HasColumnType("nvarchar(max)");
b.Property<int>("PlayerId")
.HasColumnType("int");
b.Property<string>("RosterStatus")
.HasColumnType("nvarchar(max)");
b.Property<int>("SeasonId")
.HasColumnType("int");
b.Property<DateTime>("StartDateUtc")
.HasColumnType("datetime2");
b.Property<int>("TeamId")
.HasColumnType("int");
b.HasKey("PlayerTeamId");
b.HasIndex("SeasonId");
b.HasIndex("TeamId");
b.HasIndex("PlayerId", "TeamId", "SeasonId", "StartDateUtc")
.IsUnique();
b.ToTable("NbaPlayerTeams", "dbo");
});
modelBuilder.Entity("BettingOdds.Models.NbaSeason", b =>
{
b.Property<int>("SeasonId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("SeasonId"));
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<string>("SeasonCode")
.IsRequired()
.HasMaxLength(16)
.HasColumnType("nvarchar(16)");
b.Property<string>("SeasonType")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.HasKey("SeasonId");
b.HasIndex("SeasonCode", "SeasonType")
.IsUnique();
b.ToTable("NbaSeasons", "dbo");
});
modelBuilder.Entity("BettingOdds.Models.NbaTeam", b =>
{
b.Property<int>("TeamId")
.HasColumnType("int");
b.Property<string>("Abbr")
.IsRequired()
.HasMaxLength(6)
.HasColumnType("nvarchar(6)");
b.Property<string>("City")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)");
b.Property<string>("FullName")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("nvarchar(80)");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.HasKey("TeamId");
b.HasIndex("Abbr");
b.ToTable("NbaTeams", "dbo");
});
modelBuilder.Entity("BettingOdds.Models.NbaTeamStatsSnapshot", b =>
{
b.Property<long>("SnapshotId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("SnapshotId"));
b.Property<Guid>("BatchId")
.HasColumnType("uniqueidentifier");
b.Property<decimal>("DefRtg")
.HasColumnType("decimal(7,3)");
b.Property<int>("LastNGames")
.HasColumnType("int");
b.Property<decimal>("NetRtg")
.HasColumnType("decimal(7,3)");
b.Property<decimal>("OffRtg")
.HasColumnType("decimal(7,3)");
b.Property<decimal>("Pace")
.HasColumnType("decimal(7,3)");
b.Property<DateTime>("PulledAtUtc")
.HasColumnType("datetime2");
b.Property<int>("SeasonId")
.HasColumnType("int");
b.Property<int>("TeamId")
.HasColumnType("int");
b.HasKey("SnapshotId");
b.HasIndex("PulledAtUtc");
b.HasIndex("TeamId");
b.HasIndex("SeasonId", "LastNGames", "PulledAtUtc");
b.HasIndex("SeasonId", "LastNGames", "BatchId", "TeamId")
.IsUnique();
b.ToTable("NbaTeamStatsSnapshots", "dbo");
});
modelBuilder.Entity("BettingOdds.Models.NbaGame", b =>
{
b.HasOne("BettingOdds.Models.NbaTeam", "AwayTeam")
.WithMany()
.HasForeignKey("AwayTeamId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("BettingOdds.Models.NbaTeam", "HomeTeam")
.WithMany()
.HasForeignKey("HomeTeamId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("BettingOdds.Models.NbaSeason", "Season")
.WithMany("Games")
.HasForeignKey("SeasonId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("AwayTeam");
b.Navigation("HomeTeam");
b.Navigation("Season");
});
modelBuilder.Entity("BettingOdds.Models.NbaPlayerGameStat", b =>
{
b.HasOne("BettingOdds.Models.NbaGame", "Game")
.WithMany()
.HasForeignKey("GameId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("BettingOdds.Models.NbaPlayer", "Player")
.WithMany()
.HasForeignKey("PlayerId")
.OnDelete(DeleteBehavior.Restrict)
/* ... truncated for agent context ... */
# FILE: Migrations/20260218153318_Reconcile_NbaTeams_LogoUrl.cs
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BettingOdds.Migrations
{
/// <inheritdoc />
public partial class Reconcile_NbaTeams_LogoUrl : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
IF NOT EXISTS (
SELECT 1
FROM sys.columns
WHERE object_id = OBJECT_ID(N'[dbo].[NbaTeams]')
AND name = N'LogoUrl'
)
BEGIN
ALTER TABLE [dbo].[NbaTeams] ADD [LogoUrl] nvarchar(256) NULL;
END
");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
IF EXISTS (
SELECT 1
FROM sys.columns
WHERE object_id = OBJECT_ID(N'[dbo].[NbaTeams]')
AND name = N'LogoUrl'
)
BEGIN
ALTER TABLE [dbo].[NbaTeams] DROP COLUMN [LogoUrl];
END
");
}
}
}
# FILE: Migrations/20260218153318_Reconcile_NbaTeams_LogoUrl.Designer.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260218153318_Reconcile_NbaTeams_LogoUrl")]
partial class Reconcile_NbaTeams_LogoUrl
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("dbo")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentMessage", b =>
{
b.Property<long>("AaiAgentMessageId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentMessageId"));
b.Property<long>("AaiAgentRunStepId")
.HasColumnType("bigint");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("JsonSchemaName")
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<int>("Role")
.HasColumnType("int");
b.HasKey("AaiAgentMessageId");
b.HasIndex("AaiAgentRunStepId", "CreatedUtc");
b.ToTable("AAI_AgentMessage", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatch", b =>
{
b.Property<long>("AaiAgentPatchId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("DiffSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("UnifiedDiff")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiAgentPatchId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("Path");
b.ToTable("AAI_AgentPatch", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatchSet", b =>
{
b.Property<long>("AaiAgentPatchSetId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchSetId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PatchSetSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("PlanMarkdown")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<int>("ProducedByStepType")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.ToTable("AAI_AgentPatchSet", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRun", b =>
{
b.Property<long>("AaiAgentRunId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunId"));
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("GovernanceBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("GuardModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("ImplementerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("PlannerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("RepoCommitSha")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("RepoRoot")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("RequestedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ReviewerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SelectedFilesBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("TaskText")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<decimal?>("TotalCostUsd")
.HasColumnType("decimal(18,6)");
b.Property<long?>("TotalInputTokens")
.HasColumnType("bigint");
b.Property<long?>("TotalOutputTokens")
.HasColumnType("bigint");
b.HasKey("AaiAgentRunId");
b.HasIndex("CreatedUtc");
b.HasIndex("RepoCommitSha");
b.HasIndex("Status", "CreatedUtc");
b.ToTable("AAI_AgentRun", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRunStep", b =>
{
b.Property<long>("AaiAgentRunStepId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunStepId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("CostUsd")
.HasColumnType("decimal(18,6)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<long?>("InputTokens")
.HasColumnType("bigint");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<long?>("OutputTokens")
.HasColumnType("bigint");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<int>("StepType")
.HasColumnType("int");
b.Property<double?>("Temperature")
.HasColumnType("float");
b.HasKey("AaiAgentRunStepId");
b.HasIndex("AaiAgentRunId", "StepType");
b.HasIndex("Status", "StartedUtc");
b.ToTable("AAI_AgentRunStep", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiDotnetExecution", b =>
{
b.Property<long>("AaiDotnetExecutionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiDotnetExecutionId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("Command")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<int>("ExecutionType")
.HasColumnType("int");
b.Property<int>("ExitCode")
.HasColumnType("int");
b.Property<DateTime>("StartedUtc")
.HasColumnType("datetime2");
b.Property<string>("StdErr")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("StdOut")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiDotnetExecutionId");
b.HasIndex("CreatedUtc");
b.HasIndex("AaiAgentRunId", "ExecutionType");
b.ToTable("AAI_DotnetExecution", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardReport", b =>
{
b.Property<long>("AaiGuardReportId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardReportId"));
b.Property<long?>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<bool>("Allowed")
.HasColumnType("bit");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PolicyVersion")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.HasKey("AaiGuardReportId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.HasIndex("AaiAgentRunId", "CreatedUtc");
b.ToTable("AAI_GuardReport", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardViolation", b =>
{
b.Property<long>("AaiGuardViolationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardViolationId"));
b.Property<long>("AaiGuardReportId")
.HasColumnType("bigint");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("Message")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("Path")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("Severity")
.HasColumnType("int");
b.Property<string>("Suggestion")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.HasKey("AaiGuardViolationId");
b.HasIndex("AaiGuardReportId", "Severity");
b.ToTable("AAI_GuardViolation", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiPatchApplyAttempt", b =>
{
b.Property<long>("AaiPatchApplyAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiPatchApplyAttemptId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<string>("AppliedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("AppliedUtc")
.HasColumnType("datetime2");
b.Property<string>("CommitShaAfterApply")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<int?>("FilesChangedCount")
.HasColumnType("int");
b.Property<int>("Result")
.HasColumnType("int");
b.HasKey("AaiPatchApplyAttemptId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AppliedUtc");
b.ToTable("AAI_PatchApplyAttempt", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiRepoFileSnapshot", b =>
{
b.Property<long>("AaiRepoFileSnapshotId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiRepoFileSnapshotId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("IncludedReason")
.HasMaxLength(300)
.HasColumnType
/* ... truncated for agent context ... */
# FILE: Migrations/20260218153704_AddAaiAgenticSchema.cs
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BettingOdds.Migrations
{
/// <inheritdoc />
public partial class AddAaiAgenticSchema : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
# FILE: Migrations/20260218153704_AddAaiAgenticSchema.Designer.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260218153704_AddAaiAgenticSchema")]
partial class AddAaiAgenticSchema
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("dbo")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentMessage", b =>
{
b.Property<long>("AaiAgentMessageId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentMessageId"));
b.Property<long>("AaiAgentRunStepId")
.HasColumnType("bigint");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("JsonSchemaName")
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<int>("Role")
.HasColumnType("int");
b.HasKey("AaiAgentMessageId");
b.HasIndex("AaiAgentRunStepId", "CreatedUtc");
b.ToTable("AAI_AgentMessage", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatch", b =>
{
b.Property<long>("AaiAgentPatchId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("DiffSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("UnifiedDiff")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiAgentPatchId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("Path");
b.ToTable("AAI_AgentPatch", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatchSet", b =>
{
b.Property<long>("AaiAgentPatchSetId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchSetId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PatchSetSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("PlanMarkdown")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<int>("ProducedByStepType")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.ToTable("AAI_AgentPatchSet", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRun", b =>
{
b.Property<long>("AaiAgentRunId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunId"));
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("GovernanceBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("GuardModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("ImplementerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("PlannerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("RepoCommitSha")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("RepoRoot")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("RequestedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ReviewerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SelectedFilesBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("TaskText")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<decimal?>("TotalCostUsd")
.HasColumnType("decimal(18,6)");
b.Property<long?>("TotalInputTokens")
.HasColumnType("bigint");
b.Property<long?>("TotalOutputTokens")
.HasColumnType("bigint");
b.HasKey("AaiAgentRunId");
b.HasIndex("CreatedUtc");
b.HasIndex("RepoCommitSha");
b.HasIndex("Status", "CreatedUtc");
b.ToTable("AAI_AgentRun", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRunStep", b =>
{
b.Property<long>("AaiAgentRunStepId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunStepId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("CostUsd")
.HasColumnType("decimal(18,6)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<long?>("InputTokens")
.HasColumnType("bigint");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<long?>("OutputTokens")
.HasColumnType("bigint");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<int>("StepType")
.HasColumnType("int");
b.Property<double?>("Temperature")
.HasColumnType("float");
b.HasKey("AaiAgentRunStepId");
b.HasIndex("AaiAgentRunId", "StepType");
b.HasIndex("Status", "StartedUtc");
b.ToTable("AAI_AgentRunStep", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiDotnetExecution", b =>
{
b.Property<long>("AaiDotnetExecutionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiDotnetExecutionId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("Command")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<int>("ExecutionType")
.HasColumnType("int");
b.Property<int>("ExitCode")
.HasColumnType("int");
b.Property<DateTime>("StartedUtc")
.HasColumnType("datetime2");
b.Property<string>("StdErr")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("StdOut")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiDotnetExecutionId");
b.HasIndex("CreatedUtc");
b.HasIndex("AaiAgentRunId", "ExecutionType");
b.ToTable("AAI_DotnetExecution", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardReport", b =>
{
b.Property<long>("AaiGuardReportId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardReportId"));
b.Property<long?>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<bool>("Allowed")
.HasColumnType("bit");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PolicyVersion")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.HasKey("AaiGuardReportId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.HasIndex("AaiAgentRunId", "CreatedUtc");
b.ToTable("AAI_GuardReport", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardViolation", b =>
{
b.Property<long>("AaiGuardViolationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardViolationId"));
b.Property<long>("AaiGuardReportId")
.HasColumnType("bigint");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("Message")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("Path")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("Severity")
.HasColumnType("int");
b.Property<string>("Suggestion")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.HasKey("AaiGuardViolationId");
b.HasIndex("AaiGuardReportId", "Severity");
b.ToTable("AAI_GuardViolation", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiPatchApplyAttempt", b =>
{
b.Property<long>("AaiPatchApplyAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiPatchApplyAttemptId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<string>("AppliedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("AppliedUtc")
.HasColumnType("datetime2");
b.Property<string>("CommitShaAfterApply")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<int?>("FilesChangedCount")
.HasColumnType("int");
b.Property<int>("Result")
.HasColumnType("int");
b.HasKey("AaiPatchApplyAttemptId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AppliedUtc");
b.ToTable("AAI_PatchApplyAttempt", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiRepoFileSnapshot", b =>
{
b.Property<long>("AaiRepoFileSnapshotId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiRepoFileSnapshotId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("IncludedReason")
.HasMaxLength(300)
.HasColumnType("nvarchar(300
/* ... truncated for agent context ... */
# FILE: Migrations/20260218154538_Repair_AaiAgenticSchema.cs
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BettingOdds.Migrations
{
/// <inheritdoc />
public partial class Repair_AaiAgenticSchema : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
IF OBJECT_ID('[dbo].[AAI_AgentRun]', 'U') IS NULL
BEGIN
CREATE TABLE [dbo].[AAI_AgentRun](
[AaiAgentRunId] BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[TaskText] NVARCHAR(4000) NOT NULL,
[RequestedByUserId] NVARCHAR(128) NULL,
[RepoRoot] NVARCHAR(400) NULL,
[RepoCommitSha] NVARCHAR(64) NULL,
[GovernanceBundleSha256] NVARCHAR(64) NULL,
[SelectedFilesBundleSha256] NVARCHAR(64) NULL,
[PlannerModel] NVARCHAR(100) NULL,
[ImplementerModel] NVARCHAR(100) NULL,
[ReviewerModel] NVARCHAR(100) NULL,
[GuardModel] NVARCHAR(100) NULL,
[Status] INT NOT NULL,
[CreatedUtc] DATETIME2 NOT NULL,
[StartedUtc] DATETIME2 NULL,
[CompletedUtc] DATETIME2 NULL,
[TotalInputTokens] BIGINT NULL,
[TotalOutputTokens] BIGINT NULL,
[TotalCostUsd] DECIMAL(18,6) NULL
);
END
");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
# FILE: Migrations/20260218154538_Repair_AaiAgenticSchema.Designer.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260218154538_Repair_AaiAgenticSchema")]
partial class Repair_AaiAgenticSchema
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("dbo")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentMessage", b =>
{
b.Property<long>("AaiAgentMessageId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentMessageId"));
b.Property<long>("AaiAgentRunStepId")
.HasColumnType("bigint");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("JsonSchemaName")
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<int>("Role")
.HasColumnType("int");
b.HasKey("AaiAgentMessageId");
b.HasIndex("AaiAgentRunStepId", "CreatedUtc");
b.ToTable("AAI_AgentMessage", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatch", b =>
{
b.Property<long>("AaiAgentPatchId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("DiffSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("UnifiedDiff")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiAgentPatchId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("Path");
b.ToTable("AAI_AgentPatch", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatchSet", b =>
{
b.Property<long>("AaiAgentPatchSetId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchSetId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PatchSetSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("PlanMarkdown")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<int>("ProducedByStepType")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.ToTable("AAI_AgentPatchSet", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRun", b =>
{
b.Property<long>("AaiAgentRunId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunId"));
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("GovernanceBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("GuardModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("ImplementerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("PlannerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("RepoCommitSha")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("RepoRoot")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("RequestedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ReviewerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SelectedFilesBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("TaskText")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<decimal?>("TotalCostUsd")
.HasColumnType("decimal(18,6)");
b.Property<long?>("TotalInputTokens")
.HasColumnType("bigint");
b.Property<long?>("TotalOutputTokens")
.HasColumnType("bigint");
b.HasKey("AaiAgentRunId");
b.HasIndex("CreatedUtc");
b.HasIndex("RepoCommitSha");
b.HasIndex("Status", "CreatedUtc");
b.ToTable("AAI_AgentRun", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRunStep", b =>
{
b.Property<long>("AaiAgentRunStepId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunStepId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("CostUsd")
.HasColumnType("decimal(18,6)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<long?>("InputTokens")
.HasColumnType("bigint");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<long?>("OutputTokens")
.HasColumnType("bigint");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<int>("StepType")
.HasColumnType("int");
b.Property<double?>("Temperature")
.HasColumnType("float");
b.HasKey("AaiAgentRunStepId");
b.HasIndex("AaiAgentRunId", "StepType");
b.HasIndex("Status", "StartedUtc");
b.ToTable("AAI_AgentRunStep", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiDotnetExecution", b =>
{
b.Property<long>("AaiDotnetExecutionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiDotnetExecutionId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("Command")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<int>("ExecutionType")
.HasColumnType("int");
b.Property<int>("ExitCode")
.HasColumnType("int");
b.Property<DateTime>("StartedUtc")
.HasColumnType("datetime2");
b.Property<string>("StdErr")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("StdOut")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiDotnetExecutionId");
b.HasIndex("CreatedUtc");
b.HasIndex("AaiAgentRunId", "ExecutionType");
b.ToTable("AAI_DotnetExecution", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardReport", b =>
{
b.Property<long>("AaiGuardReportId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardReportId"));
b.Property<long?>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<bool>("Allowed")
.HasColumnType("bit");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PolicyVersion")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.HasKey("AaiGuardReportId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.HasIndex("AaiAgentRunId", "CreatedUtc");
b.ToTable("AAI_GuardReport", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardViolation", b =>
{
b.Property<long>("AaiGuardViolationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardViolationId"));
b.Property<long>("AaiGuardReportId")
.HasColumnType("bigint");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("Message")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("Path")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("Severity")
.HasColumnType("int");
b.Property<string>("Suggestion")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.HasKey("AaiGuardViolationId");
b.HasIndex("AaiGuardReportId", "Severity");
b.ToTable("AAI_GuardViolation", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiPatchApplyAttempt", b =>
{
b.Property<long>("AaiPatchApplyAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiPatchApplyAttemptId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<string>("AppliedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("AppliedUtc")
.HasColumnType("datetime2");
b.Property<string>("CommitShaAfterApply")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<int?>("FilesChangedCount")
.HasColumnType("int");
b.Property<int>("Result")
.HasColumnType("int");
b.HasKey("AaiPatchApplyAttemptId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AppliedUtc");
b.ToTable("AAI_PatchApplyAttempt", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiRepoFileSnapshot", b =>
{
b.Property<long>("AaiRepoFileSnapshotId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiRepoFileSnapshotId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("IncludedReason")
.HasMaxLength(300)
.HasColumnType("nvar
/* ... truncated for agent context ... */
# FILE: Migrations/20260218154908_Repair_AaiAgenticSchema2.cs
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BettingOdds.Migrations
{
/// <inheritdoc />
public partial class Repair_AaiAgenticSchema2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AAI_AgentPatchSet",
schema: "dbo",
columns: table => new
{
AaiAgentPatchSetId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiAgentRunId = table.Column<long>(type: "bigint", nullable: false),
Title = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
PlanMarkdown = table.Column<string>(type: "nvarchar(max)", maxLength: 256, nullable: false),
ProducedByStepType = table.Column<int>(type: "int", nullable: false),
PatchSetSha256 = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_AgentPatchSet", x => x.AaiAgentPatchSetId);
table.ForeignKey(
name: "FK_AAI_AgentPatchSet_AAI_AgentRun_AaiAgentRunId",
column: x => x.AaiAgentRunId,
principalSchema: "dbo",
principalTable: "AAI_AgentRun",
principalColumn: "AaiAgentRunId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AAI_AgentRunStep",
schema: "dbo",
columns: table => new
{
AaiAgentRunStepId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiAgentRunId = table.Column<long>(type: "bigint", nullable: false),
StepType = table.Column<int>(type: "int", nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
Model = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
Temperature = table.Column<double>(type: "float", nullable: true),
InputTokens = table.Column<long>(type: "bigint", nullable: true),
OutputTokens = table.Column<long>(type: "bigint", nullable: true),
CostUsd = table.Column<decimal>(type: "decimal(18,6)", nullable: true),
DurationMs = table.Column<int>(type: "int", nullable: true),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
StartedUtc = table.Column<DateTime>(type: "datetime2", nullable: true),
CompletedUtc = table.Column<DateTime>(type: "datetime2", nullable: true),
Error = table.Column<string>(type: "nvarchar(4000)", maxLength: 4000, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_AgentRunStep", x => x.AaiAgentRunStepId);
table.ForeignKey(
name: "FK_AAI_AgentRunStep_AAI_AgentRun_AaiAgentRunId",
column: x => x.AaiAgentRunId,
principalSchema: "dbo",
principalTable: "AAI_AgentRun",
principalColumn: "AaiAgentRunId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AAI_DotnetExecution",
schema: "dbo",
columns: table => new
{
AaiDotnetExecutionId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiAgentRunId = table.Column<long>(type: "bigint", nullable: false),
ExecutionType = table.Column<int>(type: "int", nullable: false),
Command = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
ExitCode = table.Column<int>(type: "int", nullable: false),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
StartedUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
CompletedUtc = table.Column<DateTime>(type: "datetime2", nullable: true),
DurationMs = table.Column<int>(type: "int", nullable: true),
StdOut = table.Column<string>(type: "nvarchar(max)", maxLength: 256, nullable: true),
StdErr = table.Column<string>(type: "nvarchar(max)", maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_DotnetExecution", x => x.AaiDotnetExecutionId);
table.ForeignKey(
name: "FK_AAI_DotnetExecution_AAI_AgentRun_AaiAgentRunId",
column: x => x.AaiAgentRunId,
principalSchema: "dbo",
principalTable: "AAI_AgentRun",
principalColumn: "AaiAgentRunId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AAI_RepoFileSnapshot",
schema: "dbo",
columns: table => new
{
AaiRepoFileSnapshotId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiAgentRunId = table.Column<long>(type: "bigint", nullable: false),
Path = table.Column<string>(type: "nvarchar(400)", maxLength: 400, nullable: false),
ContentSha256 = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
SizeBytes = table.Column<int>(type: "int", nullable: true),
IncludedReason = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: true),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_RepoFileSnapshot", x => x.AaiRepoFileSnapshotId);
table.ForeignKey(
name: "FK_AAI_RepoFileSnapshot_AAI_AgentRun_AaiAgentRunId",
column: x => x.AaiAgentRunId,
principalSchema: "dbo",
principalTable: "AAI_AgentRun",
principalColumn: "AaiAgentRunId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AAI_AgentPatch",
schema: "dbo",
columns: table => new
{
AaiAgentPatchId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiAgentPatchSetId = table.Column<long>(type: "bigint", nullable: false),
Path = table.Column<string>(type: "nvarchar(400)", maxLength: 400, nullable: false),
UnifiedDiff = table.Column<string>(type: "nvarchar(max)", maxLength: 256, nullable: false),
Reason = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false),
DiffSha256 = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_AgentPatch", x => x.AaiAgentPatchId);
table.ForeignKey(
name: "FK_AAI_AgentPatch_AAI_AgentPatchSet_AaiAgentPatchSetId",
column: x => x.AaiAgentPatchSetId,
principalSchema: "dbo",
principalTable: "AAI_AgentPatchSet",
principalColumn: "AaiAgentPatchSetId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AAI_GuardReport",
schema: "dbo",
columns: table => new
{
AaiGuardReportId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiAgentRunId = table.Column<long>(type: "bigint", nullable: false),
AaiAgentPatchSetId = table.Column<long>(type: "bigint", nullable: true),
Allowed = table.Column<bool>(type: "bit", nullable: false),
PolicyVersion = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_GuardReport", x => x.AaiGuardReportId);
table.ForeignKey(
name: "FK_AAI_GuardReport_AAI_AgentPatchSet_AaiAgentPatchSetId",
column: x => x.AaiAgentPatchSetId,
principalSchema: "dbo",
principalTable: "AAI_AgentPatchSet",
principalColumn: "AaiAgentPatchSetId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_AAI_GuardReport_AAI_AgentRun_AaiAgentRunId",
column: x => x.AaiAgentRunId,
principalSchema: "dbo",
principalTable: "AAI_AgentRun",
principalColumn: "AaiAgentRunId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AAI_PatchApplyAttempt",
schema: "dbo",
columns: table => new
{
AaiPatchApplyAttemptId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiAgentPatchSetId = table.Column<long>(type: "bigint", nullable: false),
AppliedByUserId = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
AppliedUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
Result = table.Column<int>(type: "int", nullable: false),
FilesChangedCount = table.Column<int>(type: "int", nullable: true),
Error = table.Column<string>(type: "nvarchar(4000)", maxLength: 4000, nullable: true),
CommitShaAfterApply = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_PatchApplyAttempt", x => x.AaiPatchApplyAttemptId);
table.ForeignKey(
name: "FK_AAI_PatchApplyAttempt_AAI_AgentPatchSet_AaiAgentPatchSetId",
column: x => x.AaiAgentPatchSetId,
principalSchema: "dbo",
principalTable: "AAI_AgentPatchSet",
principalColumn: "AaiAgentPatchSetId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AAI_AgentMessage",
schema: "dbo",
columns: table => new
{
AaiAgentMessageId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiAgentRunStepId = table.Column<long>(type: "bigint", nullable: false),
Role = table.Column<int>(type: "int", nullable: false),
JsonSchemaName = table.Column<string>(type: "nvarchar(120)", maxLength: 120, nullable: true),
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 256, nullable: false),
ContentSha256 = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_AgentMessage", x => x.AaiAgentMessageId);
table.ForeignKey(
name: "FK_AAI_AgentMessage_AAI_AgentRunStep_AaiAgentRunStepId",
column: x => x.AaiAgentRunStepId,
principalSchema: "dbo",
principalTable: "AAI_AgentRunStep",
principalColumn: "AaiAgentRunStepId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AAI_GuardViolation",
schema: "dbo",
columns: table => new
{
AaiGuardViolationId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiGuardReportId = table.Column<long>(type: "bigint", nullable: false),
Code = table.Column<string>(type: "nvarchar(60)", maxLength: 60, nullable: false),
Severity = table.Column<int>(type: "int", nullable: false),
Path = table.Column<string>(type: "nvarchar(400)", maxLength: 400, nullable: true),
Message = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
Suggestion = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_GuardViolation", x => x.AaiGuardViolationId);
table.ForeignKey(
name: "FK_AAI_GuardViolation_AAI_GuardReport_AaiGuardReportId",
column: x => x.AaiGuardReportId,
principalSchema: "dbo",
principalTable: "AAI_GuardReport",
principalColumn: "AaiGuardReportId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AAI_AgentMessage_AaiAgentRunStepId_CreatedUtc",
schema: "dbo",
table: "AAI_AgentMessage",
columns: new[] { "AaiAgentRunStepId", "CreatedUtc" });
migrationBuilder.CreateIndex(
name: "IX_AAI_AgentPatch_AaiAgentPatchSetId",
schema: "dbo",
table: "AAI_AgentPatch",
column: "AaiAgentPatchSetId");
migrationBuilder.CreateIndex(
name: "IX_AAI_AgentPatch_Path",
schema: "dbo",
table: "AAI_AgentPatch",
column: "Path");
migrationBuilder.CreateIndex(
name: "IX_AAI_AgentPatchSet_AaiAgentRunId",
schema: "dbo",
table: "AAI_AgentPatchSet",
column: "AaiAgentRunId");
migrationBuilder.CreateIndex(
name: "IX_AAI_AgentRun_CreatedUtc",
schema: "dbo",
table: "AAI_AgentRun",
column: "CreatedUtc");
migrationBuilder.CreateIndex(
name: "IX_AAI_AgentRun_RepoCommitSha",
schema: "dbo",
table: "AAI_AgentRun",
column: "RepoCommitSha");
migrationBuilder.CreateIndex(
name: "IX_AAI_AgentRun_Status_CreatedUtc",
schema: "dbo",
table: "AAI_AgentRun",
columns: new[] { "Status", "CreatedUtc" });
migrationBuilder.CreateIndex(
name: "IX_AAI_AgentRunStep_AaiAgentRunId_StepType",
schema: "dbo",
table: "AAI_AgentRunStep",
columns: new[] { "AaiAgentRunId", "StepType" });
migrationBuilder.CreateIndex(
name: "IX_AAI_AgentRunStep_Status_StartedUtc",
schema: "dbo",
table: "AAI_AgentRunStep",
columns: new[] { "Status", "StartedUtc" });
migrationBuilder.CreateIndex(
name: "IX_AAI_DotnetExecution_AaiAgentRunId_ExecutionType",
schema: "dbo",
table: "AAI_DotnetExecution",
columns: new[] { "A
/* ... truncated for agent context ... */
# FILE: Migrations/20260218154908_Repair_AaiAgenticSchema2.Designer.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260218154908_Repair_AaiAgenticSchema2")]
partial class Repair_AaiAgenticSchema2
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("dbo")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentMessage", b =>
{
b.Property<long>("AaiAgentMessageId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentMessageId"));
b.Property<long>("AaiAgentRunStepId")
.HasColumnType("bigint");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("JsonSchemaName")
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<int>("Role")
.HasColumnType("int");
b.HasKey("AaiAgentMessageId");
b.HasIndex("AaiAgentRunStepId", "CreatedUtc");
b.ToTable("AAI_AgentMessage", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatch", b =>
{
b.Property<long>("AaiAgentPatchId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("DiffSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("UnifiedDiff")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiAgentPatchId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("Path");
b.ToTable("AAI_AgentPatch", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatchSet", b =>
{
b.Property<long>("AaiAgentPatchSetId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchSetId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PatchSetSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("PlanMarkdown")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<int>("ProducedByStepType")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.ToTable("AAI_AgentPatchSet", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRun", b =>
{
b.Property<long>("AaiAgentRunId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunId"));
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("GovernanceBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("GuardModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("ImplementerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("PlannerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("RepoCommitSha")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("RepoRoot")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("RequestedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ReviewerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SelectedFilesBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("TaskText")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<decimal?>("TotalCostUsd")
.HasColumnType("decimal(18,6)");
b.Property<long?>("TotalInputTokens")
.HasColumnType("bigint");
b.Property<long?>("TotalOutputTokens")
.HasColumnType("bigint");
b.HasKey("AaiAgentRunId");
b.HasIndex("CreatedUtc");
b.HasIndex("RepoCommitSha");
b.HasIndex("Status", "CreatedUtc");
b.ToTable("AAI_AgentRun", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRunStep", b =>
{
b.Property<long>("AaiAgentRunStepId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunStepId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("CostUsd")
.HasColumnType("decimal(18,6)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<long?>("InputTokens")
.HasColumnType("bigint");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<long?>("OutputTokens")
.HasColumnType("bigint");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<int>("StepType")
.HasColumnType("int");
b.Property<double?>("Temperature")
.HasColumnType("float");
b.HasKey("AaiAgentRunStepId");
b.HasIndex("AaiAgentRunId", "StepType");
b.HasIndex("Status", "StartedUtc");
b.ToTable("AAI_AgentRunStep", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiDotnetExecution", b =>
{
b.Property<long>("AaiDotnetExecutionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiDotnetExecutionId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("Command")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<int>("ExecutionType")
.HasColumnType("int");
b.Property<int>("ExitCode")
.HasColumnType("int");
b.Property<DateTime>("StartedUtc")
.HasColumnType("datetime2");
b.Property<string>("StdErr")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("StdOut")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiDotnetExecutionId");
b.HasIndex("CreatedUtc");
b.HasIndex("AaiAgentRunId", "ExecutionType");
b.ToTable("AAI_DotnetExecution", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardReport", b =>
{
b.Property<long>("AaiGuardReportId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardReportId"));
b.Property<long?>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<bool>("Allowed")
.HasColumnType("bit");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PolicyVersion")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.HasKey("AaiGuardReportId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.HasIndex("AaiAgentRunId", "CreatedUtc");
b.ToTable("AAI_GuardReport", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardViolation", b =>
{
b.Property<long>("AaiGuardViolationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardViolationId"));
b.Property<long>("AaiGuardReportId")
.HasColumnType("bigint");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("Message")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("Path")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("Severity")
.HasColumnType("int");
b.Property<string>("Suggestion")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.HasKey("AaiGuardViolationId");
b.HasIndex("AaiGuardReportId", "Severity");
b.ToTable("AAI_GuardViolation", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiPatchApplyAttempt", b =>
{
b.Property<long>("AaiPatchApplyAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiPatchApplyAttemptId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<string>("AppliedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("AppliedUtc")
.HasColumnType("datetime2");
b.Property<string>("CommitShaAfterApply")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<int?>("FilesChangedCount")
.HasColumnType("int");
b.Property<int>("Result")
.HasColumnType("int");
b.HasKey("AaiPatchApplyAttemptId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AppliedUtc");
b.ToTable("AAI_PatchApplyAttempt", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiRepoFileSnapshot", b =>
{
b.Property<long>("AaiRepoFileSnapshotId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiRepoFileSnapshotId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("IncludedReason")
.HasMaxLength(300)
.HasColumnType("nv
/* ... truncated for agent context ... */
# FILE: Migrations/20260218224121_AddAaiPatchPreflight.cs
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BettingOdds.Migrations
{
/// <inheritdoc />
public partial class AddAaiPatchPreflight : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AAI_PatchPreflightReports",
schema: "dbo",
columns: table => new
{
AaiPatchPreflightReportId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiAgentRunId = table.Column<long>(type: "bigint", nullable: false),
AaiAgentPatchSetId = table.Column<long>(type: "bigint", nullable: false),
Allowed = table.Column<bool>(type: "bit", nullable: false),
PolicyVersion = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_PatchPreflightReports", x => x.AaiPatchPreflightReportId);
table.ForeignKey(
name: "FK_AAI_PatchPreflightReports_AAI_AgentPatchSet_AaiAgentPatchSetId",
column: x => x.AaiAgentPatchSetId,
principalSchema: "dbo",
principalTable: "AAI_AgentPatchSet",
principalColumn: "AaiAgentPatchSetId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_AAI_PatchPreflightReports_AAI_AgentRun_AaiAgentRunId",
column: x => x.AaiAgentRunId,
principalSchema: "dbo",
principalTable: "AAI_AgentRun",
principalColumn: "AaiAgentRunId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "AAI_PatchPreflightViolations",
schema: "dbo",
columns: table => new
{
AaiPatchPreflightViolationId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AaiPatchPreflightReportId = table.Column<long>(type: "bigint", nullable: false),
Severity = table.Column<int>(type: "int", nullable: false),
Code = table.Column<string>(type: "nvarchar(80)", maxLength: 80, nullable: false),
Message = table.Column<string>(type: "nvarchar(4000)", maxLength: 4000, nullable: false),
Path = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
Suggestion = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
CreatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AAI_PatchPreflightViolations", x => x.AaiPatchPreflightViolationId);
table.ForeignKey(
name: "FK_AAI_PatchPreflightViolations_AAI_PatchPreflightReports_AaiPatchPreflightReportId",
column: x => x.AaiPatchPreflightReportId,
principalSchema: "dbo",
principalTable: "AAI_PatchPreflightReports",
principalColumn: "AaiPatchPreflightReportId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AAI_PatchPreflightReports_AaiAgentPatchSetId_CreatedUtc",
schema: "dbo",
table: "AAI_PatchPreflightReports",
columns: new[] { "AaiAgentPatchSetId", "CreatedUtc" });
migrationBuilder.CreateIndex(
name: "IX_AAI_PatchPreflightReports_AaiAgentRunId",
schema: "dbo",
table: "AAI_PatchPreflightReports",
column: "AaiAgentRunId");
migrationBuilder.CreateIndex(
name: "IX_AAI_PatchPreflightViolations_AaiPatchPreflightReportId_Severity",
schema: "dbo",
table: "AAI_PatchPreflightViolations",
columns: new[] { "AaiPatchPreflightReportId", "Severity" });
migrationBuilder.CreateIndex(
name: "IX_AAI_PatchPreflightViolations_Code",
schema: "dbo",
table: "AAI_PatchPreflightViolations",
column: "Code");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AAI_PatchPreflightViolations",
schema: "dbo");
migrationBuilder.DropTable(
name: "AAI_PatchPreflightReports",
schema: "dbo");
}
}
}
# FILE: Migrations/20260218224121_AddAaiPatchPreflight.Designer.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260218224121_AddAaiPatchPreflight")]
partial class AddAaiPatchPreflight
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("dbo")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentMessage", b =>
{
b.Property<long>("AaiAgentMessageId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentMessageId"));
b.Property<long>("AaiAgentRunStepId")
.HasColumnType("bigint");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("JsonSchemaName")
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<int>("Role")
.HasColumnType("int");
b.HasKey("AaiAgentMessageId");
b.HasIndex("AaiAgentRunStepId", "CreatedUtc");
b.ToTable("AAI_AgentMessage", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatch", b =>
{
b.Property<long>("AaiAgentPatchId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("DiffSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("UnifiedDiff")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiAgentPatchId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("Path");
b.ToTable("AAI_AgentPatch", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatchSet", b =>
{
b.Property<long>("AaiAgentPatchSetId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchSetId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PatchSetSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("PlanMarkdown")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<int>("ProducedByStepType")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.ToTable("AAI_AgentPatchSet", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRun", b =>
{
b.Property<long>("AaiAgentRunId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunId"));
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("GovernanceBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("GuardModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("ImplementerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("PlannerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("RepoCommitSha")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("RepoRoot")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("RequestedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ReviewerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SelectedFilesBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("TaskText")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<decimal?>("TotalCostUsd")
.HasColumnType("decimal(18,6)");
b.Property<long?>("TotalInputTokens")
.HasColumnType("bigint");
b.Property<long?>("TotalOutputTokens")
.HasColumnType("bigint");
b.HasKey("AaiAgentRunId");
b.HasIndex("CreatedUtc");
b.HasIndex("RepoCommitSha");
b.HasIndex("Status", "CreatedUtc");
b.ToTable("AAI_AgentRun", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRunStep", b =>
{
b.Property<long>("AaiAgentRunStepId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunStepId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("CostUsd")
.HasColumnType("decimal(18,6)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<long?>("InputTokens")
.HasColumnType("bigint");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<long?>("OutputTokens")
.HasColumnType("bigint");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<int>("StepType")
.HasColumnType("int");
b.Property<double?>("Temperature")
.HasColumnType("float");
b.HasKey("AaiAgentRunStepId");
b.HasIndex("AaiAgentRunId", "StepType");
b.HasIndex("Status", "StartedUtc");
b.ToTable("AAI_AgentRunStep", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiDotnetExecution", b =>
{
b.Property<long>("AaiDotnetExecutionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiDotnetExecutionId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("Command")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<int>("ExecutionType")
.HasColumnType("int");
b.Property<int>("ExitCode")
.HasColumnType("int");
b.Property<DateTime>("StartedUtc")
.HasColumnType("datetime2");
b.Property<string>("StdErr")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("StdOut")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiDotnetExecutionId");
b.HasIndex("CreatedUtc");
b.HasIndex("AaiAgentRunId", "ExecutionType");
b.ToTable("AAI_DotnetExecution", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardReport", b =>
{
b.Property<long>("AaiGuardReportId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardReportId"));
b.Property<long?>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<bool>("Allowed")
.HasColumnType("bit");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PolicyVersion")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.HasKey("AaiGuardReportId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.HasIndex("AaiAgentRunId", "CreatedUtc");
b.ToTable("AAI_GuardReport", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardViolation", b =>
{
b.Property<long>("AaiGuardViolationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardViolationId"));
b.Property<long>("AaiGuardReportId")
.HasColumnType("bigint");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("Message")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("Path")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("Severity")
.HasColumnType("int");
b.Property<string>("Suggestion")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.HasKey("AaiGuardViolationId");
b.HasIndex("AaiGuardReportId", "Severity");
b.ToTable("AAI_GuardViolation", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiPatchApplyAttempt", b =>
{
b.Property<long>("AaiPatchApplyAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiPatchApplyAttemptId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<string>("AppliedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("AppliedUtc")
.HasColumnType("datetime2");
b.Property<string>("CommitShaAfterApply")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<int?>("FilesChangedCount")
.HasColumnType("int");
b.Property<int>("Result")
.HasColumnType("int");
b.HasKey("AaiPatchApplyAttemptId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AppliedUtc");
b.ToTable("AAI_PatchApplyAttempt", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiPatchPreflightReport", b =>
{
b.Property<long>("AaiPatchPreflightReportId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiPatchPreflightReportId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<bool>("Allowed")
.HasColumnType("bit");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PolicyVersion")
.IsReq
/* ... truncated for agent context ... */
# FILE: Migrations/20260218231615_Aai_ApplyAttempt_CommitFields.cs
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BettingOdds.Migrations
{
/// <inheritdoc />
public partial class Aai_ApplyAttempt_CommitFields : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "CommitExitCode",
schema: "dbo",
table: "AAI_PatchApplyAttempt",
type: "int",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "CommitMessage",
schema: "dbo",
table: "AAI_PatchApplyAttempt",
type: "nvarchar(400)",
maxLength: 400,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "CommitStdErr",
schema: "dbo",
table: "AAI_PatchApplyAttempt",
type: "nvarchar(4000)",
maxLength: 4000,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "CommitStdOut",
schema: "dbo",
table: "AAI_PatchApplyAttempt",
type: "nvarchar(4000)",
maxLength: 4000,
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "CommittedUtc",
schema: "dbo",
table: "AAI_PatchApplyAttempt",
type: "datetime2",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "RepoCommitSha",
schema: "dbo",
table: "AAI_PatchApplyAttempt",
type: "nvarchar(64)",
maxLength: 64,
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CommitExitCode",
schema: "dbo",
table: "AAI_PatchApplyAttempt");
migrationBuilder.DropColumn(
name: "CommitMessage",
schema: "dbo",
table: "AAI_PatchApplyAttempt");
migrationBuilder.DropColumn(
name: "CommitStdErr",
schema: "dbo",
table: "AAI_PatchApplyAttempt");
migrationBuilder.DropColumn(
name: "CommitStdOut",
schema: "dbo",
table: "AAI_PatchApplyAttempt");
migrationBuilder.DropColumn(
name: "CommittedUtc",
schema: "dbo",
table: "AAI_PatchApplyAttempt");
migrationBuilder.DropColumn(
name: "RepoCommitSha",
schema: "dbo",
table: "AAI_PatchApplyAttempt");
}
}
}
# FILE: Migrations/20260218231615_Aai_ApplyAttempt_CommitFields.Designer.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260218231615_Aai_ApplyAttempt_CommitFields")]
partial class Aai_ApplyAttempt_CommitFields
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("dbo")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentMessage", b =>
{
b.Property<long>("AaiAgentMessageId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentMessageId"));
b.Property<long>("AaiAgentRunStepId")
.HasColumnType("bigint");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("JsonSchemaName")
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<int>("Role")
.HasColumnType("int");
b.HasKey("AaiAgentMessageId");
b.HasIndex("AaiAgentRunStepId", "CreatedUtc");
b.ToTable("AAI_AgentMessage", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatch", b =>
{
b.Property<long>("AaiAgentPatchId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("DiffSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("UnifiedDiff")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiAgentPatchId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("Path");
b.ToTable("AAI_AgentPatch", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatchSet", b =>
{
b.Property<long>("AaiAgentPatchSetId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchSetId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PatchSetSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("PlanMarkdown")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<int>("ProducedByStepType")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.ToTable("AAI_AgentPatchSet", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRun", b =>
{
b.Property<long>("AaiAgentRunId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunId"));
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("GovernanceBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("GuardModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("ImplementerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("PlannerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("RepoCommitSha")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("RepoRoot")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("RequestedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ReviewerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SelectedFilesBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("TaskText")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<decimal?>("TotalCostUsd")
.HasColumnType("decimal(18,6)");
b.Property<long?>("TotalInputTokens")
.HasColumnType("bigint");
b.Property<long?>("TotalOutputTokens")
.HasColumnType("bigint");
b.HasKey("AaiAgentRunId");
b.HasIndex("CreatedUtc");
b.HasIndex("RepoCommitSha");
b.HasIndex("Status", "CreatedUtc");
b.ToTable("AAI_AgentRun", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRunStep", b =>
{
b.Property<long>("AaiAgentRunStepId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunStepId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("CostUsd")
.HasColumnType("decimal(18,6)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<long?>("InputTokens")
.HasColumnType("bigint");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<long?>("OutputTokens")
.HasColumnType("bigint");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<int>("StepType")
.HasColumnType("int");
b.Property<double?>("Temperature")
.HasColumnType("float");
b.HasKey("AaiAgentRunStepId");
b.HasIndex("AaiAgentRunId", "StepType");
b.HasIndex("Status", "StartedUtc");
b.ToTable("AAI_AgentRunStep", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiDotnetExecution", b =>
{
b.Property<long>("AaiDotnetExecutionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiDotnetExecutionId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("Command")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<int>("ExecutionType")
.HasColumnType("int");
b.Property<int>("ExitCode")
.HasColumnType("int");
b.Property<DateTime>("StartedUtc")
.HasColumnType("datetime2");
b.Property<string>("StdErr")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("StdOut")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiDotnetExecutionId");
b.HasIndex("CreatedUtc");
b.HasIndex("AaiAgentRunId", "ExecutionType");
b.ToTable("AAI_DotnetExecution", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardReport", b =>
{
b.Property<long>("AaiGuardReportId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardReportId"));
b.Property<long?>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<bool>("Allowed")
.HasColumnType("bit");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PolicyVersion")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.HasKey("AaiGuardReportId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.HasIndex("AaiAgentRunId", "CreatedUtc");
b.ToTable("AAI_GuardReport", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardViolation", b =>
{
b.Property<long>("AaiGuardViolationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardViolationId"));
b.Property<long>("AaiGuardReportId")
.HasColumnType("bigint");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("Message")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("Path")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("Severity")
.HasColumnType("int");
b.Property<string>("Suggestion")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.HasKey("AaiGuardViolationId");
b.HasIndex("AaiGuardReportId", "Severity");
b.ToTable("AAI_GuardViolation", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiPatchApplyAttempt", b =>
{
b.Property<long>("AaiPatchApplyAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiPatchApplyAttemptId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<string>("AppliedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("AppliedUtc")
.HasColumnType("datetime2");
b.Property<int?>("CommitExitCode")
.HasColumnType("int");
b.Property<string>("CommitMessage")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("CommitShaAfterApply")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("CommitStdErr")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<string>("CommitStdOut")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<DateTime?>("CommittedUtc")
.HasColumnType("datetime2");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<int?>("FilesChangedCount")
.HasColumnType("int");
b.Property<string>("RepoCommitSha")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<int>("Result")
.HasColumnType("int");
b.HasKey("AaiPatchApplyAttemptId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AppliedUtc");
b.ToTable("AAI_PatchApplyAttempt", "dbo");
});
modelBuilder.Entity("BettingOdds.Do
/* ... truncated for agent context ... */
# FILE: Migrations/20260219003652_Aai_Latest_Updates.cs
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BettingOdds.Migrations
{
/// <inheritdoc />
public partial class Aai_Latest_Updates : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "MaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRunStep",
type: "int",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "GuardMaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRun",
type: "int",
nullable: true);
migrationBuilder.AddColumn<double>(
name: "GuardTemperature",
schema: "dbo",
table: "AAI_AgentRun",
type: "float",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "ImplementerMaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRun",
type: "int",
nullable: true);
migrationBuilder.AddColumn<double>(
name: "ImplementerTemperature",
schema: "dbo",
table: "AAI_AgentRun",
type: "float",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "PlannerMaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRun",
type: "int",
nullable: true);
migrationBuilder.AddColumn<double>(
name: "PlannerTemperature",
schema: "dbo",
table: "AAI_AgentRun",
type: "float",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "ReviewerMaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRun",
type: "int",
nullable: true);
migrationBuilder.AddColumn<double>(
name: "ReviewerTemperature",
schema: "dbo",
table: "AAI_AgentRun",
type: "float",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "MaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRunStep");
migrationBuilder.DropColumn(
name: "GuardMaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRun");
migrationBuilder.DropColumn(
name: "GuardTemperature",
schema: "dbo",
table: "AAI_AgentRun");
migrationBuilder.DropColumn(
name: "ImplementerMaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRun");
migrationBuilder.DropColumn(
name: "ImplementerTemperature",
schema: "dbo",
table: "AAI_AgentRun");
migrationBuilder.DropColumn(
name: "PlannerMaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRun");
migrationBuilder.DropColumn(
name: "PlannerTemperature",
schema: "dbo",
table: "AAI_AgentRun");
migrationBuilder.DropColumn(
name: "ReviewerMaxOutputTokens",
schema: "dbo",
table: "AAI_AgentRun");
migrationBuilder.DropColumn(
name: "ReviewerTemperature",
schema: "dbo",
table: "AAI_AgentRun");
}
}
}
# FILE: Migrations/20260219003652_Aai_Latest_Updates.Designer.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260219003652_Aai_Latest_Updates")]
partial class Aai_Latest_Updates
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("dbo")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentMessage", b =>
{
b.Property<long>("AaiAgentMessageId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentMessageId"));
b.Property<long>("AaiAgentRunStepId")
.HasColumnType("bigint");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("JsonSchemaName")
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<int>("Role")
.HasColumnType("int");
b.HasKey("AaiAgentMessageId");
b.HasIndex("AaiAgentRunStepId", "CreatedUtc");
b.ToTable("AAI_AgentMessage", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatch", b =>
{
b.Property<long>("AaiAgentPatchId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("DiffSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("UnifiedDiff")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiAgentPatchId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("Path");
b.ToTable("AAI_AgentPatch", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatchSet", b =>
{
b.Property<long>("AaiAgentPatchSetId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchSetId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PatchSetSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("PlanMarkdown")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<int>("ProducedByStepType")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.ToTable("AAI_AgentPatchSet", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRun", b =>
{
b.Property<long>("AaiAgentRunId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunId"));
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("GovernanceBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<int?>("GuardMaxOutputTokens")
.HasColumnType("int");
b.Property<string>("GuardModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<double?>("GuardTemperature")
.HasColumnType("float");
b.Property<int?>("ImplementerMaxOutputTokens")
.HasColumnType("int");
b.Property<string>("ImplementerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<double?>("ImplementerTemperature")
.HasColumnType("float");
b.Property<int?>("PlannerMaxOutputTokens")
.HasColumnType("int");
b.Property<string>("PlannerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<double?>("PlannerTemperature")
.HasColumnType("float");
b.Property<string>("RepoCommitSha")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("RepoRoot")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("RequestedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int?>("ReviewerMaxOutputTokens")
.HasColumnType("int");
b.Property<string>("ReviewerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<double?>("ReviewerTemperature")
.HasColumnType("float");
b.Property<string>("SelectedFilesBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("TaskText")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<decimal?>("TotalCostUsd")
.HasColumnType("decimal(18,6)");
b.Property<long?>("TotalInputTokens")
.HasColumnType("bigint");
b.Property<long?>("TotalOutputTokens")
.HasColumnType("bigint");
b.HasKey("AaiAgentRunId");
b.HasIndex("CreatedUtc");
b.HasIndex("RepoCommitSha");
b.HasIndex("Status", "CreatedUtc");
b.ToTable("AAI_AgentRun", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRunStep", b =>
{
b.Property<long>("AaiAgentRunStepId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunStepId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("CostUsd")
.HasColumnType("decimal(18,6)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<long?>("InputTokens")
.HasColumnType("bigint");
b.Property<int?>("MaxOutputTokens")
.HasColumnType("int");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<long?>("OutputTokens")
.HasColumnType("bigint");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<int>("StepType")
.HasColumnType("int");
b.Property<double?>("Temperature")
.HasColumnType("float");
b.HasKey("AaiAgentRunStepId");
b.HasIndex("AaiAgentRunId", "StepType");
b.HasIndex("Status", "StartedUtc");
b.ToTable("AAI_AgentRunStep", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiDotnetExecution", b =>
{
b.Property<long>("AaiDotnetExecutionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiDotnetExecutionId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("Command")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<int>("ExecutionType")
.HasColumnType("int");
b.Property<int>("ExitCode")
.HasColumnType("int");
b.Property<DateTime>("StartedUtc")
.HasColumnType("datetime2");
b.Property<string>("StdErr")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("StdOut")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiDotnetExecutionId");
b.HasIndex("CreatedUtc");
b.HasIndex("AaiAgentRunId", "ExecutionType");
b.ToTable("AAI_DotnetExecution", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardReport", b =>
{
b.Property<long>("AaiGuardReportId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardReportId"));
b.Property<long?>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<bool>("Allowed")
.HasColumnType("bit");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PolicyVersion")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.HasKey("AaiGuardReportId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.HasIndex("AaiAgentRunId", "CreatedUtc");
b.ToTable("AAI_GuardReport", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardViolation", b =>
{
b.Property<long>("AaiGuardViolationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardViolationId"));
b.Property<long>("AaiGuardReportId")
.HasColumnType("bigint");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("Message")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("Path")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("Severity")
.HasColumnType("int");
b.Property<string>("Suggestion")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.HasKey("AaiGuardViolationId");
b.HasIndex("AaiGuardReportId", "Severity");
b.ToTable("AAI_GuardViolation", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiPatchApplyAttempt", b =>
{
b.Property<long>("AaiPatchApplyAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiPatchApplyAttemptId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<string>("AppliedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("AppliedUtc")
.HasColumnType("datetime2");
b.Property<int?>("CommitExitCode")
.HasColumnType("int");
b.Property<string>("CommitMessage")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("CommitShaAfterApply")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("CommitStdErr")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<string>("CommitStdOut")
.HasMaxLe
/* ... truncated for agent context ... */
# FILE: Migrations/AppDbContextModelSnapshot.cs
// <auto-generated />
using System;
using BettingOdds.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BettingOdds.Migrations
{
[DbContext(typeof(AppDbContext))]
partial class AppDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("dbo")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentMessage", b =>
{
b.Property<long>("AaiAgentMessageId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentMessageId"));
b.Property<long>("AaiAgentRunStepId")
.HasColumnType("bigint");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("JsonSchemaName")
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<int>("Role")
.HasColumnType("int");
b.HasKey("AaiAgentMessageId");
b.HasIndex("AaiAgentRunStepId", "CreatedUtc");
b.ToTable("AAI_AgentMessage", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatch", b =>
{
b.Property<long>("AaiAgentPatchId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("DiffSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Reason")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("UnifiedDiff")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiAgentPatchId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("Path");
b.ToTable("AAI_AgentPatch", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentPatchSet", b =>
{
b.Property<long>("AaiAgentPatchSetId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentPatchSetId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PatchSetSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("PlanMarkdown")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<int>("ProducedByStepType")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.ToTable("AAI_AgentPatchSet", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRun", b =>
{
b.Property<long>("AaiAgentRunId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunId"));
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("GovernanceBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<int?>("GuardMaxOutputTokens")
.HasColumnType("int");
b.Property<string>("GuardModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<double?>("GuardTemperature")
.HasColumnType("float");
b.Property<int?>("ImplementerMaxOutputTokens")
.HasColumnType("int");
b.Property<string>("ImplementerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<double?>("ImplementerTemperature")
.HasColumnType("float");
b.Property<int?>("PlannerMaxOutputTokens")
.HasColumnType("int");
b.Property<string>("PlannerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<double?>("PlannerTemperature")
.HasColumnType("float");
b.Property<string>("RepoCommitSha")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("RepoRoot")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("RequestedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int?>("ReviewerMaxOutputTokens")
.HasColumnType("int");
b.Property<string>("ReviewerModel")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<double?>("ReviewerTemperature")
.HasColumnType("float");
b.Property<string>("SelectedFilesBundleSha256")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("TaskText")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<decimal?>("TotalCostUsd")
.HasColumnType("decimal(18,6)");
b.Property<long?>("TotalInputTokens")
.HasColumnType("bigint");
b.Property<long?>("TotalOutputTokens")
.HasColumnType("bigint");
b.HasKey("AaiAgentRunId");
b.HasIndex("CreatedUtc");
b.HasIndex("RepoCommitSha");
b.HasIndex("Status", "CreatedUtc");
b.ToTable("AAI_AgentRun", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiAgentRunStep", b =>
{
b.Property<long>("AaiAgentRunStepId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiAgentRunStepId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<decimal?>("CostUsd")
.HasColumnType("decimal(18,6)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<string>("Error")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<long?>("InputTokens")
.HasColumnType("bigint");
b.Property<int?>("MaxOutputTokens")
.HasColumnType("int");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<long?>("OutputTokens")
.HasColumnType("bigint");
b.Property<DateTime?>("StartedUtc")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<int>("StepType")
.HasColumnType("int");
b.Property<double?>("Temperature")
.HasColumnType("float");
b.HasKey("AaiAgentRunStepId");
b.HasIndex("AaiAgentRunId", "StepType");
b.HasIndex("Status", "StartedUtc");
b.ToTable("AAI_AgentRunStep", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiDotnetExecution", b =>
{
b.Property<long>("AaiDotnetExecutionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiDotnetExecutionId"));
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<string>("Command")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<DateTime?>("CompletedUtc")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<int?>("DurationMs")
.HasColumnType("int");
b.Property<int>("ExecutionType")
.HasColumnType("int");
b.Property<int>("ExitCode")
.HasColumnType("int");
b.Property<DateTime>("StartedUtc")
.HasColumnType("datetime2");
b.Property<string>("StdErr")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.Property<string>("StdOut")
.HasMaxLength(256)
.HasColumnType("nvarchar(max)");
b.HasKey("AaiDotnetExecutionId");
b.HasIndex("CreatedUtc");
b.HasIndex("AaiAgentRunId", "ExecutionType");
b.ToTable("AAI_DotnetExecution", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardReport", b =>
{
b.Property<long>("AaiGuardReportId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardReportId"));
b.Property<long?>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<long>("AaiAgentRunId")
.HasColumnType("bigint");
b.Property<bool>("Allowed")
.HasColumnType("bit");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("PolicyVersion")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.HasKey("AaiGuardReportId");
b.HasIndex("AaiAgentPatchSetId");
b.HasIndex("AaiAgentRunId");
b.HasIndex("AaiAgentRunId", "CreatedUtc");
b.ToTable("AAI_GuardReport", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiGuardViolation", b =>
{
b.Property<long>("AaiGuardViolationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiGuardViolationId"));
b.Property<long>("AaiGuardReportId")
.HasColumnType("bigint");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("datetime2");
b.Property<string>("Message")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("Path")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("Severity")
.HasColumnType("int");
b.Property<string>("Suggestion")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.HasKey("AaiGuardViolationId");
b.HasIndex("AaiGuardReportId", "Severity");
b.ToTable("AAI_GuardViolation", "dbo");
});
modelBuilder.Entity("BettingOdds.Domain.Entities.Agents.AaiPatchApplyAttempt", b =>
{
b.Property<long>("AaiPatchApplyAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("AaiPatchApplyAttemptId"));
b.Property<long>("AaiAgentPatchSetId")
.HasColumnType("bigint");
b.Property<string>("AppliedByUserId")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("AppliedUtc")
.HasColumnType("datetime2");
b.Property<int?>("CommitExitCode")
.HasColumnType("int");
b.Property<string>("CommitMessage")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("CommitShaAfterApply")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("CommitStdErr")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<string>("CommitStdOut")
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.Property<DateTime?>
/* ... truncated for agent context ... */
# FILE: Data/AppDbContext.cs
using BettingOdds.Domain.Entities;
using BettingOdds.Domain.Entities.Agents;
using Microsoft.EntityFrameworkCore;
namespace BettingOdds.Data;
public sealed class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
// --- DbSets ---
public DbSet<NbaTeam> NbaTeams => Set<NbaTeam>();
public DbSet<NbaSeason> NbaSeasons => Set<NbaSeason>();
public DbSet<NbaTeamStatsSnapshot> NbaTeamStatsSnapshots => Set<NbaTeamStatsSnapshot>();
public DbSet<NbaGame> NbaGames => Set<NbaGame>();
public DbSet<NbaPlayer> NbaPlayers => Set<NbaPlayer>();
public DbSet<NbaPlayerTeam> NbaPlayerTeams => Set<NbaPlayerTeam>();
public DbSet<NbaPlayerGameStat> NbaPlayerGameStats => Set<NbaPlayerGameStat>();
public DbSet<NbaPlayerRelevanceSnapshot> NbaPlayerRelevanceSnapshots => Set<NbaPlayerRelevanceSnapshot>();
public DbSet<NbaPlayerRollingStatsSnapshot> NbaPlayerRollingStatsSnapshots => Set<NbaPlayerRollingStatsSnapshot>();
// --- Agentic AI (AAI) DbSets ---
public DbSet<AaiAgentRun> AaiAgentRuns => Set<AaiAgentRun>();
public DbSet<AaiAgentRunStep> AaiAgentRunSteps => Set<AaiAgentRunStep>();
public DbSet<AaiAgentMessage> AaiAgentMessages => Set<AaiAgentMessage>();
public DbSet<AaiRepoFileSnapshot> AaiRepoFileSnapshots => Set<AaiRepoFileSnapshot>();
public DbSet<AaiAgentPatchSet> AaiAgentPatchSets => Set<AaiAgentPatchSet>();
public DbSet<AaiAgentPatch> AaiAgentPatches => Set<AaiAgentPatch>();
public DbSet<AaiPatchApplyAttempt> AaiPatchApplyAttempts => Set<AaiPatchApplyAttempt>();
public DbSet<AaiGuardReport> AaiGuardReports => Set<AaiGuardReport>();
public DbSet<AaiGuardViolation> AaiGuardViolations => Set<AaiGuardViolation>();
public DbSet<AaiDotnetExecution> AaiDotnetExecutions => Set<AaiDotnetExecution>();
public DbSet<AaiPatchPreflightReport> AaiPatchPreflightReports => Set<AaiPatchPreflightReport>();
public DbSet<AaiPatchPreflightViolation> AaiPatchPreflightViolations => Set<AaiPatchPreflightViolation>();
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
// Global defaults (explicit property mappings below still override these)
configurationBuilder.Properties<decimal>().HaveColumnType("decimal(18,6)");
configurationBuilder.Properties<string>().HaveMaxLength(256);
}
protected override void OnModelCreating(ModelBuilder model)
{
// Force dbo schema (prevents accidental db_owner schema issues)
model.HasDefaultSchema(DbNames.Schema);
ConfigureTeams(model);
ConfigureSeasons(model);
ConfigureTeamSnapshots(model);
ConfigureGames(model);
ConfigurePlayers(model);
ConfigurePlayerTeams(model);
ConfigurePlayerGameStats(model);
ConfigurePlayerRelevanceSnapshots(model);
ConfigurePlayerRollingStatsSnapshots(model);
ConfigureAai(model);
ConfigureAaiPatchPreflight(model);
}
private static class DbNames
{
public const string Schema = "dbo";
public const string NbaTeams = "NbaTeams";
public const string NbaSeasons = "NbaSeasons";
public const string NbaTeamStatsSnapshots = "NbaTeamStatsSnapshots";
public const string NbaGames = "NbaGames";
public const string NbaPlayers = "NbaPlayers";
public const string NbaPlayerTeams = "NbaPlayerTeams";
public const string NbaPlayerGameStats = "NbaPlayerGameStats";
public const string NbaPlayerRelevanceSnapshots = "NbaPlayerRelevanceSnapshots";
public const string NbaPlayerRollingStatsSnapshots = "NbaPlayerRollingStatsSnapshots";
// --- Agentic AI (AAI) ---
public const string AaiAgentRun = "AAI_AgentRun";
public const string AaiAgentRunStep = "AAI_AgentRunStep";
public const string AaiAgentMessage = "AAI_AgentMessage";
public const string AaiRepoFileSnapshot = "AAI_RepoFileSnapshot";
public const string AaiAgentPatchSet = "AAI_AgentPatchSet";
public const string AaiAgentPatch = "AAI_AgentPatch";
public const string AaiPatchApplyAttempt = "AAI_PatchApplyAttempt";
public const string AaiGuardReport = "AAI_GuardReport";
public const string AaiGuardViolation = "AAI_GuardViolation";
public const string AaiDotnetExecution = "AAI_DotnetExecution";
public const string AaiPatchPreflightReports = "AAI_PatchPreflightReports";
public const string AaiPatchPreflightViolations = "AAI_PatchPreflightViolations";
}
private static void ConfigureTeams(ModelBuilder model)
{
model.Entity<NbaTeam>(e =>
{
e.ToTable(DbNames.NbaTeams);
e.HasKey(x => x.TeamId);
// NBA TEAM_ID is provided externally (NOT identity)
e.Property(x => x.TeamId).ValueGeneratedNever();
e.Property(x => x.Abbr).HasMaxLength(6);
e.Property(x => x.City).HasMaxLength(40);
e.Property(x => x.Name).HasMaxLength(60);
e.Property(x => x.FullName).HasMaxLength(80);
e.HasIndex(x => x.Abbr);
});
}
private static void ConfigureSeasons(ModelBuilder model)
{
model.Entity<NbaSeason>(e =>
{
e.ToTable(DbNames.NbaSeasons);
e.HasKey(x => x.SeasonId);
// Identity PK
e.Property(x => x.SeasonId).ValueGeneratedOnAdd();
e.Property(x => x.SeasonCode).HasMaxLength(16).IsRequired();
e.Property(x => x.SeasonType).HasMaxLength(32).IsRequired();
e.HasIndex(x => new { x.SeasonCode, x.SeasonType }).IsUnique();
});
}
private static void ConfigureTeamSnapshots(ModelBuilder model)
{
model.Entity<NbaTeamStatsSnapshot>(e =>
{
e.ToTable(DbNames.NbaTeamStatsSnapshots);
e.HasKey(x => x.SnapshotId);
// Identity PK (critical)
e.Property(x => x.SnapshotId).ValueGeneratedOnAdd();
e.Property(x => x.BatchId).IsRequired();
e.Property(x => x.PulledAtUtc).IsRequired();
e.Property(x => x.OffRtg).HasColumnType("decimal(7,3)");
e.Property(x => x.DefRtg).HasColumnType("decimal(7,3)");
e.Property(x => x.Pace).HasColumnType("decimal(7,3)");
e.Property(x => x.NetRtg).HasColumnType("decimal(7,3)");
e.HasOne(x => x.Team)
.WithMany(t => t.StatSnapshots)
.HasForeignKey(x => x.TeamId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.Season)
.WithMany(s => s.TeamStats)
.HasForeignKey(x => x.SeasonId)
.OnDelete(DeleteBehavior.Restrict);
// One record per team per batch (season+lastN)
e.HasIndex(x => new { x.SeasonId, x.LastNGames, x.BatchId, x.TeamId }).IsUnique();
// Fast "latest batch" lookups
e.HasIndex(x => new { x.SeasonId, x.LastNGames, x.PulledAtUtc });
e.HasIndex(x => x.PulledAtUtc);
});
}
private static void ConfigureGames(ModelBuilder model)
{
model.Entity<NbaGame>(e =>
{
e.ToTable(DbNames.NbaGames);
e.HasKey(x => x.GameId);
// String PK from NBA API
e.Property(x => x.GameId).HasMaxLength(20).ValueGeneratedNever();
e.Property(x => x.Status).HasMaxLength(30);
e.Property(x => x.Arena).HasMaxLength(120);
e.Property(x => x.GameDateUtc).IsRequired();
e.Property(x => x.LastSyncedUtc).IsRequired();
e.HasOne(x => x.Season)
.WithMany(s => s.Games)
.HasForeignKey(x => x.SeasonId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.HomeTeam)
.WithMany()
.HasForeignKey(x => x.HomeTeamId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.AwayTeam)
.WithMany()
.HasForeignKey(x => x.AwayTeamId)
.OnDelete(DeleteBehavior.Restrict);
e.HasIndex(x => x.GameDateUtc);
e.HasIndex(x => new { x.SeasonId, x.GameDateUtc });
e.HasIndex(x => new { x.HomeTeamId, x.GameDateUtc });
e.HasIndex(x => new { x.AwayTeamId, x.GameDateUtc });
});
}
private static void ConfigurePlayers(ModelBuilder model)
{
model.Entity<NbaPlayer>(e =>
{
e.ToTable(DbNames.NbaPlayers);
e.HasKey(x => x.PlayerId);
// NBA PERSON_ID is provided externally (NOT identity)
e.Property(x => x.PlayerId).ValueGeneratedNever();
e.Property(x => x.DisplayName).HasMaxLength(80);
e.Property(x => x.FirstName).HasMaxLength(40);
e.Property(x => x.LastName).HasMaxLength(40);
e.HasIndex(x => x.DisplayName);
});
}
private static void ConfigurePlayerTeams(ModelBuilder model)
{
model.Entity<NbaPlayerTeam>(e =>
{
e.ToTable(DbNames.NbaPlayerTeams);
e.HasKey(x => x.PlayerTeamId);
// Identity PK
e.Property(x => x.PlayerTeamId).ValueGeneratedOnAdd();
e.Property(x => x.StartDateUtc).IsRequired();
e.HasOne(x => x.Player).WithMany().HasForeignKey(x => x.PlayerId).OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.Team).WithMany().HasForeignKey(x => x.TeamId).OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.Season).WithMany().HasForeignKey(x => x.SeasonId).OnDelete(DeleteBehavior.Restrict);
e.HasIndex(x => new { x.PlayerId, x.TeamId, x.SeasonId, x.StartDateUtc }).IsUnique();
});
}
private static void ConfigurePlayerGameStats(ModelBuilder model)
{
model.Entity<NbaPlayerGameStat>(e =>
{
e.ToTable(DbNames.NbaPlayerGameStats);
e.HasKey(x => x.PlayerGameStatId);
// Identity PK
e.Property(x => x.PlayerGameStatId).ValueGeneratedOnAdd();
e.Property(x => x.Minutes).HasColumnType("decimal(5,2)");
e.HasOne(x => x.Game).WithMany().HasForeignKey(x => x.GameId).OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.Player).WithMany().HasForeignKey(x => x.PlayerId).OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.Team).WithMany().HasForeignKey(x => x.TeamId).OnDelete(DeleteBehavior.Restrict);
e.HasIndex(x => new { x.GameId, x.PlayerId }).IsUnique();
});
}
private static void ConfigurePlayerRelevanceSnapshots(ModelBuilder model)
{
model.Entity<NbaPlayerRelevanceSnapshot>(e =>
{
e.ToTable(DbNames.NbaPlayerRelevanceSnapshots);
e.HasKey(x => x.PlayerRelevanceSnapshotId);
// Identity PK
e.Property(x => x.PlayerRelevanceSnapshotId).ValueGeneratedOnAdd();
e.Property(x => x.AsOfUtc).IsRequired();
e.Property(x => x.BatchId).IsRequired();
e.Property(x => x.CreatedUtc).IsRequired();
// 2-decimal outputs (your standard)
e.Property(x => x.RelevanceScore).HasColumnType("decimal(6,2)");
e.Property(x => x.MinutesSharePct).HasColumnType("decimal(6,2)");
e.Property(x => x.UsageProxy).HasColumnType("decimal(6,2)");
e.Property(x => x.RecentMinutesAvg).HasColumnType("decimal(7,2)");
e.Property(x => x.AvailabilityFactor).HasColumnType("decimal(6,4)");
e.HasOne(x => x.Season).WithMany().HasForeignKey(x => x.SeasonId).OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.Team).WithMany().HasForeignKey(x => x.TeamId).OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.Player).WithMany().HasForeignKey(x => x.PlayerId).OnDelete(DeleteBehavior.Restrict);
// One snapshot row per player/team/season/as-of
e.HasIndex(x => new { x.SeasonId, x.TeamId, x.PlayerId, x.AsOfUtc }).IsUnique();
// Fast “latest snapshot” lookups per team
e.HasIndex(x => new { x.SeasonId, x.TeamId, x.AsOfUtc });
e.HasIndex(x => x.AsOfUtc);
});
}
private static void ConfigurePlayerRollingStatsSnapshots(ModelBuilder model)
{
model.Entity<NbaPlayerRollingStatsSnapshot>(e =>
{
e.ToTable(DbNames.NbaPlayerRollingStatsSnapshots);
e.HasKey(x => x.SnapshotId);
// Identity PK
e.Property(x => x.SnapshotId).ValueGeneratedOnAdd();
e.Property(x => x.BatchId).IsRequired();
e.Property(x => x.AsOfUtc).IsRequired();
e.Property(x => x.LastNGames).IsRequired();
e.Property(x => x.CreatedUtc).IsRequired();
// Standard 2-decimal formatting for betting outputs
e.Property(x => x.MinutesAvg).HasColumnType("decimal(7,2)");
e.Property(x => x.PointsAvg).HasColumnType("decimal(7,2)");
e.Property(x => x.ReboundsAvg).HasColumnType("decimal(7,2)");
e.Property(x => x.AssistsAvg).HasColumnType("decimal(7,2)");
e.Property(x => x.TurnoversAvg).HasColumnType("decimal(7,2)");
// If you store 0..1 => (6,4). If 0..100 => (6,2).
e.Property(x => x.TS).HasColumnType("decimal(6,4)");
e.Property(x => x.ThreePpct).HasColumnType("decimal(6,4)");
e.HasOne<NbaSeason>()
.WithMany()
.HasForeignKey(x => x.SeasonId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne<NbaTeam>()
.WithMany()
.HasForeignKey(x => x.TeamId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne<NbaPlayer>()
.WithMany()
.HasForeignKey(x => x.PlayerId)
.OnDelete(DeleteBehavior.Restrict);
e.HasIndex(x => new { x.SeasonId, x.TeamId, x.PlayerId, x.LastNGames, x.AsOfUtc }).IsUnique();
e.HasIndex(x => new { x.SeasonId, x.TeamId, x.AsOfUtc });
e.HasIndex(x => new { x.SeasonId, x.PlayerId, x.AsOfUtc });
e.HasIndex(x => x.AsOfUtc);
});
}
private static void ConfigureAai(ModelBuilder model)
{
ConfigureAaiAgentRuns(model);
ConfigureAaiAgentRunSteps(model);
ConfigureAaiAgentMessages(model);
ConfigureAaiRepoFileSnapshots(model);
ConfigureAaiAgentPatchSets(model);
ConfigureAaiAgentPatches(model);
ConfigureAaiPatchApplyAttempts(model);
ConfigureAaiGuardReports(model);
ConfigureAaiGuardViolations(model);
ConfigureAaiDotnetExecutions(model);
}
private static void ConfigureAaiAgentRuns(ModelBuilder model)
{
model.Entity<AaiAgentRun>(e =>
{
e.ToTable(DbNames.AaiAgentRun);
e.HasKey(x => x.AaiAgentRunId);
e.Property(x => x.AaiAgentRunId).ValueGeneratedOnAdd();
e.Property(x => x.TaskText).HasMaxLength(4000).IsRequired();
e.Property(x => x.RequestedByUserId).HasMaxLength(128);
e.Property(x => x.RepoRoot).HasMaxLength(400);
e.Property(x => x.RepoCommitSha).HasMaxLength(64);
e.Property(x => x.GovernanceBundleSha256).HasMaxLength(64);
e.Property(x => x.SelectedFilesBundleSha256).HasMaxLength(64);
e.Property(x => x.PlannerModel).HasMaxLength(100);
e.Property(x => x.ImplementerModel).HasMaxLength(100);
e.Property(x => x.ReviewerModel).HasMaxLength(100);
e.Property(x => x.GuardModel).HasMaxLength(100);
e.Property(x => x.CreatedUtc).IsRequired();
e.Property(x => x.Status).IsRequired();
e.Property(x => x.TotalCostUsd).HasColumnType("decimal(18,6)");
e.HasIndex(x => x.CreatedUtc);
e.HasIndex(x => new { x.Status, x.CreatedUtc });
e.HasIndex(x => x.RepoCommitSha);
});
}
private static void ConfigureAaiAgentRunSteps(ModelBuilder model)
{
model.Entity<AaiAgentRunStep>(e =>
{
e.ToTable(DbNames.AaiAgentRunStep);
e.HasKey(x => x.AaiAgentRunStepId);
e.Property(x => x.AaiAgentRunStepId).ValueGeneratedOnAdd();
e.Property(x => x.StepType).IsRequired();
e.Property(x => x.Status).IsRequired();
e.Property(x => x.Model).HasMaxLength(100);
e.Property(x => x.Error).HasMaxLength(4000);
e.Property(x => x.CreatedUtc).IsRequired();
e.Property(x => x.CostUsd).HasColumnType("decimal(18,6)");
e.HasOne(x => x.AgentRun)
.WithMany(r => r.Steps)
.HasForeignKey(x => x.AaiAgentRunId)
.OnDelete(DeleteBehavior.Cascade);
e.HasIndex(x => new { x.AaiAgentRunId, x.StepType });
e.HasIndex(x => new { x.Status, x.StartedUtc });
});
}
private static void ConfigureAaiAgentMessages(ModelBuilder model)
{
model.Entity<AaiAgentMessage>(e =>
{
e.ToTable(DbNames.AaiAgentMessage);
e.HasKey(x => x.AaiAgentMessageId);
e.Property(x => x.AaiAgentMessageId).ValueGeneratedOnAdd();
e.Property(x => x.Role).IsRequired();
e.Property(x => x.JsonSchemaName).HasMaxLength(120);
/* ... truncated for agent context ... */
# FILE: Pages/Nba/Games.cshtml.cs
using BettingOdds.App.Queries.Games;
using BettingOdds.App.Time;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace BettingOdds.Pages.Nba;
public class GamesModel : PageModel
{
private readonly IGamesQuery _games;
private readonly IAppClock _clock;
public List<GameRow> Games { get; private set; } = new();
// NEW: for the view (avoid DateTime.UtcNow inside cshtml)
public DateTime TodayUtcStart { get; private set; }
public DateTime UpcomingEndUtcExclusive { get; private set; }
public List<IGrouping<DateTime, GameRow>> UpcomingDays { get; private set; } = new();
public IGrouping<DateTime, GameRow>? LastDayBeforeToday { get; private set; }
public GamesModel(IGamesQuery games, IAppClock clock)
{
_games = games;
_clock = clock;
}
public async Task OnGet(CancellationToken ct)
{
TodayUtcStart = _clock.GetTodayUtcStart();
UpcomingEndUtcExclusive = TodayUtcStart.AddDays(3);
Games = await _games.GetGamesWindowAsync(
todayUtcStart: TodayUtcStart,
upcomingDays: 3,
lookbackDays: 30,
ct: ct);
// Group for the view
UpcomingDays = Games
.Where(g => g.GameDateUtc >= TodayUtcStart && g.GameDateUtc < UpcomingEndUtcExclusive)
.GroupBy(g => g.GameDateUtc.Date)
.OrderBy(g => g.Key)
.ToList();
LastDayBeforeToday = Games
.Where(g => g.GameDateUtc < TodayUtcStart)
.GroupBy(g => g.GameDateUtc.Date)
.OrderByDescending(g => g.Key)
.FirstOrDefault();
}
}
# FILE: Pages/Nba/Matchup.cshtml.cs
using BettingOdds.App.Nba.Queries.Matchup;
using BettingOdds.App.Nba.Sync.GameSync;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace BettingOdds.Pages.Nba;
public class MatchupModel : PageModel
{
private readonly IMatchupQuery _query;
private readonly IGameSyncOrchestrator _gameSync;
public MatchupVm? Vm { get; private set; }
public MatchupModel(IMatchupQuery query, IGameSyncOrchestrator gameSync)
{
_query = query;
_gameSync = gameSync;
}
public async Task<IActionResult> OnGet(
int? homeId,
int? awayId,
double? totalLine,
string? gameId,
CancellationToken ct)
{
if (homeId is null || awayId is null || homeId == 0 || awayId == 0)
return BadRequest("Missing homeId/awayId.");
try
{
Vm = await _query.GetAsync(homeId.Value, awayId.Value, totalLine, gameId, ct);
return Page();
}
catch (InvalidOperationException ex)
{
return BadRequest(ex.Message);
}
}
public async Task<IActionResult> OnPostSyncGameAsync(string gameId, int homeId, int awayId, double? totalLine, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(gameId))
return RedirectToPage(new { homeId, awayId });
await _gameSync.SyncSingleGameAsync(gameId, ct);
return RedirectToPage(new { homeId, awayId, gameId, totalLine });
}
}
# FILE: Pages/Nba/Player.cshtml.cs
using BettingOdds.App.Queries.Player;
using BettingOdds.Domain.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace BettingOdds.Pages.Nba;
public class PlayerModel : PageModel
{
private readonly IPlayerQuery _query;
public PlayerModel(IPlayerQuery query)
{
_query = query;
}
[BindProperty(SupportsGet = true)]
public int PlayerId { get; set; }
[BindProperty(SupportsGet = true)]
public int? TeamId { get; set; }
[BindProperty(SupportsGet = true)]
public int LastNGames { get; set; } = 10;
public int Take { get; private set; } = 30;
public string? PlayerTitle { get; private set; }
public string? TeamLabel { get; private set; }
public DateTime? LatestAsOfUtc { get; private set; }
public string? Error { get; private set; }
public List<NbaPlayerRollingStatsSnapshot> Trend { get; private set; } = new();
public PlayerPageVm.RelevanceKpi? LatestRelevance { get; private set; }
public PlayerPageVm.DeltaKpi? Deltas { get; private set; }
public async Task<IActionResult> OnGetAsync(CancellationToken ct)
{
try
{
var vm = await _query.GetPlayerPageAsync(
new PlayerQueryArgs(PlayerId, TeamId, LastNGames, Take),
ct);
PlayerTitle = vm.PlayerTitle;
TeamLabel = vm.TeamLabel;
LatestAsOfUtc = vm.LatestAsOfUtc;
Trend = vm.Trend.ToList();
LatestRelevance = vm.LatestRelevance;
Deltas = vm.Deltas;
return Page();
}
catch (Exception ex)
{
Error = ex.Message;
return Page();
}
}
public static string Signed2(decimal v) => (v >= 0 ? "+" : "") + v.ToString("0.00");
}
# FILE: Pages/Nba/Sync.cshtml.cs
using BettingOdds.App.Sync;
using BettingOdds.App.Sync.Modules;
using BettingOdds.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
namespace BettingOdds.Pages.Nba;
public class SyncModel : PageModel
{
private readonly ISyncCenter _syncCenter;
private readonly ITeamStatsSync _teamStats;
private readonly IScheduleSync _schedule;
private readonly IPlayerSync _players;
private readonly IPlayerSnapshotBuilder _snapshots;
private readonly AppDbContext _db;
private readonly IConfiguration _cfg;
public string? Message { get; private set; }
public DateTime? LastStatsPullUtc { get; private set; }
public Guid? LastBatchId { get; private set; }
public DateTime? LastScheduleSyncUtc { get; private set; }
// NEW statuses
public DateTime? LastPlayersSyncUtc { get; private set; }
public DateTime? LastRosterSyncUtc { get; private set; }
public DateTime? LastPlayerStatsSyncUtc { get; private set; }
public DateTime? LastPlayerRollingAsOfUtc { get; private set; }
public DateTime? LastPlayerRelevanceAsOfUtc { get; private set; }
public Guid? LastPlayerBatchId { get; private set; }
[BindProperty]
public int PastDays { get; set; } = 5;
public SyncModel(
ISyncCenter syncCenter,
ITeamStatsSync teamStats,
IScheduleSync schedule,
IPlayerSync players,
IPlayerSnapshotBuilder snapshots,
AppDbContext db,
IConfiguration cfg)
{
_syncCenter = syncCenter;
_teamStats = teamStats;
_schedule = schedule;
_players = players;
_snapshots = snapshots;
_db = db;
_cfg = cfg;
}
public async Task OnGet()
{
await LoadStatusAsync();
}
public async Task<IActionResult> OnPostRunAllAsync()
{
var r = await _syncCenter.RunAllAsync();
Message = $@"
✅ Teams upserted: {r.TeamsUpserted} | Team batch: {r.TeamBatchId}
✅ Games upserted: {r.GamesUpserted}
✅ Players upserted: {r.PlayersUpserted}
✅ Roster changes: {r.RosterChanges}
✅ Player stats inserted: {r.PlayerStatsInserted}
✅ Rolling snapshots: {r.RollingInserted}
✅ Relevance snapshots: {r.RelevanceInserted} | Player batch: {r.PlayerBatchId}
".Trim();
await LoadStatusAsync();
return Page();
}
public async Task<IActionResult> OnPostTeamsAndStatsAsync()
{
var opt = SyncOptions.FromConfig(_cfg);
var seasonId = await EnsureSeasonIdAsync(opt);
var (teams, batchId) = await _teamStats.SyncTeamsAndStatsAsync(seasonId, opt);
Message = $"Teams + Stats synced: Teams upserted: {teams}, Stats batch: {batchId}.";
await LoadStatusAsync();
return Page();
}
public async Task<IActionResult> OnPostScheduleAsync()
{
var opt = SyncOptions.FromConfig(_cfg);
var seasonId = await EnsureSeasonIdAsync(opt);
var today = DateTime.UtcNow.Date;
var to = today.AddDays(2);
var games = await _schedule.SyncScheduleRangeAsync(seasonId, today, to);
Message = $"Schedule synced: Games upserted: {games}.";
await LoadStatusAsync();
return Page();
}
public async Task<IActionResult> OnPostPastScheduleAsync()
{
if (PastDays < 1) PastDays = 1;
var opt = SyncOptions.FromConfig(_cfg);
var seasonId = await EnsureSeasonIdAsync(opt);
var to = DateTime.UtcNow.Date;
var from = to.AddDays(-PastDays);
var games = await _schedule.SyncScheduleRangeAsync(seasonId, from, to);
Message = $"Past schedule synced: {games} games upserted (last {PastDays} days).";
await LoadStatusAsync();
return Page();
}
// -----------------------
// NEW handlers
// -----------------------
public async Task<IActionResult> OnPostPlayersAsync()
{
var opt = SyncOptions.FromConfig(_cfg);
var players = await _players.SyncPlayersAsync(opt);
Message = $"Players synced: Players upserted: {players}.";
await LoadStatusAsync();
return Page();
}
public async Task<IActionResult> OnPostRostersAsync()
{
var opt = SyncOptions.FromConfig(_cfg);
var seasonId = await EnsureSeasonIdAsync(opt);
var changes = await _players.SyncRostersAsync(seasonId, opt);
Message = $"Rosters synced: Changes applied: {changes}.";
await LoadStatusAsync();
return Page();
}
public async Task<IActionResult> OnPostPlayerStatsAsync()
{
var opt = SyncOptions.FromConfig(_cfg);
var seasonId = await EnsureSeasonIdAsync(opt);
var inserted = await _players.SyncPlayerGameStatsForFinalGamesAsync(seasonId, opt, onlyGameId: null);
Message = $"Player game stats synced: Rows inserted: {inserted}.";
await LoadStatusAsync();
return Page();
}
public async Task<IActionResult> OnPostPlayerSnapshotsAsync()
{
var opt = SyncOptions.FromConfig(_cfg);
var seasonId = await EnsureSeasonIdAsync(opt);
var asOfUtc = DateTime.UtcNow;
var (rollingInserted, relevanceInserted, batchId) =
await _snapshots.BuildAsync(seasonId, opt, asOfUtc);
Message = $"Player snapshots built: Rolling={rollingInserted}, Relevance={relevanceInserted}, Batch={batchId}, AsOf={asOfUtc:u}.";
await LoadStatusAsync();
return Page();
}
// -----------------------
// Status
// -----------------------
private async Task LoadStatusAsync()
{
LastStatsPullUtc = await _db.NbaTeamStatsSnapshots
.OrderByDescending(x => x.PulledAtUtc)
.Select(x => (DateTime?)x.PulledAtUtc)
.FirstOrDefaultAsync();
LastBatchId = await _db.NbaTeamStatsSnapshots
.OrderByDescending(x => x.PulledAtUtc)
.Select(x => (Guid?)x.BatchId)
.FirstOrDefaultAsync();
LastScheduleSyncUtc = await _db.NbaGames
.OrderByDescending(x => x.LastSyncedUtc)
.Select(x => (DateTime?)x.LastSyncedUtc)
.FirstOrDefaultAsync();
// Best-effort: roster sync time is a better proxy than "DateTime.UtcNow"
LastRosterSyncUtc = await _db.NbaPlayerTeams
.OrderByDescending(x => x.StartDateUtc)
.Select(x => (DateTime?)x.StartDateUtc)
.FirstOrDefaultAsync();
// Players sync time (until Player has CreatedUtc/UpdatedUtc):
// Use same roster timestamp as proxy, but only if players exist.
var playersExist = await _db.NbaPlayers.AnyAsync();
LastPlayersSyncUtc = playersExist ? LastRosterSyncUtc : null;
LastPlayerStatsSyncUtc = await _db.NbaPlayerGameStats
.Join(_db.NbaGames, s => s.GameId, g => g.GameId, (s, g) => g.GameDateUtc)
.OrderByDescending(d => d)
.Select(d => (DateTime?)d)
.FirstOrDefaultAsync();
LastPlayerRollingAsOfUtc = await _db.NbaPlayerRollingStatsSnapshots
.OrderByDescending(x => x.AsOfUtc)
.Select(x => (DateTime?)x.AsOfUtc)
.FirstOrDefaultAsync();
LastPlayerRelevanceAsOfUtc = await _db.NbaPlayerRelevanceSnapshots
.OrderByDescending(x => x.AsOfUtc)
.Select(x => (DateTime?)x.AsOfUtc)
.FirstOrDefaultAsync();
LastPlayerBatchId = await _db.NbaPlayerRelevanceSnapshots
.OrderByDescending(x => x.AsOfUtc)
.Select(x => (Guid?)x.BatchId)
.FirstOrDefaultAsync();
}
/// <summary>
/// “Right way”: resolve season ID the same way the new pipeline does.
/// We keep this local helper so the page doesn’t need to inject ISeasonResolver too.
/// </summary>
private async Task<int> EnsureSeasonIdAsync(SyncOptions opt)
{
var season = await _db.NbaSeasons
.Where(s => s.SeasonCode == opt.SeasonCode && s.SeasonType == opt.SeasonType)
.Select(s => (int?)s.SeasonId)
.FirstOrDefaultAsync();
if (season.HasValue) return season.Value;
var entity = new BettingOdds.Domain.Entities.NbaSeason
{
SeasonCode = opt.SeasonCode,
SeasonType = opt.SeasonType,
IsActive = true
};
_db.NbaSeasons.Add(entity);
await _db.SaveChangesAsync();
return entity.SeasonId;
}
}
# FILE: Pages/Nba/SyncTeamStats.cshtml.cs
using BettingOdds.App.Queries;
using BettingOdds.App.Sync;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace BettingOdds.Pages.Nba;
public class SyncTeamStatsModel : PageModel
{
private readonly SyncTeamStatsUseCase _useCase;
private readonly ISyncTeamStatsQuery _query;
public SyncTeamStatsModel(SyncTeamStatsUseCase useCase, ISyncTeamStatsQuery query)
{
_useCase = useCase;
_query = query;
}
public string SeasonLabel { get; private set; } = "�";
public DateTime? LatestPulledAtUtc { get; private set; }
public Guid? LatestBatchId { get; private set; }
public List<SyncTeamStatsRowVm> Latest { get; private set; } = new();
[BindProperty(SupportsGet = true)]
public int? SelectedLastNGames { get; set; }
public async Task OnGetAsync(CancellationToken ct)
{
await LoadAsync(ct);
}
public async Task<IActionResult> OnPostSyncAsync(CancellationToken ct)
{
var lastN = SelectedLastNGames ?? 10;
await _useCase.RunAsync(lastN, ct);
// PRG pattern (preserve selection)
return RedirectToPage(new { SelectedLastNGames = lastN });
}
private async Task LoadAsync(CancellationToken ct)
{
var vm = await _query.GetPageAsync(SelectedLastNGames, ct);
SelectedLastNGames = vm.SelectedLastNGames;
SeasonLabel = vm.SeasonLabel;
LatestPulledAtUtc = vm.LatestPulledAtUtc;
LatestBatchId = vm.LatestBatchId;
Latest = vm.Latest.ToList();
}
}
# FILE: Pages/Nba/Team.cshtml.cs
using BettingOdds.App.Queries;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace BettingOdds.Pages.Nba;
public class TeamModel : PageModel
{
private readonly ITeamQuery _query;
public TeamModel(ITeamQuery query) => _query = query;
[BindProperty(SupportsGet = true)]
public int TeamId { get; set; }
[BindProperty(SupportsGet = true)]
public int LastNGames { get; set; } = 10;
public string? TeamTitle { get; private set; }
public string? LogoUrl { get; private set; }
public string SeasonLabel { get; private set; } = "�";
// Player snapshots as-of
public DateTime? AsOfUtc { get; private set; }
// Team advanced stats pulled time
public DateTime? TeamStatsAsOfUtc { get; private set; }
// Roster tier counts
public int CoreCount { get; private set; }
public int RotationCount { get; private set; }
public int BenchCount { get; private set; }
public int FringeCount { get; private set; }
// Team insight blocks
public TeamKpisVm? TeamKpis { get; private set; }
public List<TeamTrendPoint> Trend { get; private set; } = new();
public TeamChangeVm? Change { get; private set; }
public string? Error { get; private set; }
public List<TeamPlayerRowVm> Rows { get; private set; } = new();
public sealed record TeamPlayerRowVm(
int PlayerId,
string PlayerName,
byte Tier,
string TierLabel,
decimal RelevanceScore,
decimal MinutesSharePct,
decimal MinutesAvg,
decimal PointsAvg,
decimal ReboundsAvg,
decimal AssistsAvg,
decimal TS,
decimal ThreePpct
);
public async Task<IActionResult> OnGetAsync(CancellationToken ct)
{
try
{
// Take and TrendTake can be tuned as you like.
var vm = await _query.GetTeamPageAsync(
new TeamQueryArgs(TeamId, LastNGames, Take: 12, TrendTake: 20),
ct
);
TeamTitle = vm.TeamTitle;
LogoUrl = vm.LogoUrl;
SeasonLabel = vm.SeasonLabel;
AsOfUtc = vm.AsOfUtc;
TeamStatsAsOfUtc = vm.TeamStatsAsOfUtc;
CoreCount = vm.TierCounts.Core;
RotationCount = vm.TierCounts.Rotation;
BenchCount = vm.TierCounts.Bench;
FringeCount = vm.TierCounts.Fringe;
TeamKpis = vm.TeamKpis;
Trend = vm.Trend?.ToList() ?? new List<TeamTrendPoint>();
Change = vm.Change;
Rows = vm.Rows.Select(r => new TeamPlayerRowVm(
r.PlayerId, r.PlayerName, r.Tier, r.TierLabel,
r.RelevanceScore, r.MinutesSharePct,
r.MinutesAvg, r.PointsAvg, r.ReboundsAvg, r.AssistsAvg,
r.TS, r.ThreePpct
)).ToList();
return Page();
}
catch (Exception ex)
{
Error = ex.Message;
return Page();
}
}
}
# FILE: Pages/Nba/Teams.cshtml.cs
using BettingOdds.App.Queries;
using BettingOdds.Domain.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace BettingOdds.Pages.Nba;
public class TeamsModel : PageModel
{
private readonly ITeamsQuery _query;
public List<NbaTeam> Teams { get; private set; } = new();
public int TotalTeams { get; private set; }
public string SeasonLabel { get; private set; } = "�";
public DateTime? LastUpdatedUtc { get; private set; }
[BindProperty(SupportsGet = true)]
public string? Q { get; set; }
public TeamsModel(ITeamsQuery query)
{
_query = query;
}
public async Task OnGetAsync(CancellationToken ct)
{
var vm = await _query.GetTeamsPageAsync(ct);
Teams = vm.Teams.ToList();
TotalTeams = vm.TotalTeams;
SeasonLabel = vm.SeasonLabel;
LastUpdatedUtc = vm.LastUpdatedUtc;
}
}
# FILE: Pages/Nba/Games.cshtml
@page
@model BettingOdds.Pages.Nba.GamesModel
@{
string StatusBadge(string status)
{
if (string.IsNullOrWhiteSpace(status)) return "bg-secondary";
if (status.Contains("Final", StringComparison.OrdinalIgnoreCase)) return "bg-success";
if (status.Contains("In Progress", StringComparison.OrdinalIgnoreCase)) return "bg-warning";
if (status.Contains("Q", StringComparison.OrdinalIgnoreCase) || status.Contains("Half", StringComparison.OrdinalIgnoreCase)) return "bg-warning";
if (status.Contains("ET", StringComparison.OrdinalIgnoreCase) ||
status.Contains("PM", StringComparison.OrdinalIgnoreCase) ||
status.Contains("AM", StringComparison.OrdinalIgnoreCase))
return "bg-secondary";
return "bg-secondary";
}
// Use model-provided groupings (no DateTime.UtcNow.Date here)
var upcomingDays = Model.UpcomingDays;
var lastDayBeforeToday = Model.LastDayBeforeToday;
}
<div class="container py-4 poc-shell">
<div class="poc-card p-4 p-md-5">
<!-- Header -->
<div class="d-flex flex-column flex-lg-row align-items-lg-center justify-content-between gap-3">
<div>
<h2 class="mb-1 poc-title">Upcoming Games</h2>
<div class="poc-muted">
Next <b>3 days</b> from your database. Times shown in <b>UTC</b>.
</div>
<div class="mt-3 d-flex flex-wrap gap-2">
<span class="poc-pill">Games: @Model.Games.Count</span>
<span class="poc-pill">Window: 3 days</span>
<span class="poc-pill">Timezone: UTC</span>
</div>
</div>
<div class="d-flex flex-wrap gap-2">
<a class="btn btn-outline-light btn-lg px-4" asp-page="/Index">Back</a>
<a asp-page="/Nba/Sync" class="btn poc-btn-gradient btn-lg px-4">
Sync Center
</a>
</div>
</div>
<div class="mt-4 poc-divider"></div>
@if (!Model.Games.Any())
{
<div class="mt-4 poc-muted">
No games found. Go to <b>Sync Schedule</b> to import the next 3 days.
</div>
}
else
{
<!-- Grouped by day -->
<div class="mt-4 d-flex flex-column gap-4">
@* Upcoming next 3 days *@
@foreach (var day in upcomingDays)
{
<div class="poc-card p-3 p-md-4">
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-2">
<div>
<div class="fw-semibold">@day.Key.ToString("dddd, yyyy-MM-dd")</div>
<div class="poc-muted small">@day.Count() game(s)</div>
</div>
<span class="poc-pill">
Day (UTC): @day.Key.ToString("yyyy-MM-dd")
</span>
</div>
<div class="mt-3 table-responsive">
<table class="table poc-table align-middle mb-0 poc-responsive-table">
<thead class="d-none d-md-table-header-group">
<tr>
<th>Matchup</th>
<th>Status</th>
<th>Game</th>
<th class="text-end"></th>
</tr>
</thead>
<tbody>
@foreach (var g in day.OrderBy(x => x.GameDateUtc).ThenBy(x => x.GameId))
{
<tr class="poc-row-hover">
<!-- MATCHUP -->
<td class="col-matchup">
<div class="mu-pill">
<!-- Away -->
<div class="mu-team mu-away">
<div class="mu-logo">
<img src="@(g.AwayLogoUrl ?? "/img/ui/team-fallback.png")"
alt="@g.Away logo"
class="mu-logo-img"
loading="lazy"
onerror="this.onerror=null; this.src='/img/ui/team-fallback.png';" />
</div>
<div class="mu-text">
<div class="mu-name">@g.Away</div>
<div class="mu-sub">Away</div>
</div>
</div>
<div class="mu-vs">VS</div>
<!-- Home -->
<div class="mu-team mu-home">
<div class="mu-text text-end">
<div class="mu-name">@g.Home</div>
<div class="mu-sub">Home</div>
</div>
<div class="mu-logo">
<img src="@(g.HomeLogoUrl ?? "/img/ui/team-fallback.png")"
alt="@g.Home logo"
class="mu-logo-img"
loading="lazy"
onerror="this.onerror=null; this.src='/img/ui/team-fallback.png';" />
</div>
</div>
</div>
</td>
<!-- STATUS -->
<td class="col-status">
<div class="mobile-label d-md-none">Status</div>
<span class="badge rounded-pill @StatusBadge(g.Status)">
@g.Status
</span>
</td>
<!-- GAME -->
<td class="col-game">
<div class="mobile-label d-md-none">Game</div>
<div>
<div class="poc-muted small">GameId</div>
<div class="fw-semibold">@g.GameId</div>
</div>
</td>
<!-- ACTION -->
<td class="col-action text-end">
<div class="mobile-label d-md-none">Action</div>
<a asp-page="/Nba/Matchup"
asp-route-homeId="@g.HomeId"
asp-route-awayId="@g.AwayId"
class="btn btn-sm poc-btn-gradient px-3 w-100 w-md-auto">
Project
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
@* Last day before today *@
@if (lastDayBeforeToday != null)
{
<div class="poc-card p-3 p-md-4">
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-2">
<div>
<div class="fw-semibold">Last Day With Matches</div>
<div class="poc-muted small">
@lastDayBeforeToday.Key.ToString("dddd, yyyy-MM-dd") • @lastDayBeforeToday.Count() game(s)
</div>
</div>
<span class="poc-pill">
Day (UTC): @lastDayBeforeToday.Key.ToString("yyyy-MM-dd")
</span>
</div>
<div class="mt-3 table-responsive">
<table class="table poc-table align-middle mb-0 poc-responsive-table">
<thead class="d-none d-md-table-header-group">
<tr>
<th>Matchup</th>
<th>Status</th>
<th>Game</th>
<th class="text-end"></th>
</tr>
</thead>
<tbody>
@foreach (var g in lastDayBeforeToday.OrderBy(x => x.GameDateUtc).ThenBy(x => x.GameId))
{
<tr class="poc-row-hover">
<!-- MATCHUP -->
<td class="col-matchup">
<div class="mu-pill">
<!-- Away -->
<div class="mu-team mu-away">
<div class="mu-logo">
<img src="@(g.AwayLogoUrl ?? "/img/ui/team-fallback.png")"
alt="@g.Away logo"
class="mu-logo-img"
loading="lazy"
onerror="this.onerror=null; this.src='/img/ui/team-fallback.png';" />
</div>
<div class="mu-text">
<div class="mu-name">@g.Away</div>
<div class="mu-sub">Away</div>
</div>
</div>
<div class="mu-vs">VS</div>
<!-- Home -->
<div class="mu-team mu-home">
<div class="mu-text text-end">
<div class="mu-name">@g.Home</div>
<div class="mu-sub">Home</div>
</div>
<div class="mu-logo">
<img src="@(g.HomeLogoUrl ?? "/img/ui/team-fallback.png")"
alt="@g.Home logo"
class="mu-logo-img"
loading="lazy"
onerror="this.onerror=null; this.src='/img/ui/team-fallback.png';" />
</div>
</div>
</div>
</td>
<!-- STATUS -->
<td class="col-status">
<div class="mobile-label d-md-none">Status</div>
<span class="badge rounded-pill @StatusBadge(g.Status)">
@g.Status
</span>
</td>
<!-- GAME -->
<td class="col-game">
<div class="mobile-label d-md-none">Game</div>
<div>
<div class="poc-muted small">GameId</div>
<div class="fw-semibold">@g.GameId</div>
</div>
</td>
<!-- ACTION -->
<td class="col-action text-end">
<div class="mobile-label d-md-none">Action</div>
<a asp-page="/Nba/Matchup"
asp-route-homeId="@g.HomeId"
asp-route-awayId="@g.AwayId"
class="btn btn-sm poc-btn-gradient px-3 w-100 w-md-auto">
Project
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
</div>
}
</div>
</div>
# FILE: Pages/Nba/Matchup.cshtml
@page
@model BettingOdds.Pages.Nba.MatchupModel
@{
// ---------------------------------------
// Odds helpers
// ---------------------------------------
string ToAmerican(double p)
{
if (p <= 0 || p >= 1) return "—";
var odds = p >= 0.5
? -(p / (1 - p)) * 100.0
: ((1 - p) / p) * 100.0;
return odds >= 0 ? $"+{odds:0}" : $"{odds:0}";
}
string ToDecimal(double p)
{
if (p <= 0 || p >= 1) return "—";
return (1.0 / p).ToString("0.00");
}
// ---------------------------------------
// Spread conventions (Option A sportsbook)
// ---------------------------------------
// baseline spread is "expected margin" (Home - Away)
// sportsbook line is the opposite sign: Home -X when expected margin is +X
string ToSpreadLineFromMargin(double spreadHomeMinusAway)
{
var line = -spreadHomeMinusAway;
return line >= 0 ? $"+{line:0.00}" : $"{line:0.00}";
}
// fair engine returns sportsbook-style already: Home -X => negative
string ToSpreadLineFromSportsbook(decimal homeLine)
{
var line = (double)homeLine;
return line >= 0 ? $"+{line:0.00}" : $"{line:0.00}";
}
bool IsFinal(string? status)
=> !string.IsNullOrWhiteSpace(status)
&& status.Contains("Final", StringComparison.OrdinalIgnoreCase);
string ScoreOrDash(int? v) => v.HasValue ? v.Value.ToString() : "—";
string BadgeDelta(double? v, string unit = "")
{
if (!v.HasValue) return "—";
var x = v.Value;
var s = x.ToString("+0.00;-0.00;0.00");
return string.IsNullOrWhiteSpace(unit) ? s : $"{s}{unit}";
}
string BadgeDeltaPct(double? v)
{
if (!v.HasValue) return "—";
return (v.Value * 100.0).ToString("+0.00;-0.00;0.00") + "%";
}
// ---------------------------------------
// Premium UI helpers (no extra CSS needed)
// ---------------------------------------
double Clamp01(double x) => x < 0 ? 0 : x > 1 ? 1 : x;
string ConfidenceColor(double conf01)
{
// simple 3-tier color scaling
if (conf01 >= 0.75) return "#38d996"; // green-ish
if (conf01 >= 0.50) return "#f6c343"; // amber-ish
return "#ff6b6b"; // red-ish
}
string BarColor(bool isHome)
=> isHome ? "#4aa3ff" : "#ff7a45"; // home blue / away orange (inline only)
// team factor bars: show deviation around 1.00
// map [0.80..1.20] => [0..100] as a visual (clamped)
int FactorToPct(double factor)
{
var pct = (factor - 0.80) / 0.40; // 0..1
return (int)Math.Round(Clamp01(pct) * 100.0);
}
string FactorLabel(double factor)
=> factor.ToString("0.000"); // nice, premium precision
}
<div class="container py-4 poc-shell">
<div class="poc-card p-4 p-md-5">
<!-- Header -->
<div class="d-flex flex-column flex-lg-row align-items-lg-center justify-content-between gap-3">
<div>
<h2 class="mb-1 poc-title">Matchup</h2>
<div class="poc-muted">
Baseline projection (team ratings) + player-adjusted fair lines (relevance snapshots).
</div>
<div class="mt-3 d-flex flex-wrap gap-2">
<span class="poc-pill">Output: Spread • Total • Moneyline</span>
<span class="poc-pill">Spread convention: Sportsbook (Home -X favored)</span>
@if (!string.IsNullOrWhiteSpace(Model.Vm?.GameStatus))
{
<span class="poc-pill">Status: @Model.Vm!.GameStatus</span>
}
@if (!string.IsNullOrWhiteSpace(Model.Vm?.Arena))
{
<span class="poc-pill">@Model.Vm!.Arena</span>
}
@if (!string.IsNullOrWhiteSpace(Model.Vm?.GameId))
{
<span class="poc-pill">Game: @Model.Vm!.GameId</span>
}
</div>
</div>
<div class="d-flex gap-2">
<a asp-page="/Nba/Games" class="btn btn-outline-light btn-lg px-4">
Back to Games
</a>
</div>
</div>
<div class="mt-4 poc-divider"></div>
@if (Model.Vm == null)
{
<div class="mt-4 poc-muted">
Provide <b>homeId</b> and <b>awayId</b> in the query string.
</div>
}
else
{
var vm = Model.Vm!;
var isFinal = IsFinal(vm.GameStatus);
// Baseline (team-only)
var b = vm.Baseline;
var bProb = b.HomeWinProbability;
// Player-adjusted fair (optional)
var f = vm.Fair;
var hasFair = f != null;
var fairProb = hasFair ? (double)f!.HomeWinProb : 0.0;
// ✅ FIX: use your new points (no ImpliedPoints)
double usedHomePts = hasFair && vm.FairHomePoints.HasValue
? (double)vm.FairHomePoints.Value
: b.HomePoints;
double usedAwayPts = hasFair && vm.FairAwayPoints.HasValue
? (double)vm.FairAwayPoints.Value
: b.AwayPoints;
double usedTotal = hasFair ? (double)f!.FairTotal : b.FairTotal;
// Player Adjustment Impact (badge): net point swing to home relative to baseline
// (home swing - away swing) / 2 is a clean symmetric measure;
// but for a badge, people understand "net to home" => homePtsDelta - awayPtsDelta.
double impactToHomePts = (usedHomePts - b.HomePoints) - (usedAwayPts - b.AwayPoints);
// Confidence
double conf01 = hasFair ? Clamp01((double)f!.Confidence) : 0.0;
string confColor = ConfidenceColor(conf01);
int confPct = (int)Math.Round(conf01 * 100.0);
// Team factors bars
double homeFactor = hasFair ? (double)f!.HomeTeamFactor : 1.0;
double awayFactor = hasFair ? (double)f!.AwayTeamFactor : 1.0;
int homeFactorPct = FactorToPct(homeFactor);
int awayFactorPct = FactorToPct(awayFactor);
<div class="row mt-4 g-4">
<!-- Left: Matchup + totals/points -->
<div class="col-12 col-lg-7">
<div class="poc-card p-4 h-100">
<div class="d-flex justify-content-between align-items-start gap-3 flex-wrap">
<div class="flex-grow-1">
<div class="poc-muted small">Matchup</div>
<div class="mh-shell">
<div class="mh-row">
<!-- Away -->
<div class="mh-team mh-away">
<div class="mh-logo-wrap">
<img src="@vm.AwayTeamLogoUrl"
alt="@vm.AwayTeamName logo"
class="mh-logo"
loading="lazy"
onerror="this.onerror=null; this.src='/img/ui/team-fallback.png';" />
</div>
<div class="mh-text">
<div class="mh-name">@vm.AwayTeamName</div>
<div class="mh-sub">Away</div>
</div>
</div>
<div class="mh-vs" aria-label="versus">VS</div>
<!-- Home -->
<div class="mh-team mh-home">
<div class="mh-text text-end">
<div class="mh-name">@vm.HomeTeamName</div>
<div class="mh-sub">Home</div>
</div>
<div class="mh-logo-wrap">
<img src="@vm.HomeTeamLogoUrl"
alt="@vm.HomeTeamName logo"
class="mh-logo"
loading="lazy"
onerror="this.onerror=null; this.src='/img/ui/team-fallback.png';" />
</div>
</div>
</div>
<div class="mh-meta">
<div class="mh-meta-left">
<span class="mh-meta-label">Game Date (UTC)</span>
<span class="mh-meta-value">
@(vm.GameDateUtc.HasValue? vm.GameDateUtc.Value.ToString("u") : "—")
</span>
</div>
<div class="mh-meta-right">
@if (!string.IsNullOrWhiteSpace(vm.GameStatus))
{
<span class="mh-chip">@vm.GameStatus</span>
}
@if (isFinal)
{
<span class="mh-chip">Actual: @ScoreOrDash(vm.AwayScore) - @ScoreOrDash(vm.HomeScore)</span>
}
</div>
</div>
</div>
</div>
<!-- KPI strip -->
<div class="poc-card p-4 kpi-card">
<div class="poc-muted small">Projected Total</div>
<div class="kpi-big">
@usedTotal.ToString("0.00")
</div>
<div class="kpi-foot poc-muted small mt-2">
@if (hasFair)
{
<span>Player-adjusted (fair engine).</span>
}
else
{
<span>Baseline (team-only).</span>
}
</div>
@if (isFinal && vm.AwayScore.HasValue && vm.HomeScore.HasValue)
{
var actualTotal = vm.AwayScore.Value + vm.HomeScore.Value;
var totalErr = usedTotal - actualTotal;
<div class="kpi-divider my-3"></div>
<div class="kpi-stats">
<div class="kpi-stat">
<div class="kpi-label">Actual</div>
<div class="kpi-value">@actualTotal</div>
</div>
<div class="kpi-vrule"></div>
<div class="kpi-stat">
<div class="kpi-label">Δ (Proj - Actual)</div>
<div class="kpi-value">@totalErr.ToString("0.00")</div>
</div>
</div>
}
</div>
</div>
<div class="mt-4 poc-divider"></div>
<!-- ✅ NEW: Impact bars + confidence row -->
<div class="row mt-4 g-3">
<div class="col-12">
<div class="poc-card p-4">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-2">
<div>
<div class="poc-muted small">Model Adjustments</div>
<div class="fw-semibold mt-1">Team factors + player impact</div>
</div>
@if (hasFair)
{
<div class="d-flex flex-wrap gap-2">
<span class="poc-pill">
Player Impact: <span class="fw-semibold">@impactToHomePts.ToString("+0.00;-0.00;0.00")</span> pts to Home
</span>
<span class="poc-pill">
Confidence: <span class="fw-semibold">@confPct%</span>
</span>
</div>
}
else
{
<span class="poc-pill">Fair engine not available</span>
}
</div>
@if (hasFair)
{
<div class="mt-3">
<!-- Confidence bar -->
<div class="d-flex justify-content-between align-items-center">
<div class="poc-muted small">Confidence</div>
<div class="poc-muted small">@conf01.ToString("0.00")</div>
</div>
<div class="mt-2" style="height:10px;border-radius:999px;background:rgba(255,255,255,0.08);overflow:hidden;">
<div style="height:10px;width:@confPct%;background:@confColor;"></div>
</div>
<div class="mt-3 row g-3">
<!-- Home factor -->
<div class="col-12 col-md-6">
<div class="poc-card p-3">
<div class="d-flex justify-content-between align-items-center">
<div class="fw-semibold">@vm.HomeTeamName</div>
<span class="poc-pill">Factor: @FactorLabel(homeFactor)</span>
</div>
<div class="mt-2" style="height:10px;border-radius:999px;background:rgba(255,255,255,0.08);overflow:hidden;">
<div style="height:10px;width:@homeFactorPct%;background:@BarColor(true);"></div>
</div>
<div class="mt-2 poc-muted small">
Baseline = 1.00 (higher = stronger)
</div>
</div>
</div>
<!-- Away factor -->
<div class="col-12 col-md-6">
<div class="poc-card p-3">
<div class="d-flex justify-content-between align-items-center">
<div class="fw-semibold">@vm.AwayTeamName</div>
<span class="poc-pill">Factor: @FactorLabel(awayFactor)</span>
</div>
<div class="mt-2" style="height:10px;border-radius:999px;background:rgba(255,255,255,0.08);overflow:hidden;">
<div style="height:10px;width:@awayFactorPct%;background:@BarColor(false);"></div>
</div>
<div class="mt-2 poc-muted small">
/* ... truncated for agent context ... */
# FILE: Pages/Nba/Player.cshtml
@page
@model BettingOdds.Pages.Nba.PlayerModel
@{
ViewData["Title"] = Model.PlayerTitle ?? "Player";
static string Pct01(decimal v) => (v * 100m).ToString("0.00") + "%";
}
<div class="container py-4 poc-shell">
<div class="poc-card p-4 p-md-5">
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-3 mb-3">
<div>
<h2 class="mb-1 poc-title">@Model.PlayerTitle</h2>
<div class="poc-muted">
Rolling performance snapshots and relevance context.
</div>
<div class="mt-3 d-flex flex-wrap gap-2">
<span class="poc-pill">Player ID: @Model.PlayerId</span>
@if (!string.IsNullOrWhiteSpace(Model.TeamLabel))
{
<span class="poc-pill">Team: @Model.TeamLabel</span>
}
<span class="poc-pill">Window: Last @Model.LastNGames games</span>
<span class="poc-pill">Latest As-Of (UTC): @(Model.LatestAsOfUtc?.ToString("u") ?? "—")</span>
</div>
</div>
<div class="d-flex gap-2">
@if (Model.TeamId.HasValue)
{
<a asp-page="/Nba/Team"
asp-route-teamId="@Model.TeamId"
asp-route-lastNGames="@Model.LastNGames"
class="btn btn-outline-light px-4">
Back to Team
</a>
}
<a asp-page="/Nba/Teams" class="btn btn-outline-light px-4">All Teams</a>
</div>
</div>
<div class="poc-divider"></div>
<form method="get" class="mt-3 d-flex flex-wrap gap-2 align-items-center">
<input type="hidden" name="playerId" value="@Model.PlayerId" />
@if (Model.TeamId.HasValue)
{
<input type="hidden" name="teamId" value="@Model.TeamId" />
}
<div class="poc-muted small">Rolling window</div>
<select name="lastNGames" class="form-select form-select-sm" style="max-width:160px;">
@foreach (var n in new[] { 5, 10, 15, 30 })
{
<option value="@n" selected="@(Model.LastNGames == n ? "selected" : null)">Last @n</option>
}
</select>
<button type="submit" class="btn btn-outline-light btn-sm px-3">
Apply
</button>
@if (Model.LatestRelevance != null)
{
<div class="ms-auto d-flex flex-wrap gap-2">
<span class="poc-pill">Tier: @Model.LatestRelevance.TierLabel</span>
<span class="poc-pill">Relevance: @Model.LatestRelevance.RelevanceScore.ToString("0.00")</span>
<span class="poc-pill">Min Share: @Model.LatestRelevance.MinutesSharePct.ToString("0.00")%</span>
<span class="poc-pill">Min Avg: @Model.LatestRelevance.RecentMinutesAvg.ToString("0.00")</span>
</div>
}
</form>
@if (!string.IsNullOrWhiteSpace(Model.Error))
{
<div class="mt-4 poc-card p-3">
<div class="fw-semibold">Notice</div>
<div class="poc-muted">@Model.Error</div>
</div>
}
else
{
<!-- KPI ROW -->
<div class="row g-3 mt-3">
@if (Model.Deltas != null)
{
<div class="col-12 col-lg-8">
<div class="poc-card p-4">
<div class="d-flex justify-content-between align-items-center">
<div class="fw-semibold">Momentum (Δ vs Baseline)</div>
<span class="poc-pill">Baseline: @Model.Deltas.BaselineAsOfUtc.ToString("u")</span>
</div>
<div class="row g-3 mt-2">
<div class="col-6 col-md-3">
<div class="poc-kpi">
<div class="poc-muted small">Δ MIN</div>
<div class="poc-title">@BettingOdds.Pages.Nba.PlayerModel.Signed2(Model.Deltas.DeltaMinutes)</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="poc-kpi">
<div class="poc-muted small">Δ PTS</div>
<div class="poc-title">@BettingOdds.Pages.Nba.PlayerModel.Signed2(Model.Deltas.DeltaPoints)</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="poc-kpi">
<div class="poc-muted small">Δ TS</div>
<div class="poc-title">@BettingOdds.Pages.Nba.PlayerModel.Signed2(Model.Deltas.DeltaTS)</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="poc-kpi">
<div class="poc-muted small">Δ 3P%</div>
<div class="poc-title">@BettingOdds.Pages.Nba.PlayerModel.Signed2(Model.Deltas.DeltaThreePpct)</div>
</div>
</div>
</div>
<div class="mt-2 poc-muted small">
Interpreting TS/3P%: values are in <span class="fw-semibold">0..1</span> form in DB; the table below shows them as %.
</div>
</div>
</div>
}
@if (Model.LatestRelevance != null)
{
<div class="col-12 col-lg-4">
<div class="poc-card p-4">
<div class="d-flex justify-content-between align-items-center">
<div class="fw-semibold">Role / Relevance</div>
<span class="poc-pill">@Model.LatestRelevance.TierLabel</span>
</div>
<div class="row g-3 mt-2">
<div class="col-6">
<div class="poc-kpi">
<div class="poc-muted small">Score</div>
<div class="poc-title">@Model.LatestRelevance.RelevanceScore.ToString("0.00")</div>
</div>
</div>
<div class="col-6">
<div class="poc-kpi">
<div class="poc-muted small">Min Share</div>
<div class="poc-title">@Model.LatestRelevance.MinutesSharePct.ToString("0.00")%</div>
</div>
</div>
<div class="col-12">
<div class="poc-kpi">
<div class="poc-muted small">Recent Min Avg</div>
<div class="poc-title">@Model.LatestRelevance.RecentMinutesAvg.ToString("0.00")</div>
</div>
</div>
</div>
<div class="mt-2 poc-muted small">
Tier is computed from minutes share + availability proxy.
</div>
</div>
</div>
}
</div>
<!-- TREND TABLE -->
<div class="row g-3 mt-3">
<div class="col-12">
<div class="poc-card p-4">
<div class="d-flex justify-content-between align-items-center">
<div class="fw-semibold">Rolling Trend</div>
<span class="poc-pill">Last @Model.Take snapshots</span>
</div>
<div class="table-responsive mt-3">
<table class="table table-dark table-borderless align-middle mb-0">
<thead>
<tr class="poc-muted small">
<th>As-Of (UTC)</th>
<th class="text-end">MIN</th>
<th class="text-end">PTS</th>
<th class="text-end">REB</th>
<th class="text-end">AST</th>
<th class="text-end">TOV</th>
<th class="text-end">TS%</th>
<th class="text-end">3P%</th>
</tr>
</thead>
<tbody>
@foreach (var s in Model.Trend)
{
<tr>
<td class="fw-semibold">@s.AsOfUtc.ToString("u")</td>
<td class="text-end">@s.MinutesAvg.ToString("0.00")</td>
<td class="text-end">@s.PointsAvg.ToString("0.00")</td>
<td class="text-end">@s.ReboundsAvg.ToString("0.00")</td>
<td class="text-end">@s.AssistsAvg.ToString("0.00")</td>
<td class="text-end">@s.TurnoversAvg.ToString("0.00")</td>
<td class="text-end">@Pct01(s.TS)</td>
<td class="text-end">@Pct01(s.ThreePpct)</td>
</tr>
}
</tbody>
</table>
</div>
<div class="mt-3 poc-muted small">
Tip: role changes show up in MIN; efficiency swings show up in TS/3P%.
</div>
</div>
</div>
</div>
}
</div>
</div>
OUTPUT CONTRACT (STRICT):
- Return JSON that matches AgentPatchSet {} exactly.
- Each patch must include:
- Path: relative repo path using forward slashes
- NewContent: FULL final file content (not a diff)
- Reason: short explanation
- Do NOT output unified diff.
- Do NOT wrap output in markdown.
- Conventions:
- All namespaces must start with: namespace BettingOdds.
- Domain entities: namespace BettingOdds.Domain.Entities.<Area>;
- DbContext must be:
namespace BettingOdds.Data;
public sealed class AppDbContext : DbContext
- Migrations namespace must be: namespace BettingOdds.Migrations;
- Identity PKs: public long <EntityName>Id (no int Id).
- Do not create alternate DbContexts.
- Unique indexes must not include nullable PlayerId; use deterministic PlayerKey.
|
||||||
| Preflight | Running | 2026-02-19 18:23:47 | — | — | — | 0 |
Guard
Policy checks for patch safety.
No guard report recorded for this run.
Patch sets
Unified diffs generated by the Implementer.
No patch sets were generated.
Build & test logs
Captured dotnet execution output for this run.
No executions recorded yet.
Files included
Repo file snapshot used during this run (for audit & reproducibility).
No file snapshots recorded.