Skip to content

Commit 1838661

Browse files
committed
Implement embedded Orleans localhost host
1 parent f50f3c8 commit 1838661

35 files changed

+1059
-29
lines changed

AGENTS.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This file defines how AI agents work in this solution.
2323
- `DotPilot`
2424
- `DotPilot.Core`
2525
- `DotPilot.Runtime`
26+
- `DotPilot.Runtime.Host`
2627
- `DotPilot.ReleaseTool`
2728
- `DotPilot.Tests`
2829
- `DotPilot.UITests`
@@ -152,7 +153,11 @@ For this app:
152153
- keep the Uno app project presentation-only; domain, runtime host, orchestration, integrations, and persistence code must live in separate class-library projects so UI composition does not mix with feature implementation
153154
- when the user asks to implement an epic, the delivery branch and PR must cover all of that epic's direct child issues that belong to the requested scope, not just one child issue with a partial close-out
154155
- epic implementation PRs must include automated tests for every direct child issue they claim to cover, plus the broader runtime and UI regressions required by the touched flows
155-
- the first embedded Orleans runtime cut must use `UseLocalhostClustering` together with in-memory Orleans grain storage and in-memory reminders; durable resume and replay can persist session data outside Orleans storage until a later issue explicitly upgrades the cluster topology
156+
- structure both `DotPilot.Tests` and `DotPilot.UITests` by vertical slice and explicit harness boundaries; do not keep test files in one flat project-root pile
157+
- the first embedded Orleans runtime cut must use `UseLocalhostClustering` together with in-memory Orleans grain storage and in-memory reminders; do not introduce remote clustering or external durable stores until a later backlog item explicitly requires them, and keep durable resume/replay outside Orleans storage until the cluster topology is intentionally upgraded
158+
- GitHub is the backlog, not the product: use issues and PRs only to drive task scope and traceability, and never copy GitHub issue text, labels, workflow language, or tracker metadata into production code, runtime snapshots, or user-facing UI
159+
- Desktop responsiveness is a product requirement: avoid synchronous probe, filesystem, network, or process work on UI-facing construction and navigation paths so the app stays fast and immediately reactive
160+
- Do not invent a repo-specific product framing such as "workbench" unless the active issue or feature spec explicitly uses it; implement the app features described in the backlog instead of turning internal implementation language into the product narrative
156161
- GitHub Actions workflows must use descriptive names and filenames that reflect their purpose; do not use a generic `ci.yml` catch-all because build validation and release automation are separate operator flows
157162
- GitHub Actions must be split into at least one validation workflow for normal builds/tests and one release workflow for CI-driven version resolution, release-note generation, desktop publishing, and GitHub Release publication
158163
- meaningful GitHub review comments must be evaluated and fixed when they still apply even if the original PR was closed; closed review threads are not a reason to ignore valid engineering feedback

Directory.Packages.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
<PackageVersion Include="coverlet.collector" Version="8.0.0" />
1313
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
1414
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
15+
<PackageVersion Include="Microsoft.Orleans.Core.Abstractions" Version="10.0.1" />
16+
<PackageVersion Include="Microsoft.Orleans.Persistence.Memory" Version="10.0.1" />
17+
<PackageVersion Include="Microsoft.Orleans.Reminders" Version="10.0.1" />
18+
<PackageVersion Include="Microsoft.Orleans.Server" Version="10.0.1" />
1519
<PackageVersion Include="NUnit" Version="4.5.1" />
1620
<PackageVersion Include="NUnit3TestAdapter" Version="6.1.0" />
1721
<PackageVersion Include="GitHubActionsTestLogger" Version="3.0.1" />

DotPilot.Core/DotPilot.Core.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@
66
<NoWarn>$(NoWarn);CS1591</NoWarn>
77
</PropertyGroup>
88

9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Orleans.Core.Abstractions" />
11+
</ItemGroup>
12+
913
</Project>

DotPilot.Core/Features/ControlPlaneDomain/ControlPlaneIdentifiers.cs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,96 @@
22

33
namespace DotPilot.Core.Features.ControlPlaneDomain;
44

5-
public readonly record struct WorkspaceId(Guid Value)
5+
[GenerateSerializer]
6+
public readonly record struct WorkspaceId([property: Id(0)] Guid Value)
67
{
78
public static WorkspaceId New() => new(ControlPlaneIdentifier.NewValue());
89

910
public override string ToString() => ControlPlaneIdentifier.Format(Value);
1011
}
1112

12-
public readonly record struct AgentProfileId(Guid Value)
13+
[GenerateSerializer]
14+
public readonly record struct AgentProfileId([property: Id(0)] Guid Value)
1315
{
1416
public static AgentProfileId New() => new(ControlPlaneIdentifier.NewValue());
1517

1618
public override string ToString() => ControlPlaneIdentifier.Format(Value);
1719
}
1820

19-
public readonly record struct SessionId(Guid Value)
21+
[GenerateSerializer]
22+
public readonly record struct SessionId([property: Id(0)] Guid Value)
2023
{
2124
public static SessionId New() => new(ControlPlaneIdentifier.NewValue());
2225

2326
public override string ToString() => ControlPlaneIdentifier.Format(Value);
2427
}
2528

26-
public readonly record struct FleetId(Guid Value)
29+
[GenerateSerializer]
30+
public readonly record struct FleetId([property: Id(0)] Guid Value)
2731
{
2832
public static FleetId New() => new(ControlPlaneIdentifier.NewValue());
2933

3034
public override string ToString() => ControlPlaneIdentifier.Format(Value);
3135
}
3236

33-
public readonly record struct ProviderId(Guid Value)
37+
[GenerateSerializer]
38+
public readonly record struct PolicyId([property: Id(0)] Guid Value)
39+
{
40+
public static PolicyId New() => new(ControlPlaneIdentifier.NewValue());
41+
42+
public override string ToString() => ControlPlaneIdentifier.Format(Value);
43+
}
44+
45+
[GenerateSerializer]
46+
public readonly record struct ProviderId([property: Id(0)] Guid Value)
3447
{
3548
public static ProviderId New() => new(ControlPlaneIdentifier.NewValue());
3649

3750
public override string ToString() => ControlPlaneIdentifier.Format(Value);
3851
}
3952

40-
public readonly record struct ModelRuntimeId(Guid Value)
53+
[GenerateSerializer]
54+
public readonly record struct ModelRuntimeId([property: Id(0)] Guid Value)
4155
{
4256
public static ModelRuntimeId New() => new(ControlPlaneIdentifier.NewValue());
4357

4458
public override string ToString() => ControlPlaneIdentifier.Format(Value);
4559
}
4660

47-
public readonly record struct ToolCapabilityId(Guid Value)
61+
[GenerateSerializer]
62+
public readonly record struct ToolCapabilityId([property: Id(0)] Guid Value)
4863
{
4964
public static ToolCapabilityId New() => new(ControlPlaneIdentifier.NewValue());
5065

5166
public override string ToString() => ControlPlaneIdentifier.Format(Value);
5267
}
5368

54-
public readonly record struct ApprovalId(Guid Value)
69+
[GenerateSerializer]
70+
public readonly record struct ApprovalId([property: Id(0)] Guid Value)
5571
{
5672
public static ApprovalId New() => new(ControlPlaneIdentifier.NewValue());
5773

5874
public override string ToString() => ControlPlaneIdentifier.Format(Value);
5975
}
6076

61-
public readonly record struct ArtifactId(Guid Value)
77+
[GenerateSerializer]
78+
public readonly record struct ArtifactId([property: Id(0)] Guid Value)
6279
{
6380
public static ArtifactId New() => new(ControlPlaneIdentifier.NewValue());
6481

6582
public override string ToString() => ControlPlaneIdentifier.Format(Value);
6683
}
6784

68-
public readonly record struct TelemetryRecordId(Guid Value)
85+
[GenerateSerializer]
86+
public readonly record struct TelemetryRecordId([property: Id(0)] Guid Value)
6987
{
7088
public static TelemetryRecordId New() => new(ControlPlaneIdentifier.NewValue());
7189

7290
public override string ToString() => ControlPlaneIdentifier.Format(Value);
7391
}
7492

75-
public readonly record struct EvaluationId(Guid Value)
93+
[GenerateSerializer]
94+
public readonly record struct EvaluationId([property: Id(0)] Guid Value)
7695
{
7796
public static EvaluationId New() => new(ControlPlaneIdentifier.NewValue());
7897

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,58 @@
11
namespace DotPilot.Core.Features.ControlPlaneDomain;
22

3+
[GenerateSerializer]
34
public sealed record WorkspaceDescriptor
45
{
6+
[Id(0)]
57
public WorkspaceId Id { get; init; }
68

9+
[Id(1)]
710
public string Name { get; init; } = string.Empty;
811

12+
[Id(2)]
913
public string RootPath { get; init; } = string.Empty;
1014

15+
[Id(3)]
1116
public string BranchName { get; init; } = string.Empty;
1217
}
1318

19+
[GenerateSerializer]
1420
public sealed record AgentProfileDescriptor
1521
{
22+
[Id(0)]
1623
public AgentProfileId Id { get; init; }
1724

25+
[Id(1)]
1826
public string Name { get; init; } = string.Empty;
1927

28+
[Id(2)]
2029
public AgentRoleKind Role { get; init; }
2130

31+
[Id(3)]
2232
public ProviderId? ProviderId { get; init; }
2333

34+
[Id(4)]
2435
public ModelRuntimeId? ModelRuntimeId { get; init; }
2536

26-
public IReadOnlyList<ToolCapabilityId> ToolCapabilityIds { get; init; } = [];
37+
[Id(5)]
38+
public ToolCapabilityId[] ToolCapabilityIds { get; init; } = [];
2739

28-
public IReadOnlyList<string> Tags { get; init; } = [];
40+
[Id(6)]
41+
public string[] Tags { get; init; } = [];
2942
}
3043

44+
[GenerateSerializer]
3145
public sealed record FleetDescriptor
3246
{
47+
[Id(0)]
3348
public FleetId Id { get; init; }
3449

50+
[Id(1)]
3551
public string Name { get; init; } = string.Empty;
3652

53+
[Id(2)]
3754
public FleetExecutionMode ExecutionMode { get; init; } = FleetExecutionMode.SingleAgent;
3855

39-
public IReadOnlyList<AgentProfileId> AgentProfileIds { get; init; } = [];
56+
[Id(3)]
57+
public AgentProfileId[] AgentProfileIds { get; init; } = [];
4058
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace DotPilot.Core.Features.ControlPlaneDomain;
2+
3+
[GenerateSerializer]
4+
public sealed record PolicyDescriptor
5+
{
6+
[Id(0)]
7+
public PolicyId Id { get; init; }
8+
9+
[Id(1)]
10+
public string Name { get; init; } = string.Empty;
11+
12+
[Id(2)]
13+
public ApprovalState DefaultApprovalState { get; init; } = ApprovalState.NotRequired;
14+
15+
[Id(3)]
16+
public bool AllowsNetworkAccess { get; init; }
17+
18+
[Id(4)]
19+
public bool AllowsFileSystemWrites { get; init; }
20+
21+
[Id(5)]
22+
public ApprovalScope[] ProtectedScopes { get; init; } = [];
23+
}
Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,73 @@
11
namespace DotPilot.Core.Features.ControlPlaneDomain;
22

3+
[GenerateSerializer]
34
public sealed record ToolCapabilityDescriptor
45
{
6+
[Id(0)]
57
public ToolCapabilityId Id { get; init; }
68

9+
[Id(1)]
710
public string Name { get; init; } = string.Empty;
811

12+
[Id(2)]
913
public string DisplayName { get; init; } = string.Empty;
1014

15+
[Id(3)]
1116
public ToolCapabilityKind Kind { get; init; }
1217

18+
[Id(4)]
1319
public bool RequiresApproval { get; init; }
1420

21+
[Id(5)]
1522
public bool IsEnabledByDefault { get; init; }
1623

17-
public IReadOnlyList<string> Tags { get; init; } = [];
24+
[Id(6)]
25+
public string[] Tags { get; init; } = [];
1826
}
1927

28+
[GenerateSerializer]
2029
public sealed record ProviderDescriptor
2130
{
31+
[Id(0)]
2232
public ProviderId Id { get; init; }
2333

34+
[Id(1)]
2435
public string DisplayName { get; init; } = string.Empty;
2536

37+
[Id(2)]
2638
public string CommandName { get; init; } = string.Empty;
2739

40+
[Id(3)]
2841
public ProviderConnectionStatus Status { get; init; } = ProviderConnectionStatus.Unavailable;
2942

43+
[Id(4)]
3044
public string StatusSummary { get; init; } = string.Empty;
3145

46+
[Id(5)]
3247
public bool RequiresExternalToolchain { get; init; }
3348

34-
public IReadOnlyList<ToolCapabilityId> SupportedToolIds { get; init; } = [];
49+
[Id(6)]
50+
public ToolCapabilityId[] SupportedToolIds { get; init; } = [];
3551
}
3652

53+
[GenerateSerializer]
3754
public sealed record ModelRuntimeDescriptor
3855
{
56+
[Id(0)]
3957
public ModelRuntimeId Id { get; init; }
4058

59+
[Id(1)]
4160
public string DisplayName { get; init; } = string.Empty;
4261

62+
[Id(2)]
4363
public string EngineName { get; init; } = string.Empty;
4464

65+
[Id(3)]
4566
public RuntimeKind RuntimeKind { get; init; }
4667

68+
[Id(4)]
4769
public ProviderConnectionStatus Status { get; init; } = ProviderConnectionStatus.Unavailable;
4870

49-
public IReadOnlyList<string> SupportedModelFamilies { get; init; } = [];
71+
[Id(5)]
72+
public string[] SupportedModelFamilies { get; init; } = [];
5073
}

0 commit comments

Comments
 (0)