Skip to content

Commit 65d41fa

Browse files
committed
Finalize foundation contracts epic
1 parent b23194b commit 65d41fa

File tree

9 files changed

+302
-20
lines changed

9 files changed

+302
-20
lines changed

DotPilot.Runtime/Features/RuntimeFoundation/RuntimeFoundationCatalog.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace DotPilot.Runtime.Features.RuntimeFoundation;
77
public sealed class RuntimeFoundationCatalog : IRuntimeFoundationCatalog
88
{
99
private const string EpicSummary =
10-
"Issue #12 is staged into isolated contracts, communication, host, and orchestration slices so the Uno workbench can stay presentation-only.";
10+
"Issue #12 builds on the #11 foundation contracts and stages host and orchestration work behind isolated runtime slices so the Uno app stays presentation-only.";
1111
private const string DeterministicProbePrompt =
1212
"Summarize the runtime foundation readiness for a local-first session that may require approval.";
1313
private const string DeterministicClientStatusSummary = "Always available for in-repo and CI validation.";

DotPilot.Tests/ControlPlaneDomainContractsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ private static ControlPlaneDomainEnvelope CreateEnvelope()
9797
{
9898
Id = WorkspaceId.New(),
9999
Name = "dotPilot",
100-
RootPath = "/Users/ksemenenko/Developer/dotPilot",
101-
BranchName = "codex/issue-22-domain-model",
100+
RootPath = "/repo/dotPilot",
101+
BranchName = "codex/epic-11-foundation-contracts",
102102
};
103103

104104
var codingAgent = new AgentProfileDescriptor
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
namespace DotPilot.Tests.Features.ControlPlaneDomain;
2+
3+
public sealed class ControlPlaneIdentifierContractTests
4+
{
5+
[Test]
6+
public void NewlyCreatedIdentifiersUseVersionSevenTokens()
7+
{
8+
IReadOnlyList<string> identifiers =
9+
[
10+
WorkspaceId.New().ToString(),
11+
AgentProfileId.New().ToString(),
12+
SessionId.New().ToString(),
13+
FleetId.New().ToString(),
14+
ProviderId.New().ToString(),
15+
ModelRuntimeId.New().ToString(),
16+
ToolCapabilityId.New().ToString(),
17+
ApprovalId.New().ToString(),
18+
ArtifactId.New().ToString(),
19+
TelemetryRecordId.New().ToString(),
20+
EvaluationId.New().ToString(),
21+
];
22+
23+
identifiers.Should().OnlyContain(identifier => identifier.Length == 32);
24+
identifiers.Should().OnlyContain(identifier => identifier[12] == '7');
25+
}
26+
27+
[Test]
28+
public void DefaultDescriptorsExposeSerializationSafeDefaults()
29+
{
30+
var workspace = new WorkspaceDescriptor();
31+
var agent = new AgentProfileDescriptor();
32+
var fleet = new FleetDescriptor();
33+
var tool = new ToolCapabilityDescriptor();
34+
var provider = new ProviderDescriptor();
35+
var runtime = new ModelRuntimeDescriptor();
36+
var session = new SessionDescriptor();
37+
var approval = new SessionApprovalRecord();
38+
var artifact = new ArtifactDescriptor();
39+
var telemetry = new TelemetryRecord();
40+
var evaluation = new EvaluationRecord();
41+
42+
workspace.Name.Should().BeEmpty();
43+
workspace.RootPath.Should().BeEmpty();
44+
workspace.BranchName.Should().BeEmpty();
45+
agent.Name.Should().BeEmpty();
46+
agent.ToolCapabilityIds.Should().BeEmpty();
47+
agent.Tags.Should().BeEmpty();
48+
fleet.Name.Should().BeEmpty();
49+
fleet.AgentProfileIds.Should().BeEmpty();
50+
tool.Name.Should().BeEmpty();
51+
tool.DisplayName.Should().BeEmpty();
52+
tool.Tags.Should().BeEmpty();
53+
provider.DisplayName.Should().BeEmpty();
54+
provider.CommandName.Should().BeEmpty();
55+
provider.StatusSummary.Should().BeEmpty();
56+
provider.Status.Should().Be(ProviderConnectionStatus.Unavailable);
57+
provider.SupportedToolIds.Should().BeEmpty();
58+
runtime.DisplayName.Should().BeEmpty();
59+
runtime.EngineName.Should().BeEmpty();
60+
runtime.Status.Should().Be(ProviderConnectionStatus.Unavailable);
61+
runtime.SupportedModelFamilies.Should().BeEmpty();
62+
session.Title.Should().BeEmpty();
63+
session.Phase.Should().Be(SessionPhase.Plan);
64+
session.ApprovalState.Should().Be(ApprovalState.NotRequired);
65+
session.AgentProfileIds.Should().BeEmpty();
66+
approval.State.Should().Be(ApprovalState.Pending);
67+
approval.RequestedAction.Should().BeEmpty();
68+
approval.RequestedBy.Should().BeEmpty();
69+
artifact.Name.Should().BeEmpty();
70+
artifact.RelativePath.Should().BeEmpty();
71+
telemetry.Name.Should().BeEmpty();
72+
telemetry.Summary.Should().BeEmpty();
73+
evaluation.Outcome.Should().Be(EvaluationOutcome.NeedsReview);
74+
evaluation.Summary.Should().BeEmpty();
75+
}
76+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
namespace DotPilot.Tests.Features.RuntimeCommunication;
2+
3+
public sealed class DeterministicAgentRuntimeClientContractTests
4+
{
5+
private const string ApprovalPrompt = "Execute the local-first flow and request approval before changing files.";
6+
7+
[Test]
8+
public async Task ExecuteAsyncReturnsSucceededResultWithoutProblemForPlanMode()
9+
{
10+
var client = new DeterministicAgentRuntimeClient();
11+
12+
var result = await client.ExecuteAsync(CreateRequest("Plan the contract foundation rollout.", AgentExecutionMode.Plan), CancellationToken.None);
13+
14+
result.IsSuccess.Should().BeTrue();
15+
result.IsFailed.Should().BeFalse();
16+
result.HasProblem.Should().BeFalse();
17+
result.Value.Should().NotBeNull();
18+
result.Value!.NextPhase.Should().Be(SessionPhase.Plan);
19+
}
20+
21+
[Test]
22+
public async Task ExecuteAsyncTreatsApprovalPauseAsASuccessfulStateTransition()
23+
{
24+
var client = new DeterministicAgentRuntimeClient();
25+
26+
var result = await client.ExecuteAsync(CreateRequest(ApprovalPrompt, AgentExecutionMode.Execute), CancellationToken.None);
27+
28+
result.IsSuccess.Should().BeTrue();
29+
result.HasProblem.Should().BeFalse();
30+
result.Value.Should().NotBeNull();
31+
result.Value!.NextPhase.Should().Be(SessionPhase.Paused);
32+
result.Value.ApprovalState.Should().Be(ApprovalState.Pending);
33+
}
34+
35+
[TestCase(ProviderConnectionStatus.Unavailable, RuntimeCommunicationProblemCode.ProviderUnavailable, 503)]
36+
[TestCase(ProviderConnectionStatus.RequiresAuthentication, RuntimeCommunicationProblemCode.ProviderAuthenticationRequired, 401)]
37+
[TestCase(ProviderConnectionStatus.Misconfigured, RuntimeCommunicationProblemCode.ProviderMisconfigured, 424)]
38+
[TestCase(ProviderConnectionStatus.Outdated, RuntimeCommunicationProblemCode.ProviderOutdated, 412)]
39+
public async Task ExecuteAsyncMapsProviderStatesToTypedProblems(
40+
ProviderConnectionStatus providerStatus,
41+
RuntimeCommunicationProblemCode expectedCode,
42+
int expectedStatusCode)
43+
{
44+
var client = new DeterministicAgentRuntimeClient();
45+
46+
var result = await client.ExecuteAsync(
47+
CreateRequest("Run the provider-independent runtime flow.", AgentExecutionMode.Execute, providerStatus),
48+
CancellationToken.None);
49+
50+
result.IsFailed.Should().BeTrue();
51+
result.HasProblem.Should().BeTrue();
52+
result.Value.Should().BeNull();
53+
result.Problem.Should().NotBeNull();
54+
result.Problem!.HasErrorCode(expectedCode).Should().BeTrue();
55+
result.Problem.StatusCode.Should().Be(expectedStatusCode);
56+
}
57+
58+
[Test]
59+
public async Task ExecuteAsyncReturnsOrchestrationProblemForUnsupportedExecutionModes()
60+
{
61+
var client = new DeterministicAgentRuntimeClient();
62+
63+
var result = await client.ExecuteAsync(
64+
CreateRequest("Use an invalid execution mode.", (AgentExecutionMode)999),
65+
CancellationToken.None);
66+
67+
result.IsFailed.Should().BeTrue();
68+
result.HasProblem.Should().BeTrue();
69+
result.Value.Should().BeNull();
70+
result.Problem.Should().NotBeNull();
71+
result.Problem!.HasErrorCode(RuntimeCommunicationProblemCode.OrchestrationUnavailable).Should().BeTrue();
72+
result.Problem.StatusCode.Should().Be(503);
73+
}
74+
75+
private static AgentTurnRequest CreateRequest(
76+
string prompt,
77+
AgentExecutionMode mode,
78+
ProviderConnectionStatus providerStatus = ProviderConnectionStatus.Available)
79+
{
80+
return new AgentTurnRequest(SessionId.New(), AgentProfileId.New(), prompt, mode, providerStatus);
81+
}
82+
}

docs/ADR/ADR-0003-vertical-slices-and-ui-only-uno-app.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ We will use these architectural defaults for implementation work going forward:
3232
- `DotPilot.Core` for contracts, typed identifiers, and public slice interfaces
3333
- `DotPilot.Runtime` for provider-independent runtime implementations and future host integration seams
3434
3. Feature code must be organized as vertical slices under `Features/<FeatureName>/...`, not as shared horizontal `Services`, `Models`, or `Helpers` buckets.
35-
4. Epic `#12` starts with a `RuntimeFoundation` slice that sequences issues `#22`, `#23`, `#24`, and `#25` behind a stable contract surface before live Orleans or provider integration.
35+
4. Epic `#11` establishes the shared `ControlPlaneDomain` and `RuntimeCommunication` slices, and epic `#12` builds on that foundation for runtime-host and orchestration work.
3636
5. CI-safe agent-flow verification must use a deterministic in-repo runtime client as a first-class implementation of the same public contracts, not a mock or hand-wired test double.
3737
6. Tests that require real `Codex`, `Claude Code`, or `GitHub Copilot` toolchains may run only when the corresponding toolchain is available; their absence must not weaken the provider-independent baseline.
3838

@@ -81,7 +81,7 @@ CI does not guarantee those toolchains, so the repo would lose an honest agent-f
8181

8282
- The Uno app gets cleaner and stays focused on operator-facing concerns.
8383
- Future slices can land without merging unrelated feature logic into shared buckets.
84-
- Contracts for `#12` become reusable across UI, runtime, and tests.
84+
- Contracts from epic `#11` become reusable across UI, runtime, and tests before epic `#12` begins live runtime integration.
8585
- CI keeps a real provider-independent verification path through the deterministic runtime client.
8686

8787
### Negative

docs/Architecture.md

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Architecture Overview
22

3-
Goal: give humans and agents a fast map of the active `DotPilot` solution, the current `Uno Platform` shell, the workbench foundation for epic `#13`, the Toolchain Center for epic `#14`, and the vertical-slice runtime foundation that starts epic `#12`.
3+
Goal: give humans and agents a fast map of the active `DotPilot` solution, the current `Uno Platform` shell, the foundation contracts from epic `#11`, the workbench foundation for epic `#13`, the Toolchain Center for epic `#14`, and the runtime-host backlog that builds on those contracts in epic `#12`.
44

55
This file is the required start-here architecture map for non-trivial tasks.
66

@@ -10,15 +10,14 @@ This file is the required start-here architecture map for non-trivial tasks.
1010
- **Presentation boundary:** [../DotPilot/](../DotPilot/) is now the presentation host only. It owns XAML, routing, desktop startup, and UI composition, while non-UI feature logic moves into separate DLLs.
1111
- **Workbench boundary:** epic [#13](https://github.com/managedcode/dotPilot/issues/13) is landing as a `Workbench` slice that will provide repository navigation, file inspection, artifact and log inspection, and a unified settings shell without moving that behavior into page code-behind.
1212
- **Toolchain Center boundary:** epic [#14](https://github.com/managedcode/dotPilot/issues/14) now lives as a `ToolchainCenter` slice. [../DotPilot.Core/Features/ToolchainCenter](../DotPilot.Core/Features/ToolchainCenter) defines the readiness, diagnostics, configuration, action, and polling contracts; [../DotPilot.Runtime/Features/ToolchainCenter](../DotPilot.Runtime/Features/ToolchainCenter) probes local provider CLIs for `Codex`, `Claude Code`, and `GitHub Copilot`; the Uno app surfaces the slice through the settings shell.
13+
- **Foundation contract boundary:** epic [#11](https://github.com/managedcode/dotPilot/issues/11) is represented through [../DotPilot.Core/Features/ControlPlaneDomain](../DotPilot.Core/Features/ControlPlaneDomain) and [../DotPilot.Core/Features/RuntimeCommunication](../DotPilot.Core/Features/RuntimeCommunication). These slices define the shared agent/session/tool model and the `ManagedCode.Communication` result/problem language that later runtime work reuses.
1314
- **Runtime foundation boundary:** [../DotPilot.Core/](../DotPilot.Core/) owns issue-aligned contracts, typed identifiers, and public slice interfaces; [../DotPilot.Runtime/](../DotPilot.Runtime/) owns provider-independent runtime implementations such as the deterministic test client, toolchain probing, and future embedded-host integration points.
14-
- **Domain slice boundary:** issue [#22](https://github.com/managedcode/dotPilot/issues/22) now lives in `DotPilot.Core/Features/ControlPlaneDomain`, which defines the shared agent, session, fleet, provider, runtime, approval, artifact, telemetry, and evaluation model that later slices reuse.
15-
- **Communication slice boundary:** issue [#23](https://github.com/managedcode/dotPilot/issues/23) lives in `DotPilot.Core/Features/RuntimeCommunication`, which defines the shared `ManagedCode.Communication` result/problem language for runtime public boundaries.
16-
- **First implementation slice:** epic [#12](https://github.com/managedcode/dotPilot/issues/12) is represented locally through the `RuntimeFoundation` slice, which sequences issues `#22`, `#23`, `#24`, and `#25` behind a stable contract surface instead of mixing runtime work into the Uno app.
15+
- **Embedded runtime backlog boundary:** epic [#12](https://github.com/managedcode/dotPilot/issues/12) now builds on the epic `#11` foundation contracts through the `RuntimeFoundation` slice instead of treating issues `#22` and `#23` as runtime-host work.
1716
- **Automated verification:** [../DotPilot.Tests/](../DotPilot.Tests/) covers API-style and contract flows through the new DLL boundaries; [../DotPilot.UITests/](../DotPilot.UITests/) covers the visible workbench flow, Toolchain Center, and runtime-foundation UI surface. Provider-independent flows must pass in CI through deterministic or environment-agnostic checks, while provider-specific checks can run only when the matching toolchain is available.
1817

1918
## Scoping
2019

21-
- **In scope for the current repository state:** the Uno workbench shell, the new `DotPilot.Core` and `DotPilot.Runtime` libraries, the runtime-foundation slice, and the automated validation boundaries around them.
20+
- **In scope for the current repository state:** the Uno workbench shell, the `DotPilot.Core` and `DotPilot.Runtime` libraries, the epic `#11` foundation-contract slices, the runtime-foundation planning surface for epic `#12`, and the automated validation boundaries around them.
2221
- **In scope for future implementation:** embedded Orleans hosting, `Microsoft Agent Framework`, provider adapters, persistence, telemetry, evaluation, Git tooling, and local runtimes.
2322
- **Out of scope in the current slice:** full Orleans hosting, live provider execution, remote workers, and cloud-only control-plane services.
2423

@@ -35,7 +34,7 @@ flowchart LR
3534
Adr3["ADR-0003 vertical slices + UI-only app"]
3635
Feature["agent-control-plane-experience.md"]
3736
Toolchains["toolchain-center.md"]
38-
Plan["vertical-slice-runtime-foundation.plan.md"]
37+
Plan["epic-11-foundation-contracts.plan.md"]
3938
Ui["DotPilot Uno UI host"]
4039
Core["DotPilot.Core contracts"]
4140
Runtime["DotPilot.Runtime services"]
@@ -123,31 +122,60 @@ flowchart TD
123122
RuntimeSlice --> UiSlice
124123
```
125124

126-
### Runtime foundation slice for epic #12
125+
### Foundation contract slices for epic #11
127126

128127
```mermaid
129128
flowchart TD
130-
Epic["#12 Embedded agent runtime host"]
129+
Epic["#11 Desktop control-plane foundation"]
131130
Domain["#22 Domain contracts"]
132131
Comm["#23 Communication contracts"]
132+
DomainSlice["DotPilot.Core/Features/ControlPlaneDomain"]
133+
CommunicationSlice["DotPilot.Core/Features/RuntimeCommunication"]
134+
RuntimeContracts["DotPilot.Core/Features/RuntimeFoundation"]
135+
DeterministicClient["DotPilot.Runtime/Features/RuntimeFoundation/DeterministicAgentRuntimeClient"]
136+
Tests["DotPilot.Tests contract coverage"]
137+
138+
Epic --> Domain
139+
Epic --> Comm
140+
Domain --> DomainSlice
141+
Comm --> CommunicationSlice
142+
DomainSlice --> RuntimeContracts
143+
CommunicationSlice --> RuntimeContracts
144+
CommunicationSlice --> DeterministicClient
145+
DomainSlice --> DeterministicClient
146+
DeterministicClient --> Tests
147+
RuntimeContracts --> Tests
148+
```
149+
150+
### Runtime-host backlog slices for epic #12
151+
152+
```mermaid
153+
flowchart TD
154+
Epic["#12 Embedded agent runtime host"]
133155
Host["#24 Embedded Orleans host"]
134156
MAF["#25 Agent Framework runtime"]
157+
Policy["#26 Grain traffic policy"]
158+
Sessions["#27 Session persistence and resume"]
159+
Foundation["#11 Foundation contracts"]
135160
DomainSlice["DotPilot.Core/Features/ControlPlaneDomain"]
136161
CommunicationSlice["DotPilot.Core/Features/RuntimeCommunication"]
137162
CoreSlice["DotPilot.Core/Features/RuntimeFoundation"]
138163
RuntimeSlice["DotPilot.Runtime/Features/RuntimeFoundation"]
139164
UiSlice["DotPilot runtime panel + banner"]
140165
141-
Epic --> Domain
142-
Epic --> Comm
166+
Foundation --> DomainSlice
167+
Foundation --> CommunicationSlice
143168
Epic --> Host
144169
Epic --> MAF
145-
Domain --> DomainSlice
170+
Epic --> Policy
171+
Epic --> Sessions
146172
DomainSlice --> CommunicationSlice
147173
CommunicationSlice --> CoreSlice
148-
Comm --> CommunicationSlice
149174
Host --> RuntimeSlice
150175
MAF --> RuntimeSlice
176+
Policy --> CoreSlice
177+
Sessions --> CoreSlice
178+
Sessions --> RuntimeSlice
151179
CoreSlice --> UiSlice
152180
RuntimeSlice --> UiSlice
153181
```
@@ -185,7 +213,7 @@ flowchart LR
185213
### Planning and decision docs
186214

187215
- `Solution governance`[../AGENTS.md](../AGENTS.md)
188-
- `Task plan`[../vertical-slice-runtime-foundation.plan.md](../vertical-slice-runtime-foundation.plan.md)
216+
- `Task plan`[../epic-11-foundation-contracts.plan.md](../epic-11-foundation-contracts.plan.md)
189217
- `Primary architecture decision`[ADR-0001](./ADR/ADR-0001-agent-control-plane-architecture.md)
190218
- `Vertical-slice solution decision`[ADR-0003](./ADR/ADR-0003-vertical-slices-and-ui-only-uno-app.md)
191219
- `Feature spec`[Agent Control Plane Experience](./Features/agent-control-plane-experience.md)
@@ -237,7 +265,8 @@ flowchart LR
237265

238266
- The Uno app must remain a presentation-only host instead of becoming a dump for runtime logic.
239267
- Feature work should land as vertical slices with isolated contracts and implementations, not as shared horizontal folders.
240-
- Epic `#12` starts with contracts, sequencing, deterministic runtime coverage, and UI exposure before live Orleans or provider integration.
268+
- Epic `#11` establishes the reusable contract and communication foundation before epic `#12` begins embedded runtime-host work.
269+
- Epic `#12` builds on that foundation instead of re-owning issues `#22` and `#23`.
241270
- Epic `#14` makes external-provider toolchain readiness explicit before session creation, so install, auth, diagnostics, and configuration state stays visible instead of being inferred later.
242271
- CI must stay meaningful without external provider CLIs by using the in-repo deterministic runtime client.
243272
- Real provider checks may run only when the corresponding toolchain is present and discoverable.

docs/Features/control-plane-domain-model.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,13 @@ flowchart LR
5858

5959
## Verification
6060

61+
- `dotnet test DotPilot.Tests/DotPilot.Tests.csproj --filter FullyQualifiedName~ControlPlaneDomain`
6162
- `dotnet test DotPilot.Tests/DotPilot.Tests.csproj`
6263
- `dotnet test DotPilot.slnx`
6364

6465
## Dependencies
6566

66-
- Parent epic: [#12](https://github.com/managedcode/dotPilot/issues/12)
67+
- Parent epic: [#11](https://github.com/managedcode/dotPilot/issues/11)
6768
- Runtime communication follow-up: [#23](https://github.com/managedcode/dotPilot/issues/23)
6869
- Embedded host follow-up: [#24](https://github.com/managedcode/dotPilot/issues/24)
6970
- Agent Framework follow-up: [#25](https://github.com/managedcode/dotPilot/issues/25)

docs/Features/runtime-communication-contracts.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ flowchart LR
4848

4949
## Verification
5050

51+
- `dotnet test DotPilot.Tests/DotPilot.Tests.csproj --filter FullyQualifiedName~RuntimeCommunication`
5152
- `dotnet test DotPilot.Tests/DotPilot.Tests.csproj`
5253
- `dotnet test DotPilot.slnx`
5354

0 commit comments

Comments
 (0)