Skip to content

Commit dc97cb8

Browse files
committed
Merge remote-tracking branch 'origin/main' into codex/epic-12-embedded-runtime
# Conflicts: # AGENTS.md # Directory.Packages.props # DotPilot.Runtime.Host/Features/RuntimeFoundation/EmbeddedRuntimeHostBuilderExtensions.cs # DotPilot.Runtime.Host/Features/RuntimeFoundation/EmbeddedRuntimeHostLifecycleService.cs # DotPilot.Runtime.Host/Features/RuntimeFoundation/EmbeddedRuntimeHostNames.cs # DotPilot.Runtime/Features/RuntimeFoundation/DeterministicAgentRuntimeClient.cs # DotPilot.Runtime/Features/RuntimeFoundation/RuntimeFoundationCatalog.cs # DotPilot.Tests/Features/RuntimeFoundation/EmbeddedRuntimeHostTests.cs # DotPilot.Tests/Features/RuntimeFoundation/RuntimeFoundationCatalogTests.cs # DotPilot/App.xaml.cs # docs/Architecture.md # docs/Features/embedded-orleans-host.md # issue-24-embedded-orleans-host.plan.md
2 parents a6ca453 + eb898fd commit dc97cb8

File tree

48 files changed

+777
-509
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+777
-509
lines changed

AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ For this app:
144144
- the repo-root lowercase `.editorconfig` is the source of truth for formatting, naming, style, and analyzer severity
145145
- local and CI build commands must pass `-warnaserror`; warnings are not an acceptable "green" build state in this repository
146146
- do not run parallel `dotnet` or `MSBuild` work that shares the same checkout, target outputs, or NuGet package cache; the multi-target Uno app must build serially in CI to avoid `Uno.Resizetizer` file-lock failures
147+
- do not commit user-specific local paths, usernames, or machine-specific identifiers in tests, docs, snapshots, or fixtures; use neutral synthetic values so the repo stays portable and does not leak personal machine details
147148
- quality gates should prefer analyzer-backed build failures over separate one-off CI tools; for overloaded methods and maintainability drift, enable build-time analyzers such as `CA1502` instead of adding a formatting-only gate
148149
- `Directory.Build.props` owns the shared analyzer and warning policy for future projects
149150
- `Directory.Packages.props` owns centrally managed package versions
@@ -157,6 +158,7 @@ For this app:
157158
- 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
158159
- 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
159160
- 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
161+
- never claim an epic is complete until its current GitHub scope is verified against the live issue graph; check which issues are real children versus issues that merely depend on the epic or belong to a different parent epic
160162
- 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
161163
- 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
162164
- 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
@@ -311,6 +313,7 @@ Local `AGENTS.md` files may tighten these values, but they must not loosen them
311313
- Hardcoded values are forbidden.
312314
- String literals are forbidden in implementation code. Declare them once as named constants, enums, configuration entries, or dedicated value objects, then reuse those symbols.
313315
- Avoid magic literals. Extract shared values into constants, enums, configuration, or dedicated types.
316+
- Backlog metadata does not belong in product code: issue numbers, PR numbers, review language, and planning terminology must never appear in production runtime models, diagnostics, or user-facing text unless the feature explicitly exposes source-control metadata.
314317
- Design boundaries so real behaviour can be tested through public interfaces.
315318
- For `.NET`, the repo-root `.editorconfig` is the source of truth for formatting, naming, style, and analyzer severity.
316319
- Use nested `.editorconfig` files when they serve a clear subtree-specific purpose. Do not let IDE defaults, pipeline flags, and repo config disagree.
@@ -369,6 +372,8 @@ Ask first:
369372
- Installing stale, non-canonical, or non-`mcaf-*` skills into the repo-local agent skill directory.
370373
- Moving root governance out of the repository root.
371374
- Mixing multiple `.NET` test frameworks in the active solution without a documented migration plan.
375+
- Creating auxiliary `git worktree` directories for normal PR follow-up when straightforward branch switching in the main checkout is enough.
376+
- Running build, test, or verification commands for file-only structural reorganizations when the user explicitly asked for folder cleanup without behavior changes.
372377
- Adding fallback paths or alternate harnesses that only make failures disappear in tests while the primary product path remains broken.
373378
- Switching desktop Uno pages into stacked or mobile-style responsive layouts during resize work unless the user explicitly asks for a different composition; desktop pages must stay desktop-first and protect geometry through sizing constraints instead.
374379
- Adding extra UI-test orchestration complexity when the actual goal is simply to run the tests and get an honest pass or fail result.

DotPilot.Core/Features/ToolchainCenter/ToolchainCenterContracts.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace DotPilot.Core.Features.ToolchainCenter;
44

55
public sealed record ToolchainCenterWorkstreamDescriptor(
66
int IssueNumber,
7-
string IssueLabel,
7+
string SectionLabel,
88
string Name,
99
string Summary);
1010

@@ -37,7 +37,7 @@ public sealed record ToolchainPollingDescriptor(
3737

3838
public sealed record ToolchainProviderSnapshot(
3939
int IssueNumber,
40-
string IssueLabel,
40+
string SectionLabel,
4141
ProviderDescriptor Provider,
4242
string ExecutablePath,
4343
string InstalledVersion,

DotPilot.Runtime.Host/Features/RuntimeFoundation/EmbeddedRuntimeHostBuilderExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ internal static void ConfigureSilo(ISiloBuilder siloBuilder, EmbeddedRuntimeHost
4444
cluster.ClusterId = options.ClusterId;
4545
cluster.ServiceId = options.ServiceId;
4646
});
47+
siloBuilder.AddStartupTask(
48+
static (serviceProvider, _) =>
49+
{
50+
serviceProvider
51+
.GetRequiredService<EmbeddedRuntimeHostCatalog>()
52+
.SetState(EmbeddedRuntimeHostState.Running);
53+
54+
return Task.CompletedTask;
55+
},
56+
ServiceLifecycleStage.Active);
4757
siloBuilder.AddMemoryGrainStorage(EmbeddedRuntimeHostNames.GrainStorageProviderName);
4858
siloBuilder.UseInMemoryReminderService();
4959
}

DotPilot.Runtime.Host/Features/RuntimeFoundation/EmbeddedRuntimeHostLifecycleService.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ namespace DotPilot.Runtime.Host.Features.RuntimeFoundation;
44

55
internal sealed class EmbeddedRuntimeHostLifecycleService(EmbeddedRuntimeHostCatalog catalog) : IHostedService
66
{
7-
public Task StartAsync(CancellationToken cancellationToken)
8-
{
9-
catalog.SetState(DotPilot.Core.Features.RuntimeFoundation.EmbeddedRuntimeHostState.Running);
10-
return Task.CompletedTask;
11-
}
7+
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
128

139
public Task StopAsync(CancellationToken cancellationToken)
1410
{

DotPilot.Runtime/Features/RuntimeFoundation/DeterministicAgentRuntimeClient.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using DotPilot.Core.Features.ControlPlaneDomain;
2+
using DotPilot.Core.Features.RuntimeCommunication;
23
using DotPilot.Core.Features.RuntimeFoundation;
34
using ManagedCode.Communication;
45

@@ -21,19 +22,39 @@ internal DeterministicAgentRuntimeClient(TimeProvider timeProvider)
2122
public ValueTask<Result<AgentTurnResult>> ExecuteAsync(AgentTurnRequest request, CancellationToken cancellationToken)
2223
{
2324
cancellationToken.ThrowIfCancellationRequested();
24-
return ValueTask.FromResult(_engine.Execute(request));
25+
return ValueTask.FromResult(NormalizeArtifacts(_engine.Execute(request)));
2526
}
2627

2728
public ValueTask<Result<AgentTurnResult>> ResumeAsync(AgentTurnResumeRequest request, CancellationToken cancellationToken)
2829
{
2930
cancellationToken.ThrowIfCancellationRequested();
3031
_ = request;
31-
return ValueTask.FromResult(Result<AgentTurnResult>.Fail(DotPilot.Core.Features.RuntimeCommunication.RuntimeCommunicationProblems.OrchestrationUnavailable()));
32+
return ValueTask.FromResult(Result<AgentTurnResult>.Fail(RuntimeCommunicationProblems.OrchestrationUnavailable()));
3233
}
3334

3435
public ValueTask<Result<RuntimeSessionArchive>> GetSessionArchiveAsync(SessionId sessionId, CancellationToken cancellationToken)
3536
{
3637
cancellationToken.ThrowIfCancellationRequested();
37-
return ValueTask.FromResult(Result<RuntimeSessionArchive>.Fail(DotPilot.Core.Features.RuntimeCommunication.RuntimeCommunicationProblems.SessionArchiveMissing(sessionId)));
38+
return ValueTask.FromResult(Result<RuntimeSessionArchive>.Fail(RuntimeCommunicationProblems.SessionArchiveMissing(sessionId)));
39+
}
40+
41+
private static Result<AgentTurnResult> NormalizeArtifacts(Result<AgentTurnResult> result)
42+
{
43+
if (result.IsFailed || result.Value is null)
44+
{
45+
return result;
46+
}
47+
48+
var outcome = result.Value;
49+
var normalizedArtifacts = outcome.ProducedArtifacts
50+
.Select(artifact => artifact with { CreatedAt = RuntimeFoundationDeterministicIdentity.ArtifactCreatedAt })
51+
.ToArray();
52+
53+
return Result<AgentTurnResult>.Succeed(
54+
new AgentTurnResult(
55+
outcome.Summary,
56+
outcome.NextPhase,
57+
outcome.ApprovalState,
58+
normalizedArtifacts));
3859
}
3960
}

DotPilot.Runtime/Features/RuntimeFoundation/ProviderToolchainProbe.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ status is ProviderConnectionStatus.Available
2626

2727
return new ProviderDescriptor
2828
{
29-
Id = ProviderId.New(),
29+
Id = RuntimeFoundationDeterministicIdentity.CreateProviderId(commandName),
3030
DisplayName = displayName,
3131
CommandName = commandName,
3232
Status = status,

DotPilot.Runtime/Features/RuntimeFoundation/RuntimeFoundationCatalog.cs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
using DotPilot.Core.Features.ControlPlaneDomain;
22
using DotPilot.Core.Features.RuntimeFoundation;
3-
using DotPilot.Runtime.Features.ToolchainCenter;
43

54
namespace DotPilot.Runtime.Features.RuntimeFoundation;
65

76
public sealed class RuntimeFoundationCatalog : IRuntimeFoundationCatalog
87
{
98
private const string EpicSummary =
109
"The embedded runtime stays local-first by isolating contracts, host wiring, orchestration, policy, and durable session archives away from the Uno presentation layer.";
10+
private const string EpicLabelValue = "LOCAL RUNTIME READINESS";
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.";
14+
private const string DomainModelLabel = "DOMAIN";
1415
private const string DomainModelName = "Domain contracts";
1516
private const string DomainModelSummary =
1617
"Typed identifiers and durable agent, session, fleet, provider, and runtime contracts live outside the Uno app.";
18+
private const string CommunicationLabel = "CONTRACTS";
1719
private const string CommunicationName = "Communication contracts";
1820
private const string CommunicationSummary =
1921
"Public result and problem boundaries are isolated so later provider and orchestration slices share one contract language.";
22+
private const string HostLabel = "HOST";
2023
private const string HostName = "Embedded host";
2124
private const string HostSummary =
2225
"The Orleans host integration point is sequenced behind dedicated runtime contracts instead of being baked into page code.";
26+
private const string OrchestrationLabel = "ORCHESTRATION";
2327
private const string OrchestrationName = "Orchestration runtime";
2428
private const string OrchestrationSummary =
2529
"Agent Framework orchestrates local runs, approvals, and checkpoints without moving execution logic into the Uno app.";
@@ -29,16 +33,19 @@ public sealed class RuntimeFoundationCatalog : IRuntimeFoundationCatalog
2933
private const string SessionPersistenceName = "Session persistence";
3034
private const string SessionPersistenceSummary =
3135
"Checkpoint, replay, and resume data survive host restarts in local session archives without changing the Orleans storage topology.";
36+
private readonly IReadOnlyList<ProviderDescriptor> _providers;
37+
38+
public RuntimeFoundationCatalog() => _providers = Array.AsReadOnly(CreateProviders());
3239

3340
public RuntimeFoundationSnapshot GetSnapshot()
3441
{
3542
return new(
36-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.EmbeddedAgentRuntimeHostEpic),
43+
EpicLabelValue,
3744
EpicSummary,
3845
ProviderToolchainNames.DeterministicClientDisplayName,
3946
DeterministicProbePrompt,
4047
CreateSlices(),
41-
CreateProviders());
48+
_providers);
4249
}
4350

4451
private static IReadOnlyList<RuntimeSliceDescriptor> CreateSlices()
@@ -47,25 +54,25 @@ private static IReadOnlyList<RuntimeSliceDescriptor> CreateSlices()
4754
[
4855
new(
4956
RuntimeFoundationIssues.DomainModel,
50-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.DomainModel),
57+
DomainModelLabel,
5158
DomainModelName,
5259
DomainModelSummary,
5360
RuntimeSliceState.ReadyForImplementation),
5461
new(
5562
RuntimeFoundationIssues.CommunicationContracts,
56-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.CommunicationContracts),
63+
CommunicationLabel,
5764
CommunicationName,
5865
CommunicationSummary,
5966
RuntimeSliceState.Sequenced),
6067
new(
6168
RuntimeFoundationIssues.EmbeddedOrleansHost,
62-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.EmbeddedOrleansHost),
69+
HostLabel,
6370
HostName,
6471
HostSummary,
6572
RuntimeSliceState.Sequenced),
6673
new(
6774
RuntimeFoundationIssues.AgentFrameworkRuntime,
68-
RuntimeFoundationIssues.FormatIssueLabel(RuntimeFoundationIssues.AgentFrameworkRuntime),
75+
OrchestrationLabel,
6976
OrchestrationName,
7077
OrchestrationSummary,
7178
RuntimeSliceState.Sequenced),
@@ -84,21 +91,19 @@ private static IReadOnlyList<RuntimeSliceDescriptor> CreateSlices()
8491
];
8592
}
8693

87-
private static IReadOnlyList<ProviderDescriptor> CreateProviders()
94+
private static ProviderDescriptor[] CreateProviders()
8895
{
8996
return
9097
[
9198
new ProviderDescriptor
9299
{
93-
Id = ProviderId.New(),
100+
Id = RuntimeFoundationDeterministicIdentity.CreateProviderId(ProviderToolchainNames.DeterministicClientCommandName),
94101
DisplayName = ProviderToolchainNames.DeterministicClientDisplayName,
95102
CommandName = ProviderToolchainNames.DeterministicClientCommandName,
96103
Status = ProviderConnectionStatus.Available,
97104
StatusSummary = DeterministicClientStatusSummary,
98105
RequiresExternalToolchain = false,
99106
},
100-
.. ToolchainProviderSnapshotFactory.Create(TimeProvider.System.GetUtcNow())
101-
.Select(snapshot => snapshot.Provider),
102107
];
103108
}
104109
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Security.Cryptography;
2+
using System.Text;
3+
using DotPilot.Core.Features.ControlPlaneDomain;
4+
5+
namespace DotPilot.Runtime.Features.RuntimeFoundation;
6+
7+
internal static class RuntimeFoundationDeterministicIdentity
8+
{
9+
private const string ArtifactSeedPrefix = "runtime-foundation-artifact";
10+
private const string ProviderSeedPrefix = "runtime-foundation-provider";
11+
private const string SeedSeparator = "|";
12+
13+
public static DateTimeOffset ArtifactCreatedAt { get; } = new(2026, 3, 13, 0, 0, 0, TimeSpan.Zero);
14+
15+
public static ArtifactId CreateArtifactId(SessionId sessionId, string artifactName)
16+
{
17+
return new(CreateGuid(string.Concat(ArtifactSeedPrefix, SeedSeparator, sessionId, SeedSeparator, artifactName)));
18+
}
19+
20+
public static ProviderId CreateProviderId(string commandName)
21+
{
22+
return new(CreateGuid(string.Concat(ProviderSeedPrefix, SeedSeparator, commandName)));
23+
}
24+
25+
private static Guid CreateGuid(string seed)
26+
{
27+
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(seed));
28+
Span<byte> guidBytes = stackalloc byte[16];
29+
hash[..guidBytes.Length].CopyTo(guidBytes);
30+
guidBytes[7] = (byte)((guidBytes[7] & 0x0F) | 0x80);
31+
guidBytes[8] = (byte)((guidBytes[8] & 0x3F) | 0x80);
32+
return new Guid(guidBytes);
33+
}
34+
}

0 commit comments

Comments
 (0)