Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This file defines how AI agents work in this solution.
- `DotPilot`
- `DotPilot.Core`
- `DotPilot.Runtime`
- `DotPilot.Runtime.Host`
- `DotPilot.ReleaseTool`
- `DotPilot.Tests`
- `DotPilot.UITests`
Expand Down Expand Up @@ -150,6 +151,14 @@ For this app:
- `DotPilot/DotPilot.csproj` keeps `GenerateDocumentationFile=true` with `CS1591` suppressed so `IDE0005` stays enforceable in CI across all target frameworks without inventing command-line-only build flags
- architecture work must keep a vertical-slice shape: each feature owns its contracts, orchestration, and tests behind clear boundaries instead of growing a shared horizontal service layer
- 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
- 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
- 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
- do not claim an epic is implemented unless every direct child issue in the requested scope is both realized in code and covered by automated tests; partial coverage is not an acceptable close-out
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
Expand Down
5 changes: 5 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
<PackageVersion Include="coverlet.collector" Version="8.0.0" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageVersion Include="Microsoft.Agents.AI.Workflows" Version="1.0.0-rc4" />
<PackageVersion Include="Microsoft.Orleans.Core.Abstractions" Version="10.0.1" />
<PackageVersion Include="Microsoft.Orleans.Persistence.Memory" Version="10.0.1" />
<PackageVersion Include="Microsoft.Orleans.Reminders" Version="10.0.1" />
<PackageVersion Include="Microsoft.Orleans.Server" Version="10.0.1" />
<PackageVersion Include="NUnit" Version="4.5.1" />
<PackageVersion Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageVersion Include="GitHubActionsTestLogger" Version="3.0.1" />
Expand Down
4 changes: 4 additions & 0 deletions DotPilot.Core/DotPilot.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Orleans.Core.Abstractions" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,77 +2,96 @@

namespace DotPilot.Core.Features.ControlPlaneDomain;

public readonly record struct WorkspaceId(Guid Value)
[GenerateSerializer]
public readonly record struct WorkspaceId([property: Id(0)] Guid Value)
{
public static WorkspaceId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct AgentProfileId(Guid Value)
[GenerateSerializer]
public readonly record struct AgentProfileId([property: Id(0)] Guid Value)
{
public static AgentProfileId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct SessionId(Guid Value)
[GenerateSerializer]
public readonly record struct SessionId([property: Id(0)] Guid Value)
{
public static SessionId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct FleetId(Guid Value)
[GenerateSerializer]
public readonly record struct FleetId([property: Id(0)] Guid Value)
{
public static FleetId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct ProviderId(Guid Value)
[GenerateSerializer]
public readonly record struct PolicyId([property: Id(0)] Guid Value)
{
public static PolicyId New() => new(ControlPlaneIdentifier.NewValue());

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

[GenerateSerializer]
public readonly record struct ProviderId([property: Id(0)] Guid Value)
{
public static ProviderId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct ModelRuntimeId(Guid Value)
[GenerateSerializer]
public readonly record struct ModelRuntimeId([property: Id(0)] Guid Value)
{
public static ModelRuntimeId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct ToolCapabilityId(Guid Value)
[GenerateSerializer]
public readonly record struct ToolCapabilityId([property: Id(0)] Guid Value)
{
public static ToolCapabilityId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct ApprovalId(Guid Value)
[GenerateSerializer]
public readonly record struct ApprovalId([property: Id(0)] Guid Value)
{
public static ApprovalId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct ArtifactId(Guid Value)
[GenerateSerializer]
public readonly record struct ArtifactId([property: Id(0)] Guid Value)
{
public static ArtifactId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct TelemetryRecordId(Guid Value)
[GenerateSerializer]
public readonly record struct TelemetryRecordId([property: Id(0)] Guid Value)
{
public static TelemetryRecordId New() => new(ControlPlaneIdentifier.NewValue());

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

public readonly record struct EvaluationId(Guid Value)
[GenerateSerializer]
public readonly record struct EvaluationId([property: Id(0)] Guid Value)
{
public static EvaluationId New() => new(ControlPlaneIdentifier.NewValue());

Expand Down
24 changes: 21 additions & 3 deletions DotPilot.Core/Features/ControlPlaneDomain/ParticipantContracts.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,58 @@
namespace DotPilot.Core.Features.ControlPlaneDomain;

[GenerateSerializer]
public sealed record WorkspaceDescriptor
{
[Id(0)]
public WorkspaceId Id { get; init; }

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

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

[Id(3)]
public string BranchName { get; init; } = string.Empty;
}

[GenerateSerializer]
public sealed record AgentProfileDescriptor
{
[Id(0)]
public AgentProfileId Id { get; init; }

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

[Id(2)]
public AgentRoleKind Role { get; init; }

[Id(3)]
public ProviderId? ProviderId { get; init; }

[Id(4)]
public ModelRuntimeId? ModelRuntimeId { get; init; }

public IReadOnlyList<ToolCapabilityId> ToolCapabilityIds { get; init; } = [];
[Id(5)]
public ToolCapabilityId[] ToolCapabilityIds { get; init; } = [];

public IReadOnlyList<string> Tags { get; init; } = [];
[Id(6)]
public string[] Tags { get; init; } = [];
}

[GenerateSerializer]
public sealed record FleetDescriptor
{
[Id(0)]
public FleetId Id { get; init; }

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

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

public IReadOnlyList<AgentProfileId> AgentProfileIds { get; init; } = [];
[Id(3)]
public AgentProfileId[] AgentProfileIds { get; init; } = [];
}
23 changes: 23 additions & 0 deletions DotPilot.Core/Features/ControlPlaneDomain/PolicyContracts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace DotPilot.Core.Features.ControlPlaneDomain;

[GenerateSerializer]
public sealed record PolicyDescriptor
{
[Id(0)]
public PolicyId Id { get; init; }

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

[Id(2)]
public ApprovalState DefaultApprovalState { get; init; } = ApprovalState.NotRequired;

[Id(3)]
public bool AllowsNetworkAccess { get; init; }

[Id(4)]
public bool AllowsFileSystemWrites { get; init; }

[Id(5)]
public ApprovalScope[] ProtectedScopes { get; init; } = [];
}
Original file line number Diff line number Diff line change
@@ -1,50 +1,73 @@
namespace DotPilot.Core.Features.ControlPlaneDomain;

[GenerateSerializer]
public sealed record ToolCapabilityDescriptor
{
[Id(0)]
public ToolCapabilityId Id { get; init; }

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

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

[Id(3)]
public ToolCapabilityKind Kind { get; init; }

[Id(4)]
public bool RequiresApproval { get; init; }

[Id(5)]
public bool IsEnabledByDefault { get; init; }

public IReadOnlyList<string> Tags { get; init; } = [];
[Id(6)]
public string[] Tags { get; init; } = [];
}

[GenerateSerializer]
public sealed record ProviderDescriptor
{
[Id(0)]
public ProviderId Id { get; init; }

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

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

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

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

[Id(5)]
public bool RequiresExternalToolchain { get; init; }

public IReadOnlyList<ToolCapabilityId> SupportedToolIds { get; init; } = [];
[Id(6)]
public ToolCapabilityId[] SupportedToolIds { get; init; } = [];
}

[GenerateSerializer]
public sealed record ModelRuntimeDescriptor
{
[Id(0)]
public ModelRuntimeId Id { get; init; }

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

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

[Id(3)]
public RuntimeKind RuntimeKind { get; init; }

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

public IReadOnlyList<string> SupportedModelFamilies { get; init; } = [];
[Id(5)]
public string[] SupportedModelFamilies { get; init; } = [];
}
Loading